From dd1f5152526d3e93f860290d4b695ee10e902a99 Mon Sep 17 00:00:00 2001 From: Mike Nisbet Date: Fri, 13 Oct 2023 19:16:33 +0100 Subject: [PATCH 001/174] WIP UI # Conflicts: # Assets/Prefabs/Panels/AdminPanel.prefab # Assets/Settings/Localization/Strings/Strings Shared Data.asset # Assets/Settings/Localization/Strings/Strings_en.asset --- Assets/Prefabs/Panels/AdminPanel.prefab | 190 ++- Assets/Prefabs/Panels/MultiplayerPanel.prefab | 1016 +++++++++++++++++ .../Panels/MultiplayerPanel.prefab.meta | 7 + Assets/Resources/Icons/multiplayer.png | Bin 0 -> 3052 bytes Assets/Resources/Icons/multiplayer.png.meta | 171 +++ Assets/Scripts/App.cs | 4 + Assets/Scripts/GUI/AdminPanel.cs | 1 + Assets/Scripts/GUI/BasePanel.cs | 1 + Assets/Scripts/GUI/MultiplayerPanel.cs | 26 + Assets/Scripts/GUI/MultiplayerPanel.cs.meta | 11 + Assets/Scripts/GUI/PanelManager.cs | 55 +- Assets/Scripts/SketchControlsScript.cs | 6 + .../Strings/Strings Shared Data.asset | 4 + 13 files changed, 1483 insertions(+), 9 deletions(-) create mode 100644 Assets/Prefabs/Panels/MultiplayerPanel.prefab create mode 100644 Assets/Prefabs/Panels/MultiplayerPanel.prefab.meta create mode 100644 Assets/Resources/Icons/multiplayer.png create mode 100644 Assets/Resources/Icons/multiplayer.png.meta create mode 100644 Assets/Scripts/GUI/MultiplayerPanel.cs create mode 100644 Assets/Scripts/GUI/MultiplayerPanel.cs.meta diff --git a/Assets/Prefabs/Panels/AdminPanel.prefab b/Assets/Prefabs/Panels/AdminPanel.prefab index 44487082a1..2a568ee5ed 100644 --- a/Assets/Prefabs/Panels/AdminPanel.prefab +++ b/Assets/Prefabs/Panels/AdminPanel.prefab @@ -164,6 +164,10 @@ MonoBehaviour: renderer: {fileID: 0} baseLocalPos: {x: 0, y: 0, z: 0} baseScale: {x: 0, y: 0, z: 0} + - button: {fileID: 541920620010414687} + renderer: {fileID: 0} + baseLocalPos: {x: 0, y: 0, z: 0} + baseScale: {x: 0, y: 0, z: 0} m_SaveNewButton: {fileID: 1922662397484504} m_SaveOptionsButton: {fileID: 1151082384707834} m_SettingsButton: {fileID: 1936268928807740} @@ -177,6 +181,7 @@ MonoBehaviour: m_MemoryWarning: {fileID: 1603096109966042} m_MemoryWarningButton: {fileID: 1712905289553642} m_MemoryWarningColor: {r: 0.78676474, g: 0.09256055, b: 0.09256055, a: 1} + m_MultiplayerButton: {fileID: 5787249127137797294} m_ButtonRotationAngle: 45 m_ShareButtonLoggedOutExtraText: m_TableReference: @@ -924,6 +929,7 @@ Transform: - {fileID: 4670881044846100} - {fileID: 4334957410348514} - {fileID: 4286627721716940} + - {fileID: 541920620010414687} - {fileID: 4478129342109116} - {fileID: 4736542122326996} - {fileID: 4766230287867736} @@ -1632,7 +1638,7 @@ Transform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4315711334760372} - m_RootOrder: 10 + m_RootOrder: 11 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33821366212915112 MeshFilter: @@ -2081,7 +2087,7 @@ Transform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4315711334760372} - m_RootOrder: 7 + m_RootOrder: 8 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33394186971539132 MeshFilter: @@ -2772,7 +2778,7 @@ Transform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4315711334760372} - m_RootOrder: 8 + m_RootOrder: 9 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33145436996304012 MeshFilter: @@ -2948,7 +2954,7 @@ Transform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4315711334760372} - m_RootOrder: 9 + m_RootOrder: 10 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33631275512957272 MeshFilter: @@ -3091,6 +3097,182 @@ MonoBehaviour: references: version: 2 RefIds: [] +--- !u!1 &5787249127137797294 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 541920620010414687} + - component: {fileID: 1706514172525796950} + - component: {fileID: 2050874730965094072} + - component: {fileID: 5241187314589267000} + - component: {fileID: 6550189700625956158} + m_Layer: 16 + m_Name: Button_Multiplayer + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &541920620010414687 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5787249127137797294} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0.349, y: -0.5290003, z: 0.049999952} + m_LocalScale: {x: 0.25, y: 0.25, z: 0.25} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 4315711334760372} + m_RootOrder: 7 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &1706514172525796950 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5787249127137797294} + m_Mesh: {fileID: 4300000, guid: 3eae18cf12435234ab8717789135e90b, type: 3} +--- !u!23 &2050874730965094072 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5787249127137797294} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 40d29de2bdc11f04dbfa25059165916e, 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: 0 + 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!65 &5241187314589267000 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5787249127137797294} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Size: {x: 1, y: 1, z: 0.1} + m_Center: {x: -0.000000074505806, y: 0.000000022351742, z: 0} +--- !u!114 &6550189700625956158 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5787249127137797294} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 6c6859eec74651247968d56b594ac313, type: 3} + m_Name: + m_EditorClassIdentifier: + m_DescriptionType: 0 + m_DescriptionYOffset: 0 + m_DescriptionText: ADMIN_PANEL_MULTIPLAYER_BUTTON_DESCRIPTION + m_LocalizedDescription: + m_TableReference: + m_TableCollectionName: GUID:c84355079ab3f3e4f8f3812258805f86 + m_TableEntryReference: + m_KeyId: 156198200358006784 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_DescriptionTextExtra: + m_LocalizedDescriptionExtra: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_DescriptionActivateSpeed: 12 + m_DescriptionZScale: 1 + m_ButtonTexture: {fileID: 2800000, guid: a33145647a3f52648829afbd03b5fa9b, type: 3} + m_AtlasTexture: 1 + m_ToggleButton: 0 + m_LongPressReleaseButton: 0 + m_ButtonHasPressedAudio: 0 + m_ZAdjustHover: -0.02 + m_ZAdjustClick: 0.05 + m_HoverScale: 1.1 + m_HoverBoxColliderGrow: 0.2 + m_AddOverlay: 0 + m_Command: 1001 + m_CommandParam: -1 + m_CommandParam2: -1 + m_RequiresPopup: 0 + m_CenterPopupOnButton: 0 + m_PopupOffset: {x: 0, y: 0, z: 0} + m_PopupText: + m_LocalizedPopup: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_ToggleOnDescription: + m_LocalizedToggleOnDescription: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_ToggleOnTexture: {fileID: 0} + m_AllowUnavailable: 0 + m_LinkedUIObject: {fileID: 0} + references: + version: 2 + RefIds: [] --- !u!1001 &1191275236260192388 PrefabInstance: m_ObjectHideFlags: 0 diff --git a/Assets/Prefabs/Panels/MultiplayerPanel.prefab b/Assets/Prefabs/Panels/MultiplayerPanel.prefab new file mode 100644 index 0000000000..63e3683eec --- /dev/null +++ b/Assets/Prefabs/Panels/MultiplayerPanel.prefab @@ -0,0 +1,1016 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &160214 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 415082} + - component: {fileID: 114984344201106630} + - component: {fileID: 114120113036906770} + m_Layer: 16 + m_Name: MultiplayerPanel + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &415082 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 160214} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 4, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 4000011007410470} + - {fileID: 498528} + - {fileID: 499980} + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &114984344201106630 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 160214} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 8826790ad4eae8c4b9690bfac756b1ff, type: 3} + m_Name: + m_EditorClassIdentifier: + m_PanelType: 34 + m_Collider: {fileID: 6536704} + m_Mesh: {fileID: 184986} + m_Border: {fileID: 23425245546884932} + m_MeshCollider: {fileID: 6569620} + m_ParticleBounds: {x: 0, y: 0, z: 0} + m_PanelPopUpMap: [] + m_PanelDescription: + m_LocalizedPanelDescription: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_PanelDescriptionPrefab: {fileID: 160918, guid: 3491f4f01ba6cac47b1633f36d7c6c84, + type: 3} + m_PanelDescriptionOffset: {x: 1, y: 1, z: 0} + m_PanelDescriptionColor: {r: 1, g: 1, b: 1, a: 1} + m_PanelFlairPrefab: {fileID: 0} + m_PanelFlairOffset: {x: 0, y: 0, z: 0} + m_DescriptionSpringK: 4 + m_DescriptionSpringDampen: 0.2 + m_DescriptionClosedAngle: -90 + m_DescriptionOpenAngle: 0 + m_DescriptionAlphaDistance: 90 + m_Decor: + - {fileID: 1889327720883680} + m_GazeHighlightScaleMultiplier: 1.1 + m_BorderMeshWidth: 0.02 + m_BorderMeshAdvWidth: 0.01 + m_PanelSensitivity: 0.1 + m_ClampToBounds: 0 + m_ReticleBounds: {x: 2.15, y: 2.55, z: 0} + m_BorderSphereHighlightRadius: 2.5 + m_PositioningSpheresBounds: {x: 1, y: 1} + m_PositioningSphereRadius: 0.4 + m_UseGazeRotation: 1 + m_MaxGazeRotation: 20 + m_GazeActivateSpeed: 8 + m_InitialSpawnPos: {x: 0, y: 13, z: 5} + m_InitialSpawnRotEulers: {x: 0, y: 0, z: 0} + m_WandAttachAngle: 0 + m_WandAttachYOffset: 0 + m_WandAttachHalfHeight: 1.3 + m_BeginFixed: 0 + m_CanBeFixedToWand: 1 + m_CanBeDetachedFromWand: 0 + m_PopUpGazeDuration: 0.2 + m_PromoBorders: [] + references: + version: 2 + RefIds: [] +--- !u!114 &114120113036906770 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 160214} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 739d5b1996234d64992a2ae60c3723e9, type: 3} + m_Name: + m_EditorClassIdentifier: +--- !u!1 &169614 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 420492} + - component: {fileID: 6569620} + m_Layer: 16 + m_Name: MeshCollider + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &420492 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 169614} + 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: 499980} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &6569620 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 169614} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Size: {x: 1.94, y: 2.55, z: 0.02} + m_Center: {x: 0.06, y: -0.06, z: 0} +--- !u!1 &173754 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 498528} + - component: {fileID: 6536704} + m_Layer: 16 + m_Name: Collider + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &498528 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 173754} + 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: 415082} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &6536704 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 173754} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Size: {x: 2.4, y: 3.3, z: 0.5} + m_Center: {x: 0, y: 0, z: 0} +--- !u!1 &176750 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 480900} + - component: {fileID: 3378504} + m_Layer: 16 + m_Name: HighlightMesh + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &480900 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 176750} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0.05} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 499980} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &3378504 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 176750} + m_Mesh: {fileID: 4300000, guid: 92d56c40cdd919e449581a5798c4c533, type: 3} +--- !u!1 &184986 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 499980} + m_Layer: 16 + m_Name: Mesh + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &499980 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 184986} + 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: 4197450359471226742} + - {fileID: 420492} + - {fileID: 480900} + - {fileID: 4696385544466816} + - {fileID: 4410849844686322} + m_Father: {fileID: 415082} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1000013067707022 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4000011007410470} + - component: {fileID: 33000014291051348} + - component: {fileID: 23000013088760582} + m_Layer: 16 + m_Name: _Bounds(inactive) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 0 +--- !u!4 &4000011007410470 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1000013067707022} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 2, y: 2.5, z: 2.55} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 415082} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &33000014291051348 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1000013067707022} + m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &23000013088760582 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1000013067707022} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 0 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + 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!1 &1007290988937762 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4153922631777284} + - component: {fileID: 33058761823212030} + - component: {fileID: 23101283692199882} + m_Layer: 16 + m_Name: Quad + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &4153922631777284 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1007290988937762} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0.65, y: 0.65, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 4410849844686322} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &33058761823212030 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1007290988937762} + m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &23101283692199882 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1007290988937762} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 0d7eb02b18ffb4c419fb75924cb900dc, 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!1 &1204492690482570 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4410849844686322} + - component: {fileID: 33802401890004714} + - component: {fileID: 23132643551969806} + - component: {fileID: 65355371710737186} + - component: {fileID: 114889102552369014} + m_Layer: 16 + m_Name: Button_ClosePopup + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &4410849844686322 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1204492690482570} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0.907, y: -1.206, z: -0.01} + m_LocalScale: {x: 0.29999998, y: 0.29999998, z: 0.29999998} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 4153922631777284} + - {fileID: 4402564000880478} + m_Father: {fileID: 499980} + m_RootOrder: 4 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &33802401890004714 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1204492690482570} + m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &23132643551969806 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1204492690482570} + m_Enabled: 0 + 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: 3c8ca511828182747a0b79564892ec57, 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: 0 + 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!65 &65355371710737186 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1204492690482570} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Size: {x: 1.0000001, y: 1, z: 0.01} + m_Center: {x: 0, y: 0, z: -0.01} +--- !u!114 &114889102552369014 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1204492690482570} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 6c6859eec74651247968d56b594ac313, type: 3} + m_Name: + m_EditorClassIdentifier: + m_DescriptionType: 0 + m_DescriptionYOffset: 0 + m_DescriptionText: SETTINGS_PANEL_CLOSE_BUTTON_DESCRIPTION + m_LocalizedDescription: + m_TableReference: + m_TableCollectionName: GUID:c84355079ab3f3e4f8f3812258805f86 + m_TableEntryReference: + m_KeyId: 7959094121701376 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_DescriptionTextExtra: + m_LocalizedDescriptionExtra: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_DescriptionActivateSpeed: 12 + m_DescriptionZScale: 1 + m_ButtonTexture: {fileID: 0} + m_AtlasTexture: 1 + m_ToggleButton: 0 + m_LongPressReleaseButton: 0 + m_ButtonHasPressedAudio: 1 + m_ZAdjustHover: -0.02 + m_ZAdjustClick: 0.05 + m_HoverScale: 1.1 + m_HoverBoxColliderGrow: 0.2 + m_AddOverlay: 0 + m_Command: 64 + m_CommandParam: -1 + m_CommandParam2: -1 + m_RequiresPopup: 0 + m_CenterPopupOnButton: 0 + m_PopupOffset: {x: 0, y: 0, z: 0} + m_PopupText: + m_LocalizedPopup: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_ToggleOnDescription: + m_LocalizedToggleOnDescription: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_ToggleOnTexture: {fileID: 0} + m_AllowUnavailable: 0 + m_LinkedUIObject: {fileID: 0} + references: + version: 2 + RefIds: [] +--- !u!1 &1744263076835604 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4148024965627128} + - component: {fileID: 33937921472497080} + - component: {fileID: 23425245546884932} + m_Layer: 0 + m_Name: SettingsPanelBorder + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &4148024965627128 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1744263076835604} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 0, z: 0} + m_LocalScale: {x: 100, y: 100, z: 100} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 4696385544466816} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &33937921472497080 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1744263076835604} + m_Mesh: {fileID: 4300002, guid: 3911a5e0ccf20e0489d2e0c46768cc74, type: 3} +--- !u!23 &23425245546884932 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1744263076835604} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 77dd4ff8b1158a84397aba783cd0af05, 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: 0 + 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!1 &1850095996710182 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4887086521611946} + - component: {fileID: 33901426776633756} + - component: {fileID: 23979510237070564} + m_Layer: 0 + m_Name: SettingsPanelBg + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &4887086521611946 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1850095996710182} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 0, z: 0} + m_LocalScale: {x: 100, y: 100, z: 100} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 4696385544466816} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &33901426776633756 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1850095996710182} + m_Mesh: {fileID: 4300000, guid: 3911a5e0ccf20e0489d2e0c46768cc74, type: 3} +--- !u!23 &23979510237070564 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1850095996710182} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: db0305ff9081c3b448ac79e85d26e5d4, 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: 0 + 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!1 &1852253978840482 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4696385544466816} + m_Layer: 0 + m_Name: SettingsPanel + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &4696385544466816 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1852253978840482} + 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: 4887086521611946} + - {fileID: 4148024965627128} + m_Father: {fileID: 499980} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1889327720883680 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4402564000880478} + - component: {fileID: 33054958406634006} + - component: {fileID: 23246000722874742} + m_Layer: 16 + m_Name: ExitButtonBackground + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &4402564000880478 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1889327720883680} + m_LocalRotation: {x: 0.7071068, y: -0, z: -0, w: 0.7071068} + m_LocalPosition: {x: 0, y: 0, z: 0.025} + m_LocalScale: {x: 0.79999995, y: 0.02, z: 0.8} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 4410849844686322} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 90, y: 0, z: 0} +--- !u!33 &33054958406634006 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1889327720883680} + m_Mesh: {fileID: 10206, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &23246000722874742 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1889327720883680} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 77dd4ff8b1158a84397aba783cd0af05, 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!1 &4556844761904631768 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4197450359471226742} + m_Layer: 16 + m_Name: Buttons + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &4197450359471226742 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4556844761904631768} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0.8860002, z: -0.03} + m_LocalScale: {x: 0.84952, y: 0.84952, z: 0.84952} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 2257290092732788673} + m_Father: {fileID: 499980} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1001 &8555218426663659616 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 4197450359471226742} + m_Modifications: + - target: {fileID: 7630606763700834929, guid: 0963942396a615f4fb1b390436e881b8, + type: 3} + propertyPath: m_Name + value: ActionButton + objectReference: {fileID: 0} + - target: {fileID: 7631838532712089505, guid: 0963942396a615f4fb1b390436e881b8, + type: 3} + propertyPath: m_RootOrder + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 7631838532712089505, guid: 0963942396a615f4fb1b390436e881b8, + type: 3} + propertyPath: m_LocalPosition.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 7631838532712089505, guid: 0963942396a615f4fb1b390436e881b8, + type: 3} + propertyPath: m_LocalPosition.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 7631838532712089505, guid: 0963942396a615f4fb1b390436e881b8, + type: 3} + propertyPath: m_LocalPosition.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 7631838532712089505, guid: 0963942396a615f4fb1b390436e881b8, + type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 7631838532712089505, guid: 0963942396a615f4fb1b390436e881b8, + type: 3} + propertyPath: m_LocalRotation.x + value: -0 + objectReference: {fileID: 0} + - target: {fileID: 7631838532712089505, guid: 0963942396a615f4fb1b390436e881b8, + type: 3} + propertyPath: m_LocalRotation.y + value: -0 + objectReference: {fileID: 0} + - target: {fileID: 7631838532712089505, guid: 0963942396a615f4fb1b390436e881b8, + type: 3} + propertyPath: m_LocalRotation.z + value: -0 + objectReference: {fileID: 0} + - target: {fileID: 7631838532712089505, guid: 0963942396a615f4fb1b390436e881b8, + type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 7631838532712089505, guid: 0963942396a615f4fb1b390436e881b8, + type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 7631838532712089505, guid: 0963942396a615f4fb1b390436e881b8, + type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + m_RemovedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: 0963942396a615f4fb1b390436e881b8, type: 3} +--- !u!4 &2257290092732788673 stripped +Transform: + m_CorrespondingSourceObject: {fileID: 7631838532712089505, guid: 0963942396a615f4fb1b390436e881b8, + type: 3} + m_PrefabInstance: {fileID: 8555218426663659616} + m_PrefabAsset: {fileID: 0} diff --git a/Assets/Prefabs/Panels/MultiplayerPanel.prefab.meta b/Assets/Prefabs/Panels/MultiplayerPanel.prefab.meta new file mode 100644 index 0000000000..f5ef283737 --- /dev/null +++ b/Assets/Prefabs/Panels/MultiplayerPanel.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 5a442232cda8d79489436e6d45958790 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Resources/Icons/multiplayer.png b/Assets/Resources/Icons/multiplayer.png new file mode 100644 index 0000000000000000000000000000000000000000..0c3aae8f8f489ebfbdec6b65a0bfe90a7c9ca380 GIT binary patch literal 3052 zcmb7Ghc{f^7Qcij`CfEGlp)a#K@crU)FFBqq6g7iv>9Cx{UbbO5P}FnM#%`GjR+!) z@=f#}V)W>v#=H3g-dgXib6B%GMjtXH0k>hsuyEm2-Y-_v53L?hx1oHt&tP2TNw zi&@JvuPfW0J~}*TUu<<5fqJ$Q3yFb56n`;A4uN>8G)%n2J{U$7o6-Pk4wm1Lf;@{U z^wC`m`z&uTzYL1H>=gsChAMD4EKU#=I z>bACAfWxzAjAdnI&4KHTy?uQQHxo*PXo;yul-VBt&doJ-brn1fSJoBC!ZP+dXND_7 zgMx!iP$+rQMG)gGlaQR3EJsjKkg)`tP5s&w_4U~AV`G}K1!?EVIDj_s>@0NuG+Yz% z+h5$KS=GA9=clqbZ?w{rg^q~8U-J*1_Sic=eflDJXSP2g{2?G;T2>ZYIx2MVjFsfXnmVdU)UnS2Ui$2F@x z_A3d{NFI1*^Kh&q<>IpzA@1}5;YcO!3YDviEE{nM31y@h7$)X$_4~5U5|)2dAw(M* z7A7etmsnE5i$D9BCQiu%XS9nrGS@$Q3_I&dfyk^Iru#*4ger}{x7KE2Vyf!EcV7KI z|2{eSHKIm=dWYtXW>Aof7z|cva#CL}xsOzu?W-(XEK+@}baio1#oXE2S}^$Z9%*k~ zIPjDUjM>|5%`#(bW8>1sSheV}r-l1fvnBI)9fAfVBh(6w#(+da)5H$!62??3@MBUO$Mh-JX;c zhQ*Q2+O@T{cemwa()i;ubjvH>zI}1t=8~Lwa&uRXv@_RTQ5>1PJ=0Xy)C3_A2zh8U zToKdO#woxQH4w`ONAC9vqAFgp?1$gF!7(p{j6gJ(p=l(0P=Ly){ zqYTSUI2-y?i=J5-z#}PE1}@J@sEWEc@O>zsRaK6&cWVpwOIkuvF~v`AP%$mF21Ryq z89DEc`$f7zwySE)T&NON6mm;0>UWzD*B8&n05Gz!KzIAcxC%o_XJ@hXm1zAcrG%{P zi=`z;pG!JAIwvo$RO_o#+@%e6GJ`M4*-D@QuLa_DD>$roIaxBN4%~CP$rnmOeUneP zx=Ik2L_eyV+4hs;^iz1{*J87aRI044)mB%(ChIdxT|M>*J0l2YKk6sT#l^vJI@U?H zV02<4A(o2HBmZjZgRB3^t|JTvbDQ=p@k1;De;u&fRsN?8DR}pQZwXLOHe0%VUnLl>1X&Q>VA|Ec8Hp z-k(RthBzLH0`SbrhK9_)xgY3eWn~TC@FNbrsv&K0DFo`6d!Bs$Ox{kK4-4)1J&XWk zWMl$2z6;YPbXT#&B(8PSsSM_;i#zpufWbH%xAx68*=uBq9`h*2V8m(T9}WsCbF}yj zeSHcO&UyDvc)C3-W71k%A! zlhYkaa{@tL0Y(_@|)$fepa@VdSrK{mG>~FLl72 z$K-q%`)mfVrchc`XS-yj?{-l^Uw3vwyBz9XPp~+_Ub>**pt@+JvniUqV`gP#rBCl; zppBQ1l6tPn19UE=gZVMr5OF)`$Nd9K5Usl3%j#yun_u)1MF!@w1)I29*s(L`3rJS1(VN)7O&(i!AGXWm5TT;N>QPM;qn}sC_;eY3VhWtVCN7h-q3{ zT6y}DDR(>aKBzRN@p}YZo&{~}?7RizP?Y;}N*1|@BNIq>^!E0~Po-P*GKx_rbpOGJ z-7vkw!di})_1eJQPS}}380N=;M2?%L5OXOAjE2YW%f$e|%)&w|J3ISuwZJ!Xm^g3_ zw=E9ehmXsG?2TOkui{aysUHJw-}0hnzB5WCVy_1xEg_LHTKZ~}cQ_Q>4bDS9vZDRI zd6w;o6uK_*GF0j4yJ;l`(+jHR=*UQPZO0icSeCf=ZT4H1@|PG$N#>?7^-V7#2`DpUU6H4uvN4EjxCk z)yxpDA=~YYFI>PP2pp_#Z&(0Isr;eZY4W4xfU{L^(StB#MTz3^)>QWe!aPVA5KHAR z*D*IA%T$YJ`NSMO-J5CBVM3Ge)Lb% zLZluZkN24B1rLE>_Jj!sk;ee9lu((oKKId&1jbeoO%vo&yl}=s1Ht_K{E_+jo3#@+ z()(hQlWFJQy-(KRd!Cd;W784No-1zqe3;mcFSsv*!a2if7X2I8^)@y)jS+~Z!$7)r z`Xr4%xjQ;)d;xMW8x-0H3YtG@#})*rKkA1L{00wGCZAY5Yyf0`up!UZE0zKk92*}S zo0|Ul-5V2Okj%Fx>&w;(thkks(V)WcG1BN{#`(#ms`5mGt{4f$O-)&8lo@|{A}N&I z5GbOpAdj~VAg5=3A#Qk$a+>%5s9yhT-Fphn^gDDItG#{!mhS*u(?A3B(Bb)i0Gy`I At^fc4 literal 0 HcmV?d00001 diff --git a/Assets/Resources/Icons/multiplayer.png.meta b/Assets/Resources/Icons/multiplayer.png.meta new file mode 100644 index 0000000000..3a62b8aaab --- /dev/null +++ b/Assets/Resources/Icons/multiplayer.png.meta @@ -0,0 +1,171 @@ +fileFormatVersion: 2 +guid: a33145647a3f52648829afbd03b5fa9b +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 12 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMasterTextureLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 0 + wrapV: 0 + wrapW: 0 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 0 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + cookieLightType: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Server + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Android + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: iPhone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: WebGL + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Windows Store Apps + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/App.cs b/Assets/Scripts/App.cs index 32d5895cc5..af9d66c778 100644 --- a/Assets/Scripts/App.cs +++ b/Assets/Scripts/App.cs @@ -1183,6 +1183,10 @@ private void StartReset() { PanelManager.m_Instance.ToggleBrushLabPanels(); } + else if (PanelManager.m_Instance.MultiplayerActive()) + { + PanelManager.m_Instance.ToggleMultiplayerPanels(); + } // Hide all panels. SketchControlsScript.m_Instance.RequestPanelsVisibility(false); diff --git a/Assets/Scripts/GUI/AdminPanel.cs b/Assets/Scripts/GUI/AdminPanel.cs index 1c317b55ef..be914a9851 100644 --- a/Assets/Scripts/GUI/AdminPanel.cs +++ b/Assets/Scripts/GUI/AdminPanel.cs @@ -43,6 +43,7 @@ public class AdminPanel : BasePanel [SerializeField] GameObject m_MemoryWarning; [SerializeField] GameObject m_MemoryWarningButton; [SerializeField] Color m_MemoryWarningColor; + [SerializeField] GameObject m_MultiplayerButton; [SerializeField] float m_ButtonRotationAngle = 45f; [SerializeField] LocalizedString m_ShareButtonLoggedOutExtraText; diff --git a/Assets/Scripts/GUI/BasePanel.cs b/Assets/Scripts/GUI/BasePanel.cs index dd2f5c029e..1150c4fbb3 100644 --- a/Assets/Scripts/GUI/BasePanel.cs +++ b/Assets/Scripts/GUI/BasePanel.cs @@ -106,6 +106,7 @@ public enum PanelType ReferenceMobile, CameraPath, BrushLab, + Multiplayer, WebcamPanel = 5200, Scripts = 6000, SnapSettings = 8000, diff --git a/Assets/Scripts/GUI/MultiplayerPanel.cs b/Assets/Scripts/GUI/MultiplayerPanel.cs new file mode 100644 index 0000000000..349776fab5 --- /dev/null +++ b/Assets/Scripts/GUI/MultiplayerPanel.cs @@ -0,0 +1,26 @@ +// 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 UnityEngine; +namespace TiltBrush +{ + + public class MultiplayerPanel : BasePanel + { + public override void InitPanel() + { + base.InitPanel(); + } + } +} // namespace TiltBrush diff --git a/Assets/Scripts/GUI/MultiplayerPanel.cs.meta b/Assets/Scripts/GUI/MultiplayerPanel.cs.meta new file mode 100644 index 0000000000..de5d3586ae --- /dev/null +++ b/Assets/Scripts/GUI/MultiplayerPanel.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8826790ad4eae8c4b9690bfac756b1ff +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/GUI/PanelManager.cs b/Assets/Scripts/GUI/PanelManager.cs index 188d320c56..ed0907ff96 100644 --- a/Assets/Scripts/GUI/PanelManager.cs +++ b/Assets/Scripts/GUI/PanelManager.cs @@ -171,6 +171,9 @@ public enum PanelMode // StandardToMemoryWarning - Transition to Memory Warning can come from any state. MemoryWarningToStandard, + Multiplayer, + StandardToMultiplayer, + MultiplayerToStandard } public enum PaneVisualsState @@ -234,6 +237,7 @@ public bool AvailableInCurrentMode private List m_MemoryWarningPanels; private List m_CameraPanels; private List m_BrushLabPanels; + private List m_MultiplayerPanels; private BasePanel m_AdminPanel; private AdvancedPanelLayouts m_CachedPanelLayouts; @@ -261,6 +265,7 @@ public bool AvailableInCurrentMode private float m_MemoryWarningScale; private float m_CameraScale; private float m_BrushLabScale; + private float m_MultiplayerScale; private PanelsState m_PanelsState; private PanelMode m_PanelsMode; @@ -308,6 +313,7 @@ public bool PanelsAreStable() public bool CameraActive() { return m_PanelsMode == PanelMode.Camera; } public bool MemoryWarningActive() { return m_PanelsMode == PanelMode.MemoryWarning; } public bool BrushLabActive() { return m_PanelsMode == PanelMode.BrushLab; } + public bool MultiplayerActive() { return m_PanelsMode == PanelMode.Multiplayer; } public bool PanelsHaveBeenCustomized() { return m_PanelsCustomized; } public bool AdvancedModeActive() { return m_AdvancedPanels; } public bool SketchbookActiveIncludingTransitions() @@ -368,7 +374,8 @@ public bool IsPanelUnique(BasePanel.PanelType type) return IsAdminPanel(type) || type == BasePanel.PanelType.AppSettings || type == BasePanel.PanelType.AppSettingsMobile || type == BasePanel.PanelType.Sketchbook || type == BasePanel.PanelType.SketchbookMobile || - type == BasePanel.PanelType.Camera || type == BasePanel.PanelType.MemoryWarning; + type == BasePanel.PanelType.Camera || type == BasePanel.PanelType.MemoryWarning || + type == BasePanel.PanelType.Multiplayer; } // Core panels are those that exist in the basic mode experience. Practically, those that @@ -395,6 +402,7 @@ public void Init() m_MemoryWarningPanels = new List(); m_CameraPanels = new List(); m_BrushLabPanels = new List(); + m_MultiplayerPanels = new List(); m_RevealParticleParent = new GameObject("ParticlesParent"); m_RevealParticleParent.transform.parent = transform; @@ -568,6 +576,10 @@ void CreatePanel(PanelMapKey key, bool advancedPanel) { m_BrushLabPanels.Add(p); } + else if (p.Type == BasePanel.PanelType.Multiplayer) + { + m_MultiplayerPanels.Add(p); + } else if (IsAdminPanel(p.Type)) { Debug.Assert(m_AdminPanel == null, "Multiple Admin Panels are being created."); @@ -891,7 +903,8 @@ public void UpdateWandOrientationControls() (m_PanelsMode == PanelMode.Sketchbook || m_PanelsMode == PanelMode.Settings || m_PanelsMode == PanelMode.MemoryWarning || - m_PanelsMode == PanelMode.Camera); + m_PanelsMode == PanelMode.Camera || + m_PanelsMode == PanelMode.Multiplayer); if (inVisibleAltMode && App.VrSdk.AnalogIsStick(InputManager.ControllerName.Wand)) { if (m_AltModeSwipeEatStickInput) @@ -934,6 +947,10 @@ public void UpdateWandOrientationControls() { ToggleCameraPanels(); } + else if (m_PanelsMode == PanelMode.Multiplayer) + { + ToggleMultiplayerPanels(); + } } } } @@ -1189,7 +1206,8 @@ public void AttachHeldPanelToWand(BasePanel panel) if (m_PanelsMode == PanelMode.Sketchbook || m_PanelsMode == PanelMode.Settings || m_PanelsMode == PanelMode.MemoryWarning || - m_PanelsMode == PanelMode.Camera) + m_PanelsMode == PanelMode.Camera || + m_PanelsMode == PanelMode.Multiplayer) { return; } @@ -1727,6 +1745,11 @@ public void LockPanelsToController() { SetAltPanelXfFromWand(m_BrushLabPanels[i], rBaseTransform); } + + for (int i = 0; i < m_MultiplayerPanels.Count; ++i) + { + SetAltPanelXfFromWand(m_MultiplayerPanels[i], rBaseTransform); + } } // Keep admin panel locked. @@ -1840,7 +1863,10 @@ public void SetVisible(bool bVisible) { m_BrushLabPanels[i].ResetPanel(); } - + for (int i = 0; i < m_MultiplayerPanels.Count; ++i) + { + m_MultiplayerPanels[i].ResetPanel(); + } m_PanelsState = PanelsState.Exiting; } } @@ -1906,6 +1932,12 @@ public void ToggleMemoryWarningMode() } } + public void ToggleMultiplayerPanels() + { + ToggleMode(m_MultiplayerPanels, PanelMode.Multiplayer, PanelMode.StandardToMultiplayer, + PanelMode.MultiplayerToStandard); + } + // This function toggles between the 'mode' parameter and PanelMode.Standard. Currently, // transitions from a non-Standard mode to another non-Standard mode are not allowed. // toMode and fromMode define the transition modes to mode. @@ -1918,7 +1950,8 @@ void ToggleMode(List panels, PanelMode mode, PanelMode toMode, PanelM m_PanelsMode == PanelMode.SketchbookToStandard || m_PanelsMode == PanelMode.SettingsToStandard || m_PanelsMode == PanelMode.CameraToStandard || - m_PanelsMode == PanelMode.BrushLabToStandard) + m_PanelsMode == PanelMode.BrushLabToStandard || + m_PanelsMode == PanelMode.MultiplayerToStandard) { // If we're in full standard mode, reset the panels before we shrink 'em down. if (m_PanelsMode == PanelMode.Standard) @@ -1985,6 +2018,7 @@ void ForceModeScale(PanelMode mode) m_StandardScale = 0.0f; m_BrushLabScale = 0.0f; m_MemoryWarningScale = 0.0f; + m_MultiplayerScale = 0.0f; switch (mode) { @@ -2006,6 +2040,9 @@ void ForceModeScale(PanelMode mode) case PanelMode.BrushLab: m_BrushLabScale = 1.0f; break; + case PanelMode.Multiplayer: + m_MultiplayerScale = 1.0f; + break; default: Debug.LogError("PanelManager.ForceModeScale() called with unsupported mode."); break; @@ -2044,6 +2081,7 @@ void RefreshPanelsForAnimations() SetPanelListScaleAndActive(m_MemoryWarningPanels, m_MemoryWarningScale); SetPanelListScaleAndActive(m_CameraPanels, m_CameraScale); SetPanelListScaleAndActive(m_BrushLabPanels, m_BrushLabScale); + SetPanelListScaleAndActive(m_MultiplayerPanels, m_MultiplayerScale); } void SetPanelListScaleAndActive(List panels, float scale) @@ -2067,6 +2105,7 @@ void Update() case PanelMode.Settings: break; case PanelMode.MemoryWarning: break; case PanelMode.BrushLab: break; + case PanelMode.Multiplayer: break; case PanelMode.StandardToSketchbook: AnimateScaleToMode(ref m_StandardScale, ref m_SketchbookScale, PanelMode.Sketchbook); break; @@ -2088,6 +2127,12 @@ void Update() case PanelMode.StandardToBrushLab: AnimateScaleToMode(ref m_StandardScale, ref m_BrushLabScale, PanelMode.BrushLab); break; + case PanelMode.StandardToMultiplayer: + AnimateScaleToMode(ref m_StandardScale, ref m_MultiplayerScale, PanelMode.Multiplayer); + break; + case PanelMode.MultiplayerToStandard: + AnimateScaleToMode(ref m_MultiplayerScale, ref m_StandardScale, PanelMode.Standard); + break; case PanelMode.BrushLabToStandard: AnimateScaleToMode(ref m_BrushLabScale, ref m_StandardScale, PanelMode.Standard); break; diff --git a/Assets/Scripts/SketchControlsScript.cs b/Assets/Scripts/SketchControlsScript.cs index 9fdf21c0fa..1cbf4f9412 100644 --- a/Assets/Scripts/SketchControlsScript.cs +++ b/Assets/Scripts/SketchControlsScript.cs @@ -146,6 +146,7 @@ public enum GlobalCommands // Open Brush Reserved Enums 1000-1999 LanguagePopup = 1000, + ToggleMultiplayerPanel = 1001, RenameSketch = 5200, OpenLayerOptionsPopup = 5201, @@ -4939,6 +4940,11 @@ public void IssueGlobalCommand(GlobalCommands rEnum, int iParam1 = -1, // TODO refactor code above to use this method OpenUrl($"http://localhost:{App.HttpServer.HttpPort}/examplescripts"); break; + case GlobalCommands.ToggleMultiplayerPanel: + m_PanelManager.ToggleMultiplayerPanels(); + PointerManager.m_Instance.EatLineEnabledInput(); + SketchSurfacePanel.m_Instance.EatToolsInput(); + break; case GlobalCommands.RepaintOptions: break; // Intentionally blank. case GlobalCommands.Null: break; // Intentionally blank. default: diff --git a/Assets/Settings/Localization/Strings/Strings Shared Data.asset b/Assets/Settings/Localization/Strings/Strings Shared Data.asset index 89c2aa81ce..62a43eb841 100644 --- a/Assets/Settings/Localization/Strings/Strings Shared Data.asset +++ b/Assets/Settings/Localization/Strings/Strings Shared Data.asset @@ -3279,6 +3279,10 @@ MonoBehaviour: m_Key: CONTROLLER_HINT_THUMBPAD_BRUSHSIZE m_Metadata: m_Items: [] + - m_Id: 156198200358006784 + m_Key: ADMIN_PANEL_MULTIPLAYER_BUTTON_DESCRIPTION + m_Metadata: + m_Items: [] m_Metadata: m_Items: [] m_KeyGenerator: From a0ad4256d760c2f7bc531671bce4c537c334cd8d Mon Sep 17 00:00:00 2001 From: Mike Nisbet Date: Mon, 16 Oct 2023 13:19:38 +0100 Subject: [PATCH 002/174] Fix panel spawn --- Assets/Scripts/GUI/PanelManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Assets/Scripts/GUI/PanelManager.cs b/Assets/Scripts/GUI/PanelManager.cs index ed0907ff96..cf56e1fc39 100644 --- a/Assets/Scripts/GUI/PanelManager.cs +++ b/Assets/Scripts/GUI/PanelManager.cs @@ -305,7 +305,7 @@ public Color PanelBorderMeshOutlineColor public bool PanelsAreStable() { return StandardActive() || SketchbookActive() || SettingsActive() || MemoryWarningActive() || - CameraActive() || BrushLabActive(); + CameraActive() || BrushLabActive() || MultiplayerActive(); } public bool StandardActive() { return m_PanelsMode == PanelMode.Standard; } public bool SketchbookActive() { return m_PanelsMode == PanelMode.Sketchbook; } From 774196ca38bc46cd84e179c9fba5e183e639c427 Mon Sep 17 00:00:00 2001 From: Mike Nisbet Date: Mon, 16 Oct 2023 13:22:06 +0100 Subject: [PATCH 003/174] Add MP panel to list will probably conflict # Conflicts: # Assets/Scenes/Main.unity --- Assets/Scenes/Main.unity | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/Assets/Scenes/Main.unity b/Assets/Scenes/Main.unity index 43763876e5..885388756e 100644 --- a/Assets/Scenes/Main.unity +++ b/Assets/Scenes/Main.unity @@ -708,7 +708,7 @@ MonoBehaviour: lensDirtScatterFactor: 0.5 lensDirtIntensity: 0.05 lensDirtTexture: {fileID: 0} - inputIsHDR: 1 + inputIsHDR: 0 lowQuality: 1 depthBlending: 0 depthBlendFunction: 0 @@ -30062,6 +30062,15 @@ MonoBehaviour: m_ModeGvr: 0 m_Basic: 0 m_Advanced: 0 + - m_PanelPrefab: {fileID: 160214, guid: 5a442232cda8d79489436e6d45958790, type: 3} + m_ModeVr: 1 + m_ModeVrExperimental: 1 + m_ModeQuestExperimental: 1 + m_ModeMono: 1 + m_ModeQuest: 1 + m_ModeGvr: 1 + m_Basic: 1 + m_Advanced: 1 - m_PanelPrefab: {fileID: 1272310558547734, guid: 38a4b95a5f6824c41994709bfbd012ad, type: 3} m_ModeVr: 1 @@ -33307,7 +33316,7 @@ MonoBehaviour: lensDirtScatterFactor: 0.5 lensDirtIntensity: 0.05 lensDirtTexture: {fileID: 0} - inputIsHDR: 1 + inputIsHDR: 0 lowQuality: 1 depthBlending: 0 depthBlendFunction: 0 @@ -34189,7 +34198,7 @@ MonoBehaviour: lensDirtScatterFactor: 0.5 lensDirtIntensity: 0.05 lensDirtTexture: {fileID: 0} - inputIsHDR: 1 + inputIsHDR: 0 lowQuality: 1 depthBlending: 0 depthBlendFunction: 0 From ed48bef1fa666ad7a0a931ecb1bb699e16b764fa Mon Sep 17 00:00:00 2001 From: Mike Nisbet Date: Mon, 16 Oct 2023 21:09:13 +0100 Subject: [PATCH 004/174] Oculus connection logic for testing --- Assets/OculusMR/OculusMRController.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Assets/OculusMR/OculusMRController.cs b/Assets/OculusMR/OculusMRController.cs index 746b74b1df..fd5283d3f5 100644 --- a/Assets/OculusMR/OculusMRController.cs +++ b/Assets/OculusMR/OculusMRController.cs @@ -57,11 +57,19 @@ public async void StartMRExperience(bool isHosting) { await m_SpatialAnchorManager.CreateSpatialAnchor(); m_SpatialAnchorManager.SceneLocalizeToAnchor(); - MultiplayerManager.m_Instance.Connect(); + MultiplayerManager.m_Instance.Connect(new RoomCreateData() + { + roomName = "OculusMRRoom", + maxPlayers = 12 + }); } else { - MultiplayerManager.m_Instance.Connect(); + MultiplayerManager.m_Instance.Connect(new RoomCreateData() + { + roomName = "OculusMRRoom", + maxPlayers = 12 + }); } } From 52c54ffa6e78b073817ccacbf3723f014bf92075 Mon Sep 17 00:00:00 2001 From: Mike Nisbet Date: Mon, 16 Oct 2023 21:21:53 +0100 Subject: [PATCH 005/174] Join logic # Conflicts: # Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs # Assets/Scripts/Multiplayer/MultiplayerManager.cs --- .../Multiplayer/MultiplayerDataStructs.cs | 20 ++++++ .../Multiplayer/MultiplayerInterfaces.cs | 3 +- .../Scripts/Multiplayer/MultiplayerManager.cs | 21 +++++- .../Multiplayer/Photon/PhotonManager.cs | 68 ++++++++++++++----- 4 files changed, 92 insertions(+), 20 deletions(-) diff --git a/Assets/Scripts/Multiplayer/MultiplayerDataStructs.cs b/Assets/Scripts/Multiplayer/MultiplayerDataStructs.cs index 2401d272f0..ab59b3bda5 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerDataStructs.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerDataStructs.cs @@ -46,4 +46,24 @@ public struct ExtraData { public ulong OculusPlayerId; } + + [System.Serializable] + public struct RoomCreateData + { + public string roomName; + public string roomPassword; + public bool @private; + public int maxPlayers; + public bool voiceDisabled; + } + + [System.Serializable] + public struct RoomData + { + public string roomName; + public bool @private; + public int numPlayers; + public int maxPlayers; + public bool voiceDisabled; + } } diff --git a/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs b/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs index a58eeb5210..246e7b4747 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs @@ -22,7 +22,8 @@ namespace OpenBrush.Multiplayer { public interface IConnectionHandler { - Task Connect(); + Task Init(); + Task Connect(RoomCreateData data); bool IsConnected(); Task Disconnect(bool force = false); diff --git a/Assets/Scripts/Multiplayer/MultiplayerManager.cs b/Assets/Scripts/Multiplayer/MultiplayerManager.cs index 91d588b393..11470271f8 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerManager.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerManager.cs @@ -14,6 +14,7 @@ using System; using System.Collections.Generic; +using System.Threading.Tasks; using UnityEngine; using Unity.XR.CoreUtils; #if OCULUS_SUPPORTED @@ -21,6 +22,10 @@ #endif using TiltBrush; +#if OCULUS_SUPPORTED +using OVRPlatform = Oculus.Platform; +#endif // OCULUS_SUPPORTED + namespace OpenBrush.Multiplayer { public enum MultiplayerType @@ -42,6 +47,7 @@ public class MultiplayerManager : MonoBehaviour public Action> localPlayerJoined; public Action> remotePlayerJoined; + public Action> roomDataRefreshed; ulong myOculusUserId; @@ -58,7 +64,6 @@ void Awake() void Start() { - #if OCULUS_SUPPORTED OVRPlatform.Users.GetLoggedInUser().OnComplete((msg) => { if (!msg.IsError) @@ -100,9 +105,19 @@ void OnDestroy() SketchMemoryScript.m_Instance.CommandRedo -= OnCommandRedo; } - public async void Connect() + public async Task Init() + { + var success = false; + if (m_Manager !=null) + { + success = await m_Manager.Init(); + } + return success; + } + + public async void Connect(RoomCreateData data) { - var result = await m_Manager.Connect(); + var result = await m_Manager.Connect(data); } void Update() diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs index 579b06bc88..0b1c5ffaa5 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs @@ -16,13 +16,13 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using UnityEngine; using Fusion; using Fusion.Photon.Realtime; using Fusion.Sockets; using TiltBrush; -using System.Linq; namespace OpenBrush.Multiplayer { @@ -36,37 +36,55 @@ public class PhotonManager : IConnectionHandler, INetworkRunnerCallbacks PhotonPlayerRig m_LocalPlayer; + AppSettings m_PhotonAppSettings; + 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 + m_PhotonAppSettings = new AppSettings { AppIdFusion = App.Config.PhotonFusionSecrets.ClientId, // Need this set for some reason FixedRegion = "", }; + } + + public async Task Init() + { + var result = await m_Runner.JoinSessionLobby(SessionLobby.Shared, customAppSettings: m_PhotonAppSettings); + + if (result.Ok) + { + ControllerConsoleScript.m_Instance.AddNewLine("Connected to Photon lobby"); + } + else + { + ControllerConsoleScript.m_Instance.AddNewLine("Failed to join lobby!"); + } + + return result.Ok; + } + + public async Task Connect(RoomCreateData roomCreateData) + { + if(m_Runner != null) + { + GameObject.Destroy(m_Runner); + } var args = new StartGameArgs() { GameMode = GameMode.Shared, - SessionName = "OpenBrushMultiplayerTest", - CustomPhotonAppSettings = appSettings, + SessionName = roomCreateData.roomName, + CustomPhotonAppSettings = m_PhotonAppSettings, + PlayerCount = roomCreateData.maxPlayers != 0 ? roomCreateData.maxPlayers : null, SceneManager = m_Runner.gameObject.AddComponent(), Scene = UnityEngine.SceneManagement.SceneManager.GetActiveScene().buildIndex, }; @@ -114,7 +132,7 @@ public void Update() public async Task PerformCommand(BaseCommand command) { await Task.Yield(); - return ProcessCommand(command);; + return ProcessCommand(command); } public async Task UndoCommand(BaseCommand command) @@ -248,7 +266,6 @@ public void OnPlayerJoined(NetworkRunner runner, PlayerRef player) 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); } @@ -257,6 +274,26 @@ public void OnPlayerJoined(NetworkRunner runner, PlayerRef player) m_PlayersSpawning.Add(player); } } + + public void OnSessionListUpdated(NetworkRunner runner, List sessionList) + { + + var roomData = new List(); + foreach (var session in sessionList) + { + RoomData data = new RoomData() + { + roomName = session.Name, + @private = session.IsOpen, + numPlayers = session.PlayerCount, + maxPlayers = session.MaxPlayers + }; + + roomData.Add(data); + } + + m_Manager.roomDataRefreshed?.Invoke(roomData); + } #endregion #region Unused Photon Callbacks @@ -268,7 +305,6 @@ 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) { } From a23f473ad3d3fcd34a468e01a4db15f048b4f93f Mon Sep 17 00:00:00 2001 From: Mike Nisbet Date: Mon, 16 Oct 2023 21:23:58 +0100 Subject: [PATCH 006/174] WIP panel --- Assets/Prefabs/Panels/MultiplayerPanel.prefab | 204 ++++++++++-------- Assets/Scripts/GUI/MultiplayerPanel.cs | 35 ++- 2 files changed, 148 insertions(+), 91 deletions(-) diff --git a/Assets/Prefabs/Panels/MultiplayerPanel.prefab b/Assets/Prefabs/Panels/MultiplayerPanel.prefab index 63e3683eec..4f3f9c4770 100644 --- a/Assets/Prefabs/Panels/MultiplayerPanel.prefab +++ b/Assets/Prefabs/Panels/MultiplayerPanel.prefab @@ -30,6 +30,10 @@ Transform: m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: + - {fileID: 2965947989537662772} + - {fileID: 438733611317437076} + - {fileID: 3405719730669880946} + - {fileID: 1954481780995984980} - {fileID: 4000011007410470} - {fileID: 498528} - {fileID: 499980} @@ -100,6 +104,10 @@ MonoBehaviour: m_CanBeDetachedFromWand: 0 m_PopUpGazeDuration: 0.2 m_PromoBorders: [] + m_LobbyElements: {fileID: 3644177191085397888} + m_CreateRoomElements: {fileID: 6171746570159008301} + m_RoomSettingsElements: {fileID: 8921512026995007485} + m_GeneralSettingsElements: {fileID: 8667602655244155954} references: version: 2 RefIds: [] @@ -145,7 +153,7 @@ Transform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 499980} - m_RootOrder: 1 + m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!65 &6569620 BoxCollider: @@ -190,7 +198,7 @@ Transform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 415082} - m_RootOrder: 1 + m_RootOrder: 5 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!65 &6536704 BoxCollider: @@ -235,7 +243,7 @@ Transform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 499980} - m_RootOrder: 2 + m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &3378504 MeshFilter: @@ -273,13 +281,12 @@ Transform: m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: - - {fileID: 4197450359471226742} - {fileID: 420492} - {fileID: 480900} - {fileID: 4696385544466816} - {fileID: 4410849844686322} m_Father: {fileID: 415082} - m_RootOrder: 2 + m_RootOrder: 6 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1000013067707022 GameObject: @@ -312,7 +319,7 @@ Transform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 415082} - m_RootOrder: 0 + m_RootOrder: 4 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000014291051348 MeshFilter: @@ -482,7 +489,7 @@ Transform: - {fileID: 4153922631777284} - {fileID: 4402564000880478} m_Father: {fileID: 499980} - m_RootOrder: 4 + m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33802401890004714 MeshFilter: @@ -822,7 +829,7 @@ Transform: - {fileID: 4887086521611946} - {fileID: 4148024965627128} m_Father: {fileID: 499980} - m_RootOrder: 3 + m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1889327720883680 GameObject: @@ -907,7 +914,7 @@ MeshRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_AdditionalVertexStreams: {fileID: 0} ---- !u!1 &4556844761904631768 +--- !u!1 &3644177191085397888 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -915,102 +922,119 @@ GameObject: m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - - component: {fileID: 4197450359471226742} + - component: {fileID: 2965947989537662772} m_Layer: 16 - m_Name: Buttons + m_Name: LobbyElements m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!4 &4197450359471226742 +--- !u!4 &2965947989537662772 Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 4556844761904631768} + m_GameObject: {fileID: 3644177191085397888} m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: 0, y: 0.8860002, z: -0.03} - m_LocalScale: {x: 0.84952, y: 0.84952, z: 0.84952} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 - m_Children: - - {fileID: 2257290092732788673} - m_Father: {fileID: 499980} + m_Children: [] + m_Father: {fileID: 415082} m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!1001 &8555218426663659616 -PrefabInstance: +--- !u!1 &6171746570159008301 +GameObject: m_ObjectHideFlags: 0 - serializedVersion: 2 - m_Modification: - m_TransformParent: {fileID: 4197450359471226742} - m_Modifications: - - target: {fileID: 7630606763700834929, guid: 0963942396a615f4fb1b390436e881b8, - type: 3} - propertyPath: m_Name - value: ActionButton - objectReference: {fileID: 0} - - target: {fileID: 7631838532712089505, guid: 0963942396a615f4fb1b390436e881b8, - type: 3} - propertyPath: m_RootOrder - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 7631838532712089505, guid: 0963942396a615f4fb1b390436e881b8, - type: 3} - propertyPath: m_LocalPosition.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 7631838532712089505, guid: 0963942396a615f4fb1b390436e881b8, - type: 3} - propertyPath: m_LocalPosition.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 7631838532712089505, guid: 0963942396a615f4fb1b390436e881b8, - type: 3} - propertyPath: m_LocalPosition.z - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 7631838532712089505, guid: 0963942396a615f4fb1b390436e881b8, - type: 3} - propertyPath: m_LocalRotation.w - value: 1 - objectReference: {fileID: 0} - - target: {fileID: 7631838532712089505, guid: 0963942396a615f4fb1b390436e881b8, - type: 3} - propertyPath: m_LocalRotation.x - value: -0 - objectReference: {fileID: 0} - - target: {fileID: 7631838532712089505, guid: 0963942396a615f4fb1b390436e881b8, - type: 3} - propertyPath: m_LocalRotation.y - value: -0 - objectReference: {fileID: 0} - - target: {fileID: 7631838532712089505, guid: 0963942396a615f4fb1b390436e881b8, - type: 3} - propertyPath: m_LocalRotation.z - value: -0 - objectReference: {fileID: 0} - - target: {fileID: 7631838532712089505, guid: 0963942396a615f4fb1b390436e881b8, - type: 3} - propertyPath: m_LocalEulerAnglesHint.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 7631838532712089505, guid: 0963942396a615f4fb1b390436e881b8, - type: 3} - propertyPath: m_LocalEulerAnglesHint.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 7631838532712089505, guid: 0963942396a615f4fb1b390436e881b8, - type: 3} - propertyPath: m_LocalEulerAnglesHint.z - value: 0 - objectReference: {fileID: 0} - m_RemovedComponents: [] - m_SourcePrefab: {fileID: 100100000, guid: 0963942396a615f4fb1b390436e881b8, type: 3} ---- !u!4 &2257290092732788673 stripped + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 438733611317437076} + m_Layer: 16 + m_Name: CreateRoomElements + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &438733611317437076 Transform: - m_CorrespondingSourceObject: {fileID: 7631838532712089505, guid: 0963942396a615f4fb1b390436e881b8, - type: 3} - m_PrefabInstance: {fileID: 8555218426663659616} + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6171746570159008301} + 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: 415082} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &8667602655244155954 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1954481780995984980} + m_Layer: 16 + m_Name: GeneralSettingsElements + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1954481780995984980 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8667602655244155954} + 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: 415082} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &8921512026995007485 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3405719730669880946} + m_Layer: 16 + m_Name: RoomSettingsElements + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &3405719730669880946 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8921512026995007485} + 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: 415082} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} diff --git a/Assets/Scripts/GUI/MultiplayerPanel.cs b/Assets/Scripts/GUI/MultiplayerPanel.cs index 349776fab5..de6bd67365 100644 --- a/Assets/Scripts/GUI/MultiplayerPanel.cs +++ b/Assets/Scripts/GUI/MultiplayerPanel.cs @@ -12,15 +12,48 @@ // See the License for the specific language governing permissions and // limitations under the License. +using System; +using OpenBrush.Multiplayer; using UnityEngine; + namespace TiltBrush { - public class MultiplayerPanel : BasePanel { + public enum Mode + { + Lobby, + Create, + RoomSettings, + GeneralSettings + } + + [SerializeField] private GameObject m_LobbyElements; + [SerializeField] private GameObject m_CreateRoomElements; + [SerializeField] private GameObject m_RoomSettingsElements; + [SerializeField] private GameObject m_GeneralSettingsElements; + + private Mode m_CurrentMode; + public override void InitPanel() { base.InitPanel(); + + InitMultiplayer(); + } + + public async void InitMultiplayer() + { + bool success = await MultiplayerManager.m_Instance.Init(); + } + + private void UpdateMode(Mode newMode) + { + m_CurrentMode = newMode; + m_LobbyElements.SetActive(m_CurrentMode == Mode.Lobby); + m_CreateRoomElements.SetActive(m_CurrentMode == Mode.Create); + m_RoomSettingsElements.SetActive(m_CurrentMode == Mode.RoomSettings); + m_GeneralSettingsElements.SetActive(m_CurrentMode == Mode.GeneralSettings); } } } // namespace TiltBrush From 5d4558658651e8ef5f09d577dd9160460475d533 Mon Sep 17 00:00:00 2001 From: Mike Nisbet Date: Tue, 17 Oct 2023 13:07:53 +0100 Subject: [PATCH 007/174] Setup new button type, fix close button --- Assets/Prefabs/Panels/MultiplayerPanel.prefab | 184 +++++++++++++++++- Assets/Scripts/GUI/MultiplayerPanel.cs | 30 +++ Assets/Scripts/GUI/MultiplayerPanelButton.cs | 37 ++++ .../GUI/MultiplayerPanelButton.cs.meta | 11 ++ Assets/Scripts/SketchControlsScript.cs | 5 +- .../Strings/Strings Shared Data.asset | 4 + .../Localization/Strings/Strings_en.asset | 4 + 7 files changed, 270 insertions(+), 5 deletions(-) create mode 100644 Assets/Scripts/GUI/MultiplayerPanelButton.cs create mode 100644 Assets/Scripts/GUI/MultiplayerPanelButton.cs.meta diff --git a/Assets/Prefabs/Panels/MultiplayerPanel.prefab b/Assets/Prefabs/Panels/MultiplayerPanel.prefab index 4f3f9c4770..07cee7bdab 100644 --- a/Assets/Prefabs/Panels/MultiplayerPanel.prefab +++ b/Assets/Prefabs/Panels/MultiplayerPanel.prefab @@ -573,7 +573,7 @@ MonoBehaviour: m_TableReference: m_TableCollectionName: GUID:c84355079ab3f3e4f8f3812258805f86 m_TableEntryReference: - m_KeyId: 7959094121701376 + m_KeyId: 157582826200739840 m_Key: m_FallbackState: 0 m_WaitForCompletion: 0 @@ -600,7 +600,7 @@ MonoBehaviour: m_HoverScale: 1.1 m_HoverBoxColliderGrow: 0.2 m_AddOverlay: 0 - m_Command: 64 + m_Command: 1001 m_CommandParam: -1 m_CommandParam2: -1 m_RequiresPopup: 0 @@ -941,7 +941,8 @@ Transform: m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 - m_Children: [] + m_Children: + - {fileID: 4742402761985048582} m_Father: {fileID: 415082} m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} @@ -1007,6 +1008,183 @@ Transform: m_Father: {fileID: 415082} m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &8790421672380404409 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4742402761985048582} + - component: {fileID: 2186148485191086047} + - component: {fileID: 3184411263972264924} + - component: {fileID: 1674489288884736589} + - component: {fileID: 5392421505523717988} + m_Layer: 16 + m_Name: PanelButton_Large_Help + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &4742402761985048582 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8790421672380404409} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0.5700002, z: -0.03} + m_LocalScale: {x: 0.35, y: 0.35, z: 0.35} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 2965947989537662772} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &2186148485191086047 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8790421672380404409} + m_Mesh: {fileID: 4300000, guid: 5501f437160666942ae970f3648fbeb8, type: 3} +--- !u!23 &3184411263972264924 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8790421672380404409} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 40d29de2bdc11f04dbfa25059165916e, 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: 0 + 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!65 &1674489288884736589 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8790421672380404409} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Size: {x: 1, y: 1, z: 0.1} + m_Center: {x: -0.000000074505806, y: 0.000000022351742, z: 0} +--- !u!114 &5392421505523717988 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8790421672380404409} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: b78a8d7209bbdc546979b549a875d550, type: 3} + m_Name: + m_EditorClassIdentifier: + m_DescriptionType: 0 + m_DescriptionYOffset: 0 + m_DescriptionText: + m_LocalizedDescription: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_DescriptionTextExtra: + m_LocalizedDescriptionExtra: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_DescriptionActivateSpeed: 12 + m_DescriptionZScale: 1 + m_ButtonTexture: {fileID: 0} + m_AtlasTexture: 1 + m_ToggleButton: 0 + m_LongPressReleaseButton: 0 + m_ButtonHasPressedAudio: 1 + m_ZAdjustHover: -0.02 + m_ZAdjustClick: 0.02 + m_HoverScale: 1.1 + m_HoverBoxColliderGrow: 0.2 + m_AddOverlay: 0 + m_Command: 1002 + m_CommandParam: 1 + m_CommandParam2: -1 + m_RequiresPopup: 0 + m_CenterPopupOnButton: 0 + m_PopupOffset: {x: 0, y: 0, z: 0} + m_PopupText: + m_LocalizedPopup: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_ToggleOnDescription: + m_LocalizedToggleOnDescription: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_ToggleOnTexture: {fileID: 0} + m_AllowUnavailable: 0 + m_LinkedUIObject: {fileID: 0} + m_CommandIgnored: 0 + references: + version: 2 + RefIds: [] --- !u!1 &8921512026995007485 GameObject: m_ObjectHideFlags: 0 diff --git a/Assets/Scripts/GUI/MultiplayerPanel.cs b/Assets/Scripts/GUI/MultiplayerPanel.cs index de6bd67365..d4ddd329db 100644 --- a/Assets/Scripts/GUI/MultiplayerPanel.cs +++ b/Assets/Scripts/GUI/MultiplayerPanel.cs @@ -22,6 +22,7 @@ public class MultiplayerPanel : BasePanel { public enum Mode { + Null, Lobby, Create, RoomSettings, @@ -55,5 +56,34 @@ private void UpdateMode(Mode newMode) m_RoomSettingsElements.SetActive(m_CurrentMode == Mode.RoomSettings); m_GeneralSettingsElements.SetActive(m_CurrentMode == Mode.GeneralSettings); } + + private void RefreshObjects() + { + + } + + // This function serves as a callback from ProfilePopUpButtons that want to + // change the mode of the popup on click. + public void OnMultiplayerPanelButtonPressed(MultiplayerPanelButton button) + { + switch (button.m_Command) + { + // Identifier for signaling we understand the info message. + case SketchControlsScript.GlobalCommands.Null: + UpdateMode(Mode.Lobby); + RefreshObjects(); + break; + case SketchControlsScript.GlobalCommands.MultiplayerPanelOptions: + switch((Mode)button.m_CommandParam) + { + case Mode.Lobby: + UpdateMode(Mode.Lobby); + break; + default: + break; + } + break; + } + } } } // namespace TiltBrush diff --git a/Assets/Scripts/GUI/MultiplayerPanelButton.cs b/Assets/Scripts/GUI/MultiplayerPanelButton.cs new file mode 100644 index 0000000000..afc40b2aea --- /dev/null +++ b/Assets/Scripts/GUI/MultiplayerPanelButton.cs @@ -0,0 +1,37 @@ +// 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 UnityEngine; + +namespace TiltBrush +{ + public class MultiplayerPanelButton : OptionButton + { + [SerializeField] private bool m_CommandIgnored = false; + + override protected void OnButtonPressed() + { + // For some circumstances on mobile, we want to ignore the command, but still + // notify the popup that we were pressed. Which happens below. + if (!m_CommandIgnored) + { + base.OnButtonPressed(); + } + + MultiplayerPanel popup = m_Manager.GetComponent(); + Debug.Assert(popup != null); + popup.OnMultiplayerPanelButtonPressed(this); + } + } +} // namespace TiltBrush diff --git a/Assets/Scripts/GUI/MultiplayerPanelButton.cs.meta b/Assets/Scripts/GUI/MultiplayerPanelButton.cs.meta new file mode 100644 index 0000000000..8cc41752e7 --- /dev/null +++ b/Assets/Scripts/GUI/MultiplayerPanelButton.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b78a8d7209bbdc546979b549a875d550 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/SketchControlsScript.cs b/Assets/Scripts/SketchControlsScript.cs index 1cbf4f9412..9cead5d7d7 100644 --- a/Assets/Scripts/SketchControlsScript.cs +++ b/Assets/Scripts/SketchControlsScript.cs @@ -146,7 +146,8 @@ public enum GlobalCommands // Open Brush Reserved Enums 1000-1999 LanguagePopup = 1000, - ToggleMultiplayerPanel = 1001, + MultiplayerTogglePanel = 1001, + MultiplayerPanelOptions = 1002, // iParam1: Popup options RenameSketch = 5200, OpenLayerOptionsPopup = 5201, @@ -4940,7 +4941,7 @@ public void IssueGlobalCommand(GlobalCommands rEnum, int iParam1 = -1, // TODO refactor code above to use this method OpenUrl($"http://localhost:{App.HttpServer.HttpPort}/examplescripts"); break; - case GlobalCommands.ToggleMultiplayerPanel: + case GlobalCommands.MultiplayerTogglePanel: m_PanelManager.ToggleMultiplayerPanels(); PointerManager.m_Instance.EatLineEnabledInput(); SketchSurfacePanel.m_Instance.EatToolsInput(); diff --git a/Assets/Settings/Localization/Strings/Strings Shared Data.asset b/Assets/Settings/Localization/Strings/Strings Shared Data.asset index 62a43eb841..dad2dc39ea 100644 --- a/Assets/Settings/Localization/Strings/Strings Shared Data.asset +++ b/Assets/Settings/Localization/Strings/Strings Shared Data.asset @@ -3283,6 +3283,10 @@ MonoBehaviour: m_Key: ADMIN_PANEL_MULTIPLAYER_BUTTON_DESCRIPTION m_Metadata: m_Items: [] + - m_Id: 157582826200739840 + m_Key: MULTIPLAYER_PANEL_CLOSE_BUTTON_DESCRIPTION + m_Metadata: + m_Items: [] m_Metadata: m_Items: [] m_KeyGenerator: diff --git a/Assets/Settings/Localization/Strings/Strings_en.asset b/Assets/Settings/Localization/Strings/Strings_en.asset index 3ce0ae4026..95eb304311 100644 --- a/Assets/Settings/Localization/Strings/Strings_en.asset +++ b/Assets/Settings/Localization/Strings/Strings_en.asset @@ -3472,6 +3472,10 @@ MonoBehaviour: Brush Size' m_Metadata: m_Items: [] + - m_Id: 157582826200739840 + m_Localized: Close + m_Metadata: + m_Items: [] references: version: 2 RefIds: [] From 99b54d1c9cfb54c6607d82c830fe663f845ffa00 Mon Sep 17 00:00:00 2001 From: Mike Nisbet Date: Wed, 18 Oct 2023 09:26:40 +0100 Subject: [PATCH 008/174] Change instantiation point of photon runner # Conflicts: # Assets/Scripts/PassthroughManager.cs --- .../Multiplayer/Photon/PhotonPlayerRig.prefab | 4 +-- .../Multiplayer/MultiplayerInterfaces.cs | 1 + .../Scripts/Multiplayer/MultiplayerManager.cs | 34 ++++++++++++++++--- .../Multiplayer/Photon/PhotonManager.cs | 25 +++++++------- .../Multiplayer/Photon/PhotonPlayerRig.cs | 17 ++++++++++ Assets/Scripts/PointerManager.cs | 6 ++++ 6 files changed, 68 insertions(+), 19 deletions(-) diff --git a/Assets/Resources/Multiplayer/Photon/PhotonPlayerRig.prefab b/Assets/Resources/Multiplayer/Photon/PhotonPlayerRig.prefab index 524ebbe795..a042c63202 100644 --- a/Assets/Resources/Multiplayer/Photon/PhotonPlayerRig.prefab +++ b/Assets/Resources/Multiplayer/Photon/PhotonPlayerRig.prefab @@ -53,8 +53,8 @@ MonoBehaviour: m_Left: {fileID: 6268094594865570998} m_Right: {fileID: 4999079164026519325} m_Tool: {fileID: 5771162771103583132} - headTransform: {fileID: 5041384770245166357} _oculusPlayerId: 0 + headTransform: {fileID: 5041384770245166357} --- !u!114 &6752862290110515822 MonoBehaviour: m_ObjectHideFlags: 0 @@ -69,7 +69,7 @@ MonoBehaviour: m_EditorClassIdentifier: ObjectInterest: 1 DefaultInterestGroups: [] - DestroyWhenStateAuthorityLeaves: 0 + DestroyWhenStateAuthorityLeaves: 1 AllowStateAuthorityOverride: 0 AoiPositionSource: {fileID: 0} Flags: 2305 diff --git a/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs b/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs index 246e7b4747..421b00067c 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs @@ -40,6 +40,7 @@ public interface IConnectionHandler public interface ITransientData { + int PlayerId { get; set; } void TransmitData(T data); T RecieveData(); } diff --git a/Assets/Scripts/Multiplayer/MultiplayerManager.cs b/Assets/Scripts/Multiplayer/MultiplayerManager.cs index 11470271f8..95d08815d2 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerManager.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerManager.cs @@ -14,6 +14,7 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using UnityEngine; using Unity.XR.CoreUtils; @@ -45,8 +46,9 @@ public class MultiplayerManager : MonoBehaviour private ITransientData m_LocalPlayer; private List> m_RemotePlayers; - public Action> localPlayerJoined; - public Action> remotePlayerJoined; + public Action> localPlayerJoined; + public Action> remotePlayerJoined; + public Action playerLeft; public Action> roomDataRefreshed; ulong myOculusUserId; @@ -91,6 +93,7 @@ void Start() localPlayerJoined += OnLocalPlayerJoined; remotePlayerJoined += OnRemotePlayerJoined; + playerLeft += OnPlayerLeft; SketchMemoryScript.m_Instance.CommandPerformed += OnCommandPerformed; SketchMemoryScript.m_Instance.CommandUndo += OnCommandUndo; SketchMemoryScript.m_Instance.CommandRedo += OnCommandRedo; @@ -100,6 +103,7 @@ void OnDestroy() { localPlayerJoined -= OnLocalPlayerJoined; remotePlayerJoined -= OnRemotePlayerJoined; + playerLeft -= OnPlayerLeft; SketchMemoryScript.m_Instance.CommandPerformed -= OnCommandPerformed; SketchMemoryScript.m_Instance.CommandUndo -= OnCommandUndo; SketchMemoryScript.m_Instance.CommandRedo -= OnCommandRedo; @@ -108,7 +112,7 @@ void OnDestroy() public async Task Init() { var success = false; - if (m_Manager !=null) + if (m_Manager != null) { success = await m_Manager.Init(); } @@ -162,6 +166,7 @@ void Update() foreach (var player in m_RemotePlayers) { data = player.RecieveData(); +#if OCULUS_SUPPORTED // New user, share the anchor with them if (data.ExtraData.OculusPlayerId != 0 && !oculusPlayerIds.Contains(data.ExtraData.OculusPlayerId)) { @@ -170,6 +175,7 @@ void Update() oculusPlayerIds.Add(data.ExtraData.OculusPlayerId); newUser = true; } +#endif // OCULUS_SUPPORTED } if (newUser) @@ -178,17 +184,35 @@ void Update() } } - void OnLocalPlayerJoined(ITransientData playerData) + void OnLocalPlayerJoined(int id, ITransientData playerData) { m_LocalPlayer = playerData; } - void OnRemotePlayerJoined(ITransientData playerData) + void OnRemotePlayerJoined(int id, ITransientData playerData) { Debug.Log("Adding new player to track."); + playerData.PlayerId = id; m_RemotePlayers.Add(playerData); } + void OnPlayerLeft(int id) + { + if (m_LocalPlayer.PlayerId == id) + { + Debug.Log("Possible to get here!"); + return; + } + var copy = m_RemotePlayers.ToList(); + foreach (var player in copy) + { + if (player.PlayerId == id) + { + m_RemotePlayers.Remove(player); + } + } + } + private async void OnCommandPerformed(BaseCommand command) { if (!IsConnected) diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs index 0b1c5ffaa5..e0b2279950 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs @@ -45,6 +45,7 @@ public PhotonManager(MultiplayerManager manager) var runnerGO = new GameObject("Photon Network Components"); m_Runner = runnerGO.AddComponent(); + m_Runner.gameObject.AddComponent(); m_Runner.ProvideInput = true; m_Runner.AddCallbacks(this); @@ -58,11 +59,13 @@ public PhotonManager(MultiplayerManager manager) public async Task Init() { + await Task.Yield(); + return true; var result = await m_Runner.JoinSessionLobby(SessionLobby.Shared, customAppSettings: m_PhotonAppSettings); if (result.Ok) { - ControllerConsoleScript.m_Instance.AddNewLine("Connected to Photon lobby"); + ControllerConsoleScript.m_Instance.AddNewLine("Connected to lobby"); } else { @@ -73,19 +76,14 @@ public async Task Init() } public async Task Connect(RoomCreateData roomCreateData) - { - if(m_Runner != null) - { - GameObject.Destroy(m_Runner); - } - + { var args = new StartGameArgs() { GameMode = GameMode.Shared, SessionName = roomCreateData.roomName, CustomPhotonAppSettings = m_PhotonAppSettings, PlayerCount = roomCreateData.maxPlayers != 0 ? roomCreateData.maxPlayers : null, - SceneManager = m_Runner.gameObject.AddComponent(), + SceneManager = m_Runner.gameObject.GetComponent(), Scene = UnityEngine.SceneManagement.SceneManager.GetActiveScene().buildIndex, }; @@ -122,7 +120,7 @@ public void Update() var newPlayer = m_Runner.GetPlayerObject(player); if (newPlayer != null) { - m_Manager.remotePlayerJoined?.Invoke(newPlayer.GetComponent()); + m_Manager.remotePlayerJoined?.Invoke(player.PlayerId, newPlayer.GetComponent()); m_PlayersSpawning.Remove(player); } } @@ -267,7 +265,7 @@ public void OnPlayerJoined(NetworkRunner runner, PlayerRef player) m_LocalPlayer = playerObj.GetComponent(); m_Runner.SetPlayerObject(m_Runner.LocalPlayer, playerObj); - m_Manager.localPlayerJoined?.Invoke(m_LocalPlayer); + m_Manager.localPlayerJoined?.Invoke(player.PlayerId, m_LocalPlayer); } else { @@ -275,9 +273,13 @@ public void OnPlayerJoined(NetworkRunner runner, PlayerRef player) } } - public void OnSessionListUpdated(NetworkRunner runner, List sessionList) + public void OnPlayerLeft(NetworkRunner runner, PlayerRef player) { + m_Manager.playerLeft?.Invoke(player.PlayerId); + } + public void OnSessionListUpdated(NetworkRunner runner, List sessionList) + { var roomData = new List(); foreach (var session in sessionList) { @@ -297,7 +299,6 @@ public void OnSessionListUpdated(NetworkRunner runner, List session #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) { } diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonPlayerRig.cs b/Assets/Scripts/Multiplayer/Photon/PhotonPlayerRig.cs index 40a9e81912..ca7a3bab6d 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonPlayerRig.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonPlayerRig.cs @@ -17,6 +17,7 @@ using UnityEngine; using Fusion; using TiltBrush; +using System; namespace OpenBrush.Multiplayer { @@ -39,6 +40,14 @@ public class PhotonPlayerRig : NetworkBehaviour, ITransientData [SerializeField] private Transform headTransform; private PlayerRigData transmitData; + public int m_PlayerId; + + public int PlayerId + { + get { return m_PlayerId; } + set { m_PlayerId = value; } + } + public void TransmitData(PlayerRigData data) { transmitData = data; @@ -116,6 +125,14 @@ public override void Render() var remoteTR = TrTransform.TR(m_PlayerHead.InterpolationTarget.position, m_PlayerHead.InterpolationTarget.rotation); App.Scene.AsScene[headTransform] = remoteTR; } + + void OnDestroy() + { + if (transientPointer != null) + { + PointerManager.m_Instance.RemoveRemotePointer(transientPointer); + } + } } } diff --git a/Assets/Scripts/PointerManager.cs b/Assets/Scripts/PointerManager.cs index 7daa121504..38c056ce8f 100644 --- a/Assets/Scripts/PointerManager.cs +++ b/Assets/Scripts/PointerManager.cs @@ -398,6 +398,12 @@ public PointerScript CreateRemotePointer() return script; } + public void RemoveRemotePointer(PointerScript pointer) + { + m_RemoteUserPointers.Remove(pointer); + Destroy(pointer.gameObject); + } + /// The brush size, using "normalized" values in the range [0,1]. /// Guaranteed to be in [0,1]. public float GetPointerBrushSize01(InputManager.ControllerName controller) From 87c9a6260b791161f655dfff5d2b83fd19a50efc Mon Sep 17 00:00:00 2001 From: Mike Nisbet Date: Thu, 7 Dec 2023 15:14:36 +0000 Subject: [PATCH 009/174] beta tag prefab --- Assets/Prefabs/BetaTag.prefab | 415 +++++++++++++++ Assets/Prefabs/BetaTag.prefab.meta | 7 + .../PopUps/GDriveSync/DisabledElements.prefab | 490 +++--------------- .../PopUps/GDriveSync/EnabledElements.prefab | 482 +++-------------- .../Strings/Strings Shared Data.asset | 2 +- 5 files changed, 571 insertions(+), 825 deletions(-) create mode 100644 Assets/Prefabs/BetaTag.prefab create mode 100644 Assets/Prefabs/BetaTag.prefab.meta diff --git a/Assets/Prefabs/BetaTag.prefab b/Assets/Prefabs/BetaTag.prefab new file mode 100644 index 0000000000..fe7bbe60fe --- /dev/null +++ b/Assets/Prefabs/BetaTag.prefab @@ -0,0 +1,415 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &492106549256326987 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 8171046643914759413} + - component: {fileID: 2382359695583312413} + - component: {fileID: 7242636611821479444} + m_Layer: 16 + m_Name: Background + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &8171046643914759413 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 492106549256326987} + m_LocalRotation: {x: -0.7071068, y: -0, z: -0, w: 0.7071067} + m_LocalPosition: {x: 0, y: 0.0041000843, z: 0.002} + m_LocalScale: {x: 10, y: 6, z: 6} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 4378067676344109730} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &2382359695583312413 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 492106549256326987} + m_Mesh: {fileID: 4300002, guid: 3efe5b86b55995545a231ce66a53b402, type: 3} +--- !u!23 &7242636611821479444 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 492106549256326987} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 28d0bd4d20e3f8143994275754956c5e, 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: 0 + 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!1 &768695854730814423 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 7395277295064072936} + - component: {fileID: 400887753388735756} + - component: {fileID: 2556331195588322189} + - component: {fileID: 6612620938781836974} + m_Layer: 0 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &7395277295064072936 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 768695854730814423} + 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: 4378067676344109730} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 20, y: 5} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!23 &400887753388735756 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 768695854730814423} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, 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: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + 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!114 &2556331195588322189 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 768695854730814423} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9541d86e2fd84c1d9990edf0852d74ab, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_text: BETA + m_isRightToLeft: 0 + m_fontAsset: {fileID: 11400000, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + m_sharedMaterial: {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + m_fontSharedMaterials: [] + m_fontMaterial: {fileID: 0} + m_fontMaterials: [] + m_fontColor32: + serializedVersion: 2 + rgba: 4278190080 + m_fontColor: {r: 0, g: 0, b: 0, a: 1} + m_enableVertexGradient: 0 + m_colorMode: 3 + m_fontColorGradient: + topLeft: {r: 1, g: 1, b: 1, a: 1} + topRight: {r: 1, g: 1, b: 1, a: 1} + bottomLeft: {r: 1, g: 1, b: 1, a: 1} + bottomRight: {r: 1, g: 1, b: 1, a: 1} + m_fontColorGradientPreset: {fileID: 0} + m_spriteAsset: {fileID: 0} + m_tintAllSprites: 0 + m_StyleSheet: {fileID: 0} + m_TextStyleHashCode: -1183493901 + m_overrideHtmlColors: 0 + m_faceColor: + serializedVersion: 2 + rgba: 4294967295 + m_fontSize: 0.79 + m_fontSizeBase: 0.79 + m_fontWeight: 400 + m_enableAutoSizing: 0 + m_fontSizeMin: 18 + m_fontSizeMax: 72 + m_fontStyle: 0 + m_HorizontalAlignment: 2 + m_VerticalAlignment: 512 + m_textAlignment: 65535 + m_characterSpacing: 0 + m_wordSpacing: 0 + m_lineSpacing: 0 + m_lineSpacingMax: 0 + m_paragraphSpacing: 0 + m_charWidthMaxAdj: 0 + m_enableWordWrapping: 1 + m_wordWrappingRatios: 0.4 + m_overflowMode: 0 + m_linkedTextComponent: {fileID: 0} + parentLinkedComponent: {fileID: 0} + m_enableKerning: 1 + m_enableExtraPadding: 0 + checkPaddingRequired: 0 + m_isRichText: 1 + m_parseCtrlCharacters: 1 + m_isOrthographic: 0 + m_isCullingEnabled: 0 + m_horizontalMapping: 0 + m_verticalMapping: 0 + m_uvLineOffset: 0 + m_geometrySortingOrder: 0 + m_IsTextObjectScaleStatic: 0 + m_VertexBufferAutoSizeReduction: 0 + m_useMaxVisibleDescender: 1 + m_pageToDisplay: 1 + m_margin: {x: 0, y: 0, z: 0, w: 0} + m_isUsingLegacyAnimationComponent: 0 + m_isVolumetricText: 0 + _SortingLayer: 0 + _SortingLayerID: 0 + _SortingOrder: 0 + m_hasFontAssetChanged: 0 + m_renderer: {fileID: 400887753388735756} + m_maskType: 0 +--- !u!114 &6612620938781836974 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 768695854730814423} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 69beb381e244f92449b8c4cf954630e9, type: 3} + m_Name: + m_EditorClassIdentifier: + m_TrackedObjects: + - rid: 3215294302546034780 + references: + version: 2 + RefIds: + - rid: 3215294302546034780 + type: {class: TrackedUGuiGraphic, ns: UnityEngine.Localization.PropertyVariants.TrackedObjects, + asm: Unity.Localization} + data: + m_Target: {fileID: 2556331195588322189} + m_TrackedProperties: + items: + - rid: 3215294302546034781 + m_UpdateType: 0 + - rid: 3215294302546034781 + type: {class: LocalizedStringProperty, ns: UnityEngine.Localization.PropertyVariants.TrackedProperties, + asm: Unity.Localization} + data: + m_Localized: + m_TableReference: + m_TableCollectionName: GUID:c84355079ab3f3e4f8f3812258805f86 + m_TableEntryReference: + m_KeyId: 76121021711179776 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_PropertyPath: m_text +--- !u!1 &2206467382688623040 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4378067676344109730} + m_Layer: 0 + m_Name: BetaTag + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &4378067676344109730 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2206467382688623040} + 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: 7395277295064072936} + - {fileID: 8171046643914759413} + - {fileID: 1853447512016924465} + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &2586623423367620634 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1853447512016924465} + - component: {fileID: 6116880465705314692} + - component: {fileID: 5545206284097801361} + m_Layer: 16 + m_Name: Border + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1853447512016924465 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2586623423367620634} + m_LocalRotation: {x: -0.7071068, y: -0, z: -0, w: 0.7071067} + m_LocalPosition: {x: 0, y: 0.0041000843, z: 0.004} + m_LocalScale: {x: 10.75, y: 10.75, z: 7} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 4378067676344109730} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &6116880465705314692 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2586623423367620634} + m_Mesh: {fileID: 4300002, guid: 3efe5b86b55995545a231ce66a53b402, type: 3} +--- !u!23 &5545206284097801361 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2586623423367620634} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: d153b453067a0724889fb677fef801a1, 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: 0 + 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/BetaTag.prefab.meta b/Assets/Prefabs/BetaTag.prefab.meta new file mode 100644 index 0000000000..cef13a179b --- /dev/null +++ b/Assets/Prefabs/BetaTag.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 2538d4a3972b37c4c9e8649481dd9a2b +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Prefabs/PopUps/GDriveSync/DisabledElements.prefab b/Assets/Prefabs/PopUps/GDriveSync/DisabledElements.prefab index cf9219e504..f420f8cd55 100644 --- a/Assets/Prefabs/PopUps/GDriveSync/DisabledElements.prefab +++ b/Assets/Prefabs/PopUps/GDriveSync/DisabledElements.prefab @@ -166,89 +166,6 @@ MeshRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_AdditionalVertexStreams: {fileID: 0} ---- !u!1 &437988600671713722 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 5183940061544528065} - - component: {fileID: 4225421745048100917} - - component: {fileID: 8734623784063967320} - m_Layer: 16 - m_Name: Background - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!4 &5183940061544528065 -Transform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 437988600671713722} - m_LocalRotation: {x: -0.7071068, y: -0, z: -0, w: 0.7071067} - m_LocalPosition: {x: 0, y: 0.0041000843, z: 0.002} - m_LocalScale: {x: 10, y: 6, z: 6} - m_ConstrainProportionsScale: 0 - m_Children: [] - m_Father: {fileID: 4937758326570016829} - m_RootOrder: 1 - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!33 &4225421745048100917 -MeshFilter: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 437988600671713722} - m_Mesh: {fileID: 4300002, guid: 3efe5b86b55995545a231ce66a53b402, type: 3} ---- !u!23 &8734623784063967320 -MeshRenderer: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 437988600671713722} - m_Enabled: 1 - m_CastShadows: 0 - m_ReceiveShadows: 0 - m_DynamicOccludee: 1 - m_StaticShadowCaster: 0 - m_MotionVectors: 1 - m_LightProbeUsage: 1 - m_ReflectionProbeUsage: 1 - m_RayTracingMode: 2 - m_RayTraceProcedural: 0 - m_RenderingLayerMask: 1 - m_RendererPriority: 0 - m_Materials: - - {fileID: 2100000, guid: 28d0bd4d20e3f8143994275754956c5e, 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: 0 - 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!1 &461309234141555871 GameObject: m_ObjectHideFlags: 0 @@ -497,7 +414,7 @@ Transform: - {fileID: 60557364801231681} - {fileID: 3310547944333858703} - {fileID: 1038922574623870723} - - {fileID: 4937758326570016829} + - {fileID: 5342878369760894836} m_Father: {fileID: 7092514869646762003} m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} @@ -642,123 +559,6 @@ BoxCollider: serializedVersion: 2 m_Size: {x: 1.3, y: 0.3, z: 0.01} m_Center: {x: 0, y: 0, z: -0.01} ---- !u!1 &3221966458938766337 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 4937758326570016829} - m_Layer: 0 - m_Name: BetaTag - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!4 &4937758326570016829 -Transform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 3221966458938766337} - m_LocalRotation: {x: -0, y: -0, z: 0.3746118, w: 0.9271818} - m_LocalPosition: {x: 0.55, y: -0.03399992, z: -0.013999999} - m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 - m_Children: - - {fileID: 4946005680407247088} - - {fileID: 5183940061544528065} - - {fileID: 7329579163772372070} - m_Father: {fileID: 7446882484427533872} - m_RootOrder: 3 - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 44.001003} ---- !u!1 &4452402264514060099 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 7329579163772372070} - - component: {fileID: 3004005737530521341} - - component: {fileID: 2292147869231861182} - m_Layer: 16 - m_Name: Border - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!4 &7329579163772372070 -Transform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 4452402264514060099} - m_LocalRotation: {x: -0.7071068, y: -0, z: -0, w: 0.7071067} - m_LocalPosition: {x: 0, y: 0.0041000843, z: 0.004} - m_LocalScale: {x: 10.75, y: 10.75, z: 7} - m_ConstrainProportionsScale: 0 - m_Children: [] - m_Father: {fileID: 4937758326570016829} - m_RootOrder: 2 - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!33 &3004005737530521341 -MeshFilter: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 4452402264514060099} - m_Mesh: {fileID: 4300002, guid: 3efe5b86b55995545a231ce66a53b402, type: 3} ---- !u!23 &2292147869231861182 -MeshRenderer: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 4452402264514060099} - m_Enabled: 1 - m_CastShadows: 0 - m_ReceiveShadows: 0 - m_DynamicOccludee: 1 - m_StaticShadowCaster: 0 - m_MotionVectors: 1 - m_LightProbeUsage: 1 - m_ReflectionProbeUsage: 1 - m_RayTracingMode: 2 - m_RayTraceProcedural: 0 - m_RenderingLayerMask: 1 - m_RendererPriority: 0 - m_Materials: - - {fileID: 2100000, guid: d153b453067a0724889fb677fef801a1, 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: 0 - 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!1 &5108924410514366489 GameObject: m_ObjectHideFlags: 0 @@ -793,219 +593,6 @@ Transform: m_Father: {fileID: 0} m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!1 &5700003095927352931 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 4946005680407247088} - - component: {fileID: 2655483490364869926} - - component: {fileID: 3656094398338665249} - - component: {fileID: 7356415779420507544} - m_Layer: 0 - m_Name: Text - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!224 &4946005680407247088 -RectTransform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 5700003095927352931} - 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: 4937758326570016829} - m_RootOrder: 0 - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0.5, y: 0.5} - m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: 0, y: 0} - m_SizeDelta: {x: 20, y: 5} - m_Pivot: {x: 0.5, y: 0.5} ---- !u!23 &2655483490364869926 -MeshRenderer: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 5700003095927352931} - m_Enabled: 1 - m_CastShadows: 0 - m_ReceiveShadows: 0 - m_DynamicOccludee: 1 - m_StaticShadowCaster: 0 - m_MotionVectors: 1 - m_LightProbeUsage: 1 - m_ReflectionProbeUsage: 1 - m_RayTracingMode: 2 - m_RayTraceProcedural: 0 - m_RenderingLayerMask: 1 - m_RendererPriority: 0 - m_Materials: - - {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, 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: 0 - m_IgnoreNormalsForChartDetection: 0 - m_ImportantGI: 0 - m_StitchLightmapSeams: 1 - 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!114 &3656094398338665249 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 5700003095927352931} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 9541d86e2fd84c1d9990edf0852d74ab, type: 3} - m_Name: - m_EditorClassIdentifier: - m_Material: {fileID: 0} - m_Color: {r: 1, g: 1, b: 1, a: 1} - m_RaycastTarget: 1 - m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} - m_Maskable: 1 - m_OnCullStateChanged: - m_PersistentCalls: - m_Calls: [] - m_text: BETA - m_isRightToLeft: 0 - m_fontAsset: {fileID: 11400000, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} - m_sharedMaterial: {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} - m_fontSharedMaterials: [] - m_fontMaterial: {fileID: 0} - m_fontMaterials: [] - m_fontColor32: - serializedVersion: 2 - rgba: 4278190080 - m_fontColor: {r: 0, g: 0, b: 0, a: 1} - m_enableVertexGradient: 0 - m_colorMode: 3 - m_fontColorGradient: - topLeft: {r: 1, g: 1, b: 1, a: 1} - topRight: {r: 1, g: 1, b: 1, a: 1} - bottomLeft: {r: 1, g: 1, b: 1, a: 1} - bottomRight: {r: 1, g: 1, b: 1, a: 1} - m_fontColorGradientPreset: {fileID: 0} - m_spriteAsset: {fileID: 0} - m_tintAllSprites: 0 - m_StyleSheet: {fileID: 0} - m_TextStyleHashCode: -1183493901 - m_overrideHtmlColors: 0 - m_faceColor: - serializedVersion: 2 - rgba: 4294967295 - m_fontSize: 0.79 - m_fontSizeBase: 0.79 - m_fontWeight: 400 - m_enableAutoSizing: 0 - m_fontSizeMin: 18 - m_fontSizeMax: 72 - m_fontStyle: 0 - m_HorizontalAlignment: 2 - m_VerticalAlignment: 512 - m_textAlignment: 65535 - m_characterSpacing: 0 - m_wordSpacing: 0 - m_lineSpacing: 0 - m_lineSpacingMax: 0 - m_paragraphSpacing: 0 - m_charWidthMaxAdj: 0 - m_enableWordWrapping: 1 - m_wordWrappingRatios: 0.4 - m_overflowMode: 0 - m_linkedTextComponent: {fileID: 0} - parentLinkedComponent: {fileID: 0} - m_enableKerning: 1 - m_enableExtraPadding: 0 - checkPaddingRequired: 0 - m_isRichText: 1 - m_parseCtrlCharacters: 1 - m_isOrthographic: 0 - m_isCullingEnabled: 0 - m_horizontalMapping: 0 - m_verticalMapping: 0 - m_uvLineOffset: 0 - m_geometrySortingOrder: 0 - m_IsTextObjectScaleStatic: 0 - m_VertexBufferAutoSizeReduction: 0 - m_useMaxVisibleDescender: 1 - m_pageToDisplay: 1 - m_margin: {x: 0, y: 0, z: 0, w: 0} - m_isUsingLegacyAnimationComponent: 0 - m_isVolumetricText: 0 - _SortingLayer: 0 - _SortingLayerID: 0 - _SortingOrder: 0 - m_hasFontAssetChanged: 0 - m_renderer: {fileID: 2655483490364869926} - m_maskType: 0 ---- !u!114 &7356415779420507544 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 5700003095927352931} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 69beb381e244f92449b8c4cf954630e9, type: 3} - m_Name: - m_EditorClassIdentifier: - m_TrackedObjects: - - rid: 3215294302546034804 - references: - version: 2 - RefIds: - - rid: 3215294302546034804 - type: {class: TrackedUGuiGraphic, ns: UnityEngine.Localization.PropertyVariants.TrackedObjects, - asm: Unity.Localization} - data: - m_Target: {fileID: 3656094398338665249} - m_TrackedProperties: - items: - - rid: 3215294302546034805 - m_UpdateType: 0 - - rid: 3215294302546034805 - type: {class: LocalizedStringProperty, ns: UnityEngine.Localization.PropertyVariants.TrackedProperties, - asm: Unity.Localization} - data: - m_Localized: - m_TableReference: - m_TableCollectionName: GUID:c84355079ab3f3e4f8f3812258805f86 - m_TableEntryReference: - m_KeyId: 76121021711179776 - m_Key: - m_FallbackState: 0 - m_WaitForCompletion: 0 - m_LocalVariables: [] - m_PropertyPath: m_text --- !u!1 &8618323702079337633 GameObject: m_ObjectHideFlags: 0 @@ -1434,3 +1021,78 @@ MonoBehaviour: m_WaitForCompletion: 0 m_LocalVariables: [] m_PropertyPath: m_text +--- !u!1001 &8568013399981233622 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 7446882484427533872} + m_Modifications: + - target: {fileID: 2206467382688623040, guid: 2538d4a3972b37c4c9e8649481dd9a2b, + type: 3} + propertyPath: m_Name + value: BetaTag + objectReference: {fileID: 0} + - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, + type: 3} + propertyPath: m_RootOrder + value: 3 + objectReference: {fileID: 0} + - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, + type: 3} + propertyPath: m_LocalPosition.x + value: 0.55 + objectReference: {fileID: 0} + - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, + type: 3} + propertyPath: m_LocalPosition.y + value: -0.03399992 + objectReference: {fileID: 0} + - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, + type: 3} + propertyPath: m_LocalPosition.z + value: -0.013999999 + objectReference: {fileID: 0} + - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, + type: 3} + propertyPath: m_LocalRotation.w + value: 0.9271818 + objectReference: {fileID: 0} + - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, + type: 3} + propertyPath: m_LocalRotation.x + value: -0 + objectReference: {fileID: 0} + - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, + type: 3} + propertyPath: m_LocalRotation.y + value: -0 + objectReference: {fileID: 0} + - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, + type: 3} + propertyPath: m_LocalRotation.z + value: 0.3746118 + objectReference: {fileID: 0} + - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, + type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, + type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, + type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + m_RemovedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: 2538d4a3972b37c4c9e8649481dd9a2b, type: 3} +--- !u!4 &5342878369760894836 stripped +Transform: + m_CorrespondingSourceObject: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, + type: 3} + m_PrefabInstance: {fileID: 8568013399981233622} + m_PrefabAsset: {fileID: 0} diff --git a/Assets/Prefabs/PopUps/GDriveSync/EnabledElements.prefab b/Assets/Prefabs/PopUps/GDriveSync/EnabledElements.prefab index 014438560f..d39c2550ac 100644 --- a/Assets/Prefabs/PopUps/GDriveSync/EnabledElements.prefab +++ b/Assets/Prefabs/PopUps/GDriveSync/EnabledElements.prefab @@ -2933,123 +2933,6 @@ MeshRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_AdditionalVertexStreams: {fileID: 0} ---- !u!1 &6604792200441248002 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 7058727754757553705} - - component: {fileID: 3219758793499752604} - - component: {fileID: 3799314549064326537} - m_Layer: 16 - m_Name: Border - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!4 &7058727754757553705 -Transform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 6604792200441248002} - m_LocalRotation: {x: -0.7071068, y: -0, z: -0, w: 0.7071067} - m_LocalPosition: {x: 0, y: 0.0041000843, z: 0.004} - m_LocalScale: {x: 10.75, y: 10.75, z: 7} - m_ConstrainProportionsScale: 0 - m_Children: [] - m_Father: {fileID: 4940275707197938618} - m_RootOrder: 2 - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!33 &3219758793499752604 -MeshFilter: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 6604792200441248002} - m_Mesh: {fileID: 4300002, guid: 3efe5b86b55995545a231ce66a53b402, type: 3} ---- !u!23 &3799314549064326537 -MeshRenderer: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 6604792200441248002} - m_Enabled: 1 - m_CastShadows: 0 - m_ReceiveShadows: 0 - m_DynamicOccludee: 1 - m_StaticShadowCaster: 0 - m_MotionVectors: 1 - m_LightProbeUsage: 1 - m_ReflectionProbeUsage: 1 - m_RayTracingMode: 2 - m_RayTraceProcedural: 0 - m_RenderingLayerMask: 1 - m_RendererPriority: 0 - m_Materials: - - {fileID: 2100000, guid: d153b453067a0724889fb677fef801a1, 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: 0 - 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!1 &7409430375216068824 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 4940275707197938618} - m_Layer: 0 - m_Name: BetaTag - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!4 &4940275707197938618 -Transform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 7409430375216068824} - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: 0.721, y: 0.481, z: -0.02} - m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 - m_Children: - - {fileID: 2228202418004579312} - - {fileID: 659782219572039149} - - {fileID: 7058727754757553705} - m_Father: {fileID: 8058103854604790177} - m_RootOrder: 1 - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &7465046787535995228 GameObject: m_ObjectHideFlags: 0 @@ -3748,219 +3631,6 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 8daae932e00bdb749b4f667b08f50ca8, type: 3} m_Name: m_EditorClassIdentifier: ---- !u!1 &8279748072826843855 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 2228202418004579312} - - component: {fileID: 9069504756444048404} - - component: {fileID: 6572034308306940565} - - component: {fileID: 2560815885350393782} - m_Layer: 0 - m_Name: Text - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!224 &2228202418004579312 -RectTransform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 8279748072826843855} - 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: 4940275707197938618} - m_RootOrder: 0 - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0.5, y: 0.5} - m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: 0, y: 0} - m_SizeDelta: {x: 20, y: 5} - m_Pivot: {x: 0.5, y: 0.5} ---- !u!23 &9069504756444048404 -MeshRenderer: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 8279748072826843855} - m_Enabled: 1 - m_CastShadows: 0 - m_ReceiveShadows: 0 - m_DynamicOccludee: 1 - m_StaticShadowCaster: 0 - m_MotionVectors: 1 - m_LightProbeUsage: 1 - m_ReflectionProbeUsage: 1 - m_RayTracingMode: 2 - m_RayTraceProcedural: 0 - m_RenderingLayerMask: 1 - m_RendererPriority: 0 - m_Materials: - - {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, 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: 0 - m_IgnoreNormalsForChartDetection: 0 - m_ImportantGI: 0 - m_StitchLightmapSeams: 1 - 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!114 &6572034308306940565 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 8279748072826843855} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 9541d86e2fd84c1d9990edf0852d74ab, type: 3} - m_Name: - m_EditorClassIdentifier: - m_Material: {fileID: 0} - m_Color: {r: 1, g: 1, b: 1, a: 1} - m_RaycastTarget: 1 - m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} - m_Maskable: 1 - m_OnCullStateChanged: - m_PersistentCalls: - m_Calls: [] - m_text: BETA - m_isRightToLeft: 0 - m_fontAsset: {fileID: 11400000, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} - m_sharedMaterial: {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} - m_fontSharedMaterials: [] - m_fontMaterial: {fileID: 0} - m_fontMaterials: [] - m_fontColor32: - serializedVersion: 2 - rgba: 4278190080 - m_fontColor: {r: 0, g: 0, b: 0, a: 1} - m_enableVertexGradient: 0 - m_colorMode: 3 - m_fontColorGradient: - topLeft: {r: 1, g: 1, b: 1, a: 1} - topRight: {r: 1, g: 1, b: 1, a: 1} - bottomLeft: {r: 1, g: 1, b: 1, a: 1} - bottomRight: {r: 1, g: 1, b: 1, a: 1} - m_fontColorGradientPreset: {fileID: 0} - m_spriteAsset: {fileID: 0} - m_tintAllSprites: 0 - m_StyleSheet: {fileID: 0} - m_TextStyleHashCode: -1183493901 - m_overrideHtmlColors: 0 - m_faceColor: - serializedVersion: 2 - rgba: 4294967295 - m_fontSize: 0.79 - m_fontSizeBase: 0.79 - m_fontWeight: 400 - m_enableAutoSizing: 0 - m_fontSizeMin: 18 - m_fontSizeMax: 72 - m_fontStyle: 0 - m_HorizontalAlignment: 2 - m_VerticalAlignment: 512 - m_textAlignment: 65535 - m_characterSpacing: 0 - m_wordSpacing: 0 - m_lineSpacing: 0 - m_lineSpacingMax: 0 - m_paragraphSpacing: 0 - m_charWidthMaxAdj: 0 - m_enableWordWrapping: 1 - m_wordWrappingRatios: 0.4 - m_overflowMode: 0 - m_linkedTextComponent: {fileID: 0} - parentLinkedComponent: {fileID: 0} - m_enableKerning: 1 - m_enableExtraPadding: 0 - checkPaddingRequired: 0 - m_isRichText: 1 - m_parseCtrlCharacters: 1 - m_isOrthographic: 0 - m_isCullingEnabled: 0 - m_horizontalMapping: 0 - m_verticalMapping: 0 - m_uvLineOffset: 0 - m_geometrySortingOrder: 0 - m_IsTextObjectScaleStatic: 0 - m_VertexBufferAutoSizeReduction: 0 - m_useMaxVisibleDescender: 1 - m_pageToDisplay: 1 - m_margin: {x: 0, y: 0, z: 0, w: 0} - m_isUsingLegacyAnimationComponent: 0 - m_isVolumetricText: 0 - _SortingLayer: 0 - _SortingLayerID: 0 - _SortingOrder: 0 - m_hasFontAssetChanged: 0 - m_renderer: {fileID: 9069504756444048404} - m_maskType: 0 ---- !u!114 &2560815885350393782 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 8279748072826843855} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 69beb381e244f92449b8c4cf954630e9, type: 3} - m_Name: - m_EditorClassIdentifier: - m_TrackedObjects: - - rid: 3215294302546034780 - references: - version: 2 - RefIds: - - rid: 3215294302546034780 - type: {class: TrackedUGuiGraphic, ns: UnityEngine.Localization.PropertyVariants.TrackedObjects, - asm: Unity.Localization} - data: - m_Target: {fileID: 6572034308306940565} - m_TrackedProperties: - items: - - rid: 3215294302546034781 - m_UpdateType: 0 - - rid: 3215294302546034781 - type: {class: LocalizedStringProperty, ns: UnityEngine.Localization.PropertyVariants.TrackedProperties, - asm: Unity.Localization} - data: - m_Localized: - m_TableReference: - m_TableCollectionName: GUID:c84355079ab3f3e4f8f3812258805f86 - m_TableEntryReference: - m_KeyId: 76121021711179776 - m_Key: - m_FallbackState: 0 - m_WaitForCompletion: 0 - m_LocalVariables: [] - m_PropertyPath: m_text --- !u!1 &8383965637925172465 GameObject: m_ObjectHideFlags: 0 @@ -4690,86 +4360,78 @@ MonoBehaviour: m_WaitForCompletion: 0 m_LocalVariables: [] m_PropertyPath: m_text ---- !u!1 &9122383556718042707 -GameObject: +--- !u!1001 &8668698999585902872 +PrefabInstance: m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 659782219572039149} - - component: {fileID: 6431897053494340357} - - component: {fileID: 2075706908915906316} - m_Layer: 16 - m_Name: Background - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!4 &659782219572039149 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 8058103854604790177} + m_Modifications: + - target: {fileID: 2206467382688623040, guid: 2538d4a3972b37c4c9e8649481dd9a2b, + type: 3} + propertyPath: m_Name + value: BetaTag + objectReference: {fileID: 0} + - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, + type: 3} + propertyPath: m_RootOrder + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, + type: 3} + propertyPath: m_LocalPosition.x + value: 0.721 + objectReference: {fileID: 0} + - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, + type: 3} + propertyPath: m_LocalPosition.y + value: 0.481 + objectReference: {fileID: 0} + - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, + type: 3} + propertyPath: m_LocalPosition.z + value: -0.02 + objectReference: {fileID: 0} + - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, + type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, + type: 3} + propertyPath: m_LocalRotation.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, + type: 3} + propertyPath: m_LocalRotation.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, + type: 3} + propertyPath: m_LocalRotation.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, + type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, + type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, + type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + m_RemovedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: 2538d4a3972b37c4c9e8649481dd9a2b, type: 3} +--- !u!4 &4940275707197938618 stripped Transform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 9122383556718042707} - m_LocalRotation: {x: -0.7071068, y: -0, z: -0, w: 0.7071067} - m_LocalPosition: {x: 0, y: 0.0041000843, z: 0.002} - m_LocalScale: {x: 10, y: 6, z: 6} - m_ConstrainProportionsScale: 0 - m_Children: [] - m_Father: {fileID: 4940275707197938618} - m_RootOrder: 1 - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!33 &6431897053494340357 -MeshFilter: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} + m_CorrespondingSourceObject: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, + type: 3} + m_PrefabInstance: {fileID: 8668698999585902872} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 9122383556718042707} - m_Mesh: {fileID: 4300002, guid: 3efe5b86b55995545a231ce66a53b402, type: 3} ---- !u!23 &2075706908915906316 -MeshRenderer: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 9122383556718042707} - m_Enabled: 1 - m_CastShadows: 0 - m_ReceiveShadows: 0 - m_DynamicOccludee: 1 - m_StaticShadowCaster: 0 - m_MotionVectors: 1 - m_LightProbeUsage: 1 - m_ReflectionProbeUsage: 1 - m_RayTracingMode: 2 - m_RayTraceProcedural: 0 - m_RenderingLayerMask: 1 - m_RendererPriority: 0 - m_Materials: - - {fileID: 2100000, guid: 28d0bd4d20e3f8143994275754956c5e, 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: 0 - 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/Settings/Localization/Strings/Strings Shared Data.asset b/Assets/Settings/Localization/Strings/Strings Shared Data.asset index dad2dc39ea..ad5f0cc038 100644 --- a/Assets/Settings/Localization/Strings/Strings Shared Data.asset +++ b/Assets/Settings/Localization/Strings/Strings Shared Data.asset @@ -1452,7 +1452,7 @@ MonoBehaviour: m_Metadata: m_Items: [] - m_Id: 76121021711179776 - m_Key: POPUP_GDRIVE_BETATAG + m_Key: LABEL_BETATAG m_Metadata: m_Items: [] - m_Id: 76121263181455360 From 3ee080a47316c727441645fd9b904eebefe9561f Mon Sep 17 00:00:00 2001 From: Mike Nisbet Date: Thu, 7 Dec 2023 15:16:32 +0000 Subject: [PATCH 010/174] Add alpha tag to multiplayer --- Assets/Prefabs/Panels/MultiplayerPanel.prefab | 107 +++++++++++++++++- .../Strings/Strings Shared Data.asset | 4 + .../Localization/Strings/Strings_en.asset | 8 ++ 3 files changed, 116 insertions(+), 3 deletions(-) diff --git a/Assets/Prefabs/Panels/MultiplayerPanel.prefab b/Assets/Prefabs/Panels/MultiplayerPanel.prefab index 07cee7bdab..e480cc9740 100644 --- a/Assets/Prefabs/Panels/MultiplayerPanel.prefab +++ b/Assets/Prefabs/Panels/MultiplayerPanel.prefab @@ -86,7 +86,7 @@ MonoBehaviour: m_BorderMeshWidth: 0.02 m_BorderMeshAdvWidth: 0.01 m_PanelSensitivity: 0.1 - m_ClampToBounds: 0 + m_ClampToBounds: 1 m_ReticleBounds: {x: 2.15, y: 2.55, z: 0} m_BorderSphereHighlightRadius: 2.5 m_PositioningSpheresBounds: {x: 1, y: 1} @@ -283,6 +283,7 @@ Transform: m_Children: - {fileID: 420492} - {fileID: 480900} + - {fileID: 6533003833265780531} - {fileID: 4696385544466816} - {fileID: 4410849844686322} m_Father: {fileID: 415082} @@ -489,7 +490,7 @@ Transform: - {fileID: 4153922631777284} - {fileID: 4402564000880478} m_Father: {fileID: 499980} - m_RootOrder: 3 + m_RootOrder: 4 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33802401890004714 MeshFilter: @@ -829,7 +830,7 @@ Transform: - {fileID: 4887086521611946} - {fileID: 4148024965627128} m_Father: {fileID: 499980} - m_RootOrder: 2 + m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1889327720883680 GameObject: @@ -1216,3 +1217,103 @@ Transform: m_Father: {fileID: 415082} m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1001 &7380239823636675985 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 499980} + m_Modifications: + - target: {fileID: 2206467382688623040, guid: 2538d4a3972b37c4c9e8649481dd9a2b, + type: 3} + propertyPath: m_Name + value: AlphaTag + objectReference: {fileID: 0} + - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, + type: 3} + propertyPath: m_RootOrder + value: 2 + objectReference: {fileID: 0} + - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, + type: 3} + propertyPath: m_LocalScale.x + value: 2 + objectReference: {fileID: 0} + - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, + type: 3} + propertyPath: m_LocalScale.y + value: 2 + objectReference: {fileID: 0} + - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, + type: 3} + propertyPath: m_LocalScale.z + value: 2 + objectReference: {fileID: 0} + - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, + type: 3} + propertyPath: m_LocalPosition.x + value: 0.76 + objectReference: {fileID: 0} + - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, + type: 3} + propertyPath: m_LocalPosition.y + value: 1.054 + objectReference: {fileID: 0} + - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, + type: 3} + propertyPath: m_LocalPosition.z + value: -0.02 + objectReference: {fileID: 0} + - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, + type: 3} + propertyPath: m_LocalRotation.w + value: 0.92387956 + objectReference: {fileID: 0} + - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, + type: 3} + propertyPath: m_LocalRotation.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, + type: 3} + propertyPath: m_LocalRotation.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, + type: 3} + propertyPath: m_LocalRotation.z + value: -0.38268343 + objectReference: {fileID: 0} + - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, + type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, + type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, + type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: -45 + objectReference: {fileID: 0} + - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, + type: 3} + propertyPath: m_ConstrainProportionsScale + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 6612620938781836974, guid: 2538d4a3972b37c4c9e8649481dd9a2b, + type: 3} + propertyPath: managedReferences[3215294302546034781].m_Localized.m_TableEntryReference.m_KeyId + value: 176097607781543936 + objectReference: {fileID: 0} + m_RemovedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: 2538d4a3972b37c4c9e8649481dd9a2b, type: 3} +--- !u!4 &6533003833265780531 stripped +Transform: + m_CorrespondingSourceObject: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, + type: 3} + m_PrefabInstance: {fileID: 7380239823636675985} + m_PrefabAsset: {fileID: 0} diff --git a/Assets/Settings/Localization/Strings/Strings Shared Data.asset b/Assets/Settings/Localization/Strings/Strings Shared Data.asset index ad5f0cc038..762871fcda 100644 --- a/Assets/Settings/Localization/Strings/Strings Shared Data.asset +++ b/Assets/Settings/Localization/Strings/Strings Shared Data.asset @@ -3287,6 +3287,10 @@ MonoBehaviour: m_Key: MULTIPLAYER_PANEL_CLOSE_BUTTON_DESCRIPTION m_Metadata: m_Items: [] + - m_Id: 176097607781543936 + m_Key: LABEL_ALPHATAG + m_Metadata: + m_Items: [] m_Metadata: m_Items: [] m_KeyGenerator: diff --git a/Assets/Settings/Localization/Strings/Strings_en.asset b/Assets/Settings/Localization/Strings/Strings_en.asset index 95eb304311..74e89ce8e8 100644 --- a/Assets/Settings/Localization/Strings/Strings_en.asset +++ b/Assets/Settings/Localization/Strings/Strings_en.asset @@ -3476,6 +3476,14 @@ MonoBehaviour: m_Localized: Close m_Metadata: m_Items: [] + - m_Id: 176097607781543936 + m_Localized: ALPHA + m_Metadata: + m_Items: [] + - m_Id: 156198200358006784 + m_Localized: Multiplayer (Alpha) + m_Metadata: + m_Items: [] references: version: 2 RefIds: [] From 3882616bf25e67008ae48fa1b546de0a1885bc96 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Fri, 22 Dec 2023 09:55:06 +0000 Subject: [PATCH 011/174] Remove duplicate "using" block [CI BUILD] --- Assets/Scripts/Multiplayer/MultiplayerManager.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Assets/Scripts/Multiplayer/MultiplayerManager.cs b/Assets/Scripts/Multiplayer/MultiplayerManager.cs index 95d08815d2..5c59e71834 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerManager.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerManager.cs @@ -23,10 +23,6 @@ #endif using TiltBrush; -#if OCULUS_SUPPORTED -using OVRPlatform = Oculus.Platform; -#endif // OCULUS_SUPPORTED - namespace OpenBrush.Multiplayer { public enum MultiplayerType @@ -83,7 +79,7 @@ void Start() switch (m_MultiplayerType) { case MultiplayerType.Photon: -#if FUSION_WEAVER +#if FUSION_WEAVER m_Manager = new PhotonManager(this); #endif // FUSION_WEAVER break; From de5499aabf7e2d18e1712fddbda3397c88d268c1 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Thu, 3 Oct 2024 19:01:05 +0100 Subject: [PATCH 012/174] Trigger CI workflow From 4fe8071b923e9972ecbea27ffef5527b50a98d84 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Mon, 7 Oct 2024 09:45:53 +0100 Subject: [PATCH 013/174] Minimal UI Implementation for multiplayer room -Implemented global command: MultiplayerJoinRoom. -Updated the multiplayer panel prefab and manager. -Changed the admin panel layout from hexagon to octagon. TODO: -Add room name editing capabilities. --- Assets/Prefabs/Panels/AdminPanel.prefab | 429 +++- Assets/Prefabs/Panels/MultiplayerPanel.prefab | 1742 +++++++++++++++-- Assets/Scripts/GUI/MultiplayerPanel.cs | 84 +- .../Scripts/Multiplayer/MultiplayerManager.cs | 4 +- .../Multiplayer/Photon/PhotonManager.cs | 23 +- Assets/Scripts/SketchControlsScript.cs | 1 + Assets/XR/XRGeneralSettings.asset | 2 +- 7 files changed, 2063 insertions(+), 222 deletions(-) diff --git a/Assets/Prefabs/Panels/AdminPanel.prefab b/Assets/Prefabs/Panels/AdminPanel.prefab index 2a568ee5ed..1e8c7aceed 100644 --- a/Assets/Prefabs/Panels/AdminPanel.prefab +++ b/Assets/Prefabs/Panels/AdminPanel.prefab @@ -25,6 +25,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000816559363234} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 6.277, y: 15.634, z: 3.794} m_LocalScale: {x: 1, y: 1, z: 1} @@ -34,7 +35,6 @@ Transform: - {fileID: 4603977406768154} - {fileID: 4035380900912510} m_Father: {fileID: 0} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!114 &114828844590133850 MonoBehaviour: @@ -233,13 +233,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1106930226929544} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: -0.049999878} m_LocalScale: {x: 0.85057116, y: 0.85057116, z: 0.85057116} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4402161299253448} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33482202212998726 MeshFilter: @@ -318,13 +318,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1151082384707834} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: 0, y: 0.4499998, z: 0.049999952} + m_LocalPosition: {x: -0.218, y: 0.476, z: 0.049999952} m_LocalScale: {x: 0.35, y: 0.35, z: 0.35} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4315711334760372} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33825377552793314 MeshFilter: @@ -384,9 +384,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1151082384707834} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1, y: 1, z: 0.1} m_Center: {x: -0.000000074505806, y: 0.000000022351742, z: 0} --- !u!114 &114436392384945342 @@ -492,13 +500,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1192991703587922} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1.6, y: 1.6, z: 1.6} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4482757564845618} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33907749212384082 MeshFilter: @@ -574,13 +582,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1222021979522926} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: -0.04, z: 0.05} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4035380900912510} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33386300729919342 MeshFilter: @@ -614,13 +622,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1229321774763870} + serializedVersion: 2 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: 4035380900912510} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!65 &65063648299555090 BoxCollider: @@ -630,9 +638,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1229321774763870} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1.5, y: 1.55, z: 0.05} m_Center: {x: 0, y: -0.05, z: -0.03} --- !u!1 &1262073490112886 @@ -662,13 +678,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1262073490112886} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: 0.38999987, y: 0.22500038, z: 0.049999952} + m_LocalPosition: {x: 0.463, y: 0.216, z: 0.049999952} m_LocalScale: {x: 0.35, y: 0.35, z: 0.35} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4315711334760372} - m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33941583646683618 MeshFilter: @@ -728,9 +744,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1262073490112886} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1, y: 1, z: 0.1} m_Center: {x: -0.000000074505806, y: 0.000000022351742, z: 0} --- !u!114 &114969883342015630 @@ -836,13 +860,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1289872023204522} + serializedVersion: 2 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: 4402161299253448} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33356562983312826 MeshFilter: @@ -917,6 +941,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1349288981255934} + serializedVersion: 2 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} @@ -924,6 +949,7 @@ Transform: m_Children: - {fileID: 4931497879020284} - {fileID: 4298548666588398} + - {fileID: 3913670656884359384} - {fileID: 4476673398456346} - {fileID: 4115296977812378} - {fileID: 4670881044846100} @@ -936,7 +962,6 @@ Transform: - {fileID: 4569698380707294} - {fileID: 412988840869617173} m_Father: {fileID: 4035380900912510} - m_RootOrder: 6 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1411794300516396 GameObject: @@ -965,6 +990,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1411794300516396} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0.025} m_LocalScale: {x: 0.45, y: 0.45, z: 0.45} @@ -973,7 +999,6 @@ Transform: - {fileID: 4176161590853336} - {fileID: 4188334422648780} m_Father: {fileID: 4035380900912510} - m_RootOrder: 5 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33703830478315952 MeshFilter: @@ -1113,9 +1138,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1411794300516396} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1, y: 1, z: 0.25} m_Center: {x: 0, y: 0, z: 0} --- !u!1 &1577576848578658 @@ -1141,6 +1174,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1577576848578658} + serializedVersion: 2 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} @@ -1154,7 +1188,6 @@ Transform: - {fileID: 4402161299253448} - {fileID: 4315711334760372} m_Father: {fileID: 4482757564845618} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1603096109966042 GameObject: @@ -1182,13 +1215,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1603096109966042} + serializedVersion: 2 m_LocalRotation: {x: -0.7071068, y: -0, z: -0, w: 0.7071067} - m_LocalPosition: {x: 0.25899982, y: 0.21700001, z: -0.035000086} + m_LocalPosition: {x: 0.432, y: 0.444, z: -0.035000086} m_LocalScale: {x: 0.08, y: 0.029999977, z: 0.07999991} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4035380900912510} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33529072324201512 MeshFilter: @@ -1280,13 +1313,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1712905289553642} + serializedVersion: 2 m_LocalRotation: {x: -0, y: 1, z: -0, w: 0} - m_LocalPosition: {x: 0.34899998, y: 0.5290003, z: 0.049999952} + m_LocalPosition: {x: 0.5220003, y: 0.75599957, z: 0.049999952} m_LocalScale: {x: 10, y: 10, z: 10} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4315711334760372} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 180, z: 0} --- !u!33 &33766935304042522 MeshFilter: @@ -1346,9 +1379,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1712905289553642} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 0.026, y: 0.023, z: 0.0035} m_Center: {x: 0, y: 0.0025, z: 0} --- !u!114 &114210005370009680 @@ -1456,13 +1497,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1784932838058358} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: 0.38999987, y: -0.19999981, z: 0.049999952} - m_LocalScale: {x: 0.25, y: 0.25, z: 0.25} + m_LocalPosition: {x: 0.49, y: -0.184, z: 0.049999952} + m_LocalScale: {x: 0.35, y: 0.35, z: 0.35} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4315711334760372} - m_RootOrder: 6 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33541849226562688 MeshFilter: @@ -1522,9 +1563,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1784932838058358} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1, y: 1, z: 0.1} m_Center: {x: -0.000000074505806, y: 0.000000022351742, z: 0} --- !u!114 &114451730632273282 @@ -1632,13 +1681,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1793958170638982} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: -0.38999987, y: 0.22500038, z: 0.049999952} + m_LocalPosition: {x: -0.48, y: 0.193, z: 0.049999952} m_LocalScale: {x: 0.35, y: 0.35, z: 0.35} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4315711334760372} - m_RootOrder: 11 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33821366212915112 MeshFilter: @@ -1698,9 +1747,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1793958170638982} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1, y: 1, z: 0.1} m_Center: {x: -0.000000074505806, y: 0.000000022351742, z: 0} --- !u!114 &114524679305128758 @@ -1807,13 +1864,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1835688465934486} + serializedVersion: 2 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_LocalScale: {x: 1.3, y: 1.3, z: 1.3} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4035380900912510} - m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33453747807715154 MeshFilter: @@ -1905,13 +1962,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1841057133452878} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: 0.38999987, y: 0.22500038, z: 0.049999952} + m_LocalPosition: {x: 0.459, y: 0.213, z: 0.049999952} m_LocalScale: {x: 0.35, y: 0.35, z: 0.35} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4315711334760372} - m_RootOrder: 4 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33555655433307320 MeshFilter: @@ -1971,9 +2028,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1841057133452878} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1, y: 1, z: 0.1} m_Center: {x: -0.000000074505806, y: 0.000000022351742, z: 0} --- !u!114 &114553944017920612 @@ -2081,13 +2146,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1843451682130658} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: 0, y: -0.4499998, z: 0.049999952} + m_LocalPosition: {x: -0.206, y: -0.488, z: 0.049999952} m_LocalScale: {x: 0.35, y: 0.35, z: 0.35} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4315711334760372} - m_RootOrder: 8 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33394186971539132 MeshFilter: @@ -2147,9 +2212,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1843451682130658} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1.0000001, y: 1.0000001, z: 0.1} m_Center: {x: 0, y: 0, z: 0} --- !u!114 &114591466390577530 @@ -2277,13 +2350,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1857847558550948} + serializedVersion: 2 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_LocalScale: {x: 1.3, y: 1.3, z: 1.3} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4035380900912510} - m_RootOrder: 4 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33266412785695894 MeshFilter: @@ -2372,13 +2445,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1900864892112550} + serializedVersion: 2 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: 4482757564845618} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!65 &65962833710381506 BoxCollider: @@ -2388,9 +2461,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1900864892112550} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 2, y: 2, z: 0.5} m_Center: {x: 0, y: 0, z: 0} --- !u!1 &1922662397484504 @@ -2420,13 +2501,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1922662397484504} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: 0, y: 0.4499998, z: 0.049999952} + m_LocalPosition: {x: -0.221, y: 0.47, z: 0.049999952} m_LocalScale: {x: 0.35, y: 0.35, z: 0.35} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4315711334760372} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33700391611944570 MeshFilter: @@ -2486,9 +2567,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1922662397484504} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1, y: 1, z: 0.1} m_Center: {x: -0.000000074505806, y: 0.000000022351742, z: 0} --- !u!114 &114805731067982576 @@ -2596,13 +2685,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1936268928807740} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: 0.38999987, y: -0.19999981, z: 0.049999952} - m_LocalScale: {x: 0.25, y: 0.25, z: 0.25} + m_LocalPosition: {x: 0.487, y: -0.178, z: 0.049999952} + m_LocalScale: {x: 0.35, y: 0.35, z: 0.35} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4315711334760372} - m_RootOrder: 5 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33183811785653948 MeshFilter: @@ -2662,9 +2751,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1936268928807740} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1, y: 1, z: 0.1} m_Center: {x: -0.000000074505806, y: 0.000000022351742, z: 0} --- !u!114 &114213771481485110 @@ -2772,13 +2869,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1960334381407134} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: -0.38999987, y: -0.19999981, z: 0.049999952} - m_LocalScale: {x: 0.25, y: 0.25, z: 0.25} + m_LocalPosition: {x: -0.487, y: -0.19999981, z: 0.049999952} + m_LocalScale: {x: 0.35, y: 0.35, z: 0.35} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4315711334760372} - m_RootOrder: 9 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33145436996304012 MeshFilter: @@ -2838,9 +2935,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1960334381407134} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1, y: 1, z: 0.1} m_Center: {x: -0.000000074505806, y: 0.000000022351742, z: 0} --- !u!114 &114459347850906084 @@ -2948,13 +3053,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1976071349665942} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: -0.38999987, y: -0.19999981, z: 0.049999952} - m_LocalScale: {x: 0.25, y: 0.25, z: 0.25} + m_LocalPosition: {x: -0.484, y: -0.19999981, z: 0.049999952} + m_LocalScale: {x: 0.35, y: 0.35, z: 0.35} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4315711334760372} - m_RootOrder: 10 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33631275512957272 MeshFilter: @@ -3014,9 +3119,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1976071349665942} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1, y: 1, z: 0.1} m_Center: {x: -0.000000074505806, y: 0.000000022351742, z: 0} --- !u!114 &114961917929844728 @@ -3097,6 +3210,190 @@ MonoBehaviour: references: version: 2 RefIds: [] +--- !u!1 &1283872556223845477 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3913670656884359384} + - component: {fileID: 1109356708297080057} + - component: {fileID: 6407745213757458711} + - component: {fileID: 6605348980898369502} + - component: {fileID: 8695917633644188848} + m_Layer: 16 + m_Name: Button_SaveOptions (1) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &3913670656884359384 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1283872556223845477} + serializedVersion: 2 + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0.186, y: 0.482, z: 0.049999952} + m_LocalScale: {x: 0.35, y: 0.35, z: 0.35} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 4315711334760372} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &1109356708297080057 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1283872556223845477} + m_Mesh: {fileID: 4300000, guid: 3eae18cf12435234ab8717789135e90b, type: 3} +--- !u!23 &6407745213757458711 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1283872556223845477} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 40d29de2bdc11f04dbfa25059165916e, 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: 0 + 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!65 &6605348980898369502 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1283872556223845477} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 0.1} + m_Center: {x: -0.000000074505806, y: 0.000000022351742, z: 0} +--- !u!114 &8695917633644188848 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1283872556223845477} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 6c6859eec74651247968d56b594ac313, type: 3} + m_Name: + m_EditorClassIdentifier: + m_DescriptionType: 0 + m_DescriptionYOffset: 0 + m_DescriptionText: ADMIN_PANEL_SAVE_SKETCH_BUTTON_DESCRIPTION + m_LocalizedDescription: + m_TableReference: + m_TableCollectionName: GUID:c84355079ab3f3e4f8f3812258805f86 + m_TableEntryReference: + m_KeyId: 5064506084139008 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_DescriptionTextExtra: + m_LocalizedDescriptionExtra: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_DescriptionActivateSpeed: 12 + m_DescriptionZScale: 1 + m_ButtonTexture: {fileID: 0} + m_AtlasTexture: 1 + m_ToggleButton: 0 + m_LongPressReleaseButton: 0 + m_ButtonHasPressedAudio: 0 + m_ZAdjustHover: -0.02 + m_ZAdjustClick: 0.05 + m_HoverScale: 1.1 + m_HoverBoxColliderGrow: 0.2 + m_AddOverlay: 0 + m_Command: 0 + m_CommandParam: -1 + m_CommandParam2: -1 + m_RequiresPopup: 1 + m_CenterPopupOnButton: 0 + m_PopupOffset: {x: 0, y: 0, z: 0} + m_PopupText: + m_LocalizedPopup: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_ToggleOnDescription: + m_LocalizedToggleOnDescription: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_ToggleOnTexture: {fileID: 0} + m_AllowUnavailable: 1 + m_LinkedUIObject: {fileID: 0} + references: + version: 2 + RefIds: [] --- !u!1 &5787249127137797294 GameObject: m_ObjectHideFlags: 0 @@ -3124,13 +3421,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 5787249127137797294} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: 0.349, y: -0.5290003, z: 0.049999952} - m_LocalScale: {x: 0.25, y: 0.25, z: 0.25} + m_LocalPosition: {x: 0.196, y: -0.488, z: 0.049999952} + m_LocalScale: {x: 0.35, y: 0.35, z: 0.35} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4315711334760372} - m_RootOrder: 7 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &1706514172525796950 MeshFilter: @@ -3190,9 +3487,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 5787249127137797294} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1, y: 1, z: 0.1} m_Center: {x: -0.000000074505806, y: 0.000000022351742, z: 0} --- !u!114 &6550189700625956158 @@ -3278,6 +3583,7 @@ PrefabInstance: m_ObjectHideFlags: 0 serializedVersion: 2 m_Modification: + serializedVersion: 3 m_TransformParent: {fileID: 4315711334760372} m_Modifications: - target: {fileID: 478447133200725155, guid: 604a64bb74b56a44bb9fbbc997be3ef7, @@ -3406,6 +3712,9 @@ PrefabInstance: value: 0 objectReference: {fileID: 0} m_RemovedComponents: [] + m_RemovedGameObjects: [] + m_AddedGameObjects: [] + m_AddedComponents: [] m_SourcePrefab: {fileID: 100100000, guid: 604a64bb74b56a44bb9fbbc997be3ef7, type: 3} --- !u!4 &412988840869617173 stripped Transform: diff --git a/Assets/Prefabs/Panels/MultiplayerPanel.prefab b/Assets/Prefabs/Panels/MultiplayerPanel.prefab index e480cc9740..a182d7ddae 100644 --- a/Assets/Prefabs/Panels/MultiplayerPanel.prefab +++ b/Assets/Prefabs/Panels/MultiplayerPanel.prefab @@ -25,6 +25,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 160214} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 4, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} @@ -38,7 +39,6 @@ Transform: - {fileID: 498528} - {fileID: 499980} m_Father: {fileID: 0} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!114 &114984344201106630 MonoBehaviour: @@ -104,10 +104,10 @@ MonoBehaviour: m_CanBeDetachedFromWand: 0 m_PopUpGazeDuration: 0.2 m_PromoBorders: [] + m_RoomNumberTextLobby: {fileID: 1974621732855197313} + m_RoomNumberTextRoomSettings: {fileID: 932498118536074667} m_LobbyElements: {fileID: 3644177191085397888} - m_CreateRoomElements: {fileID: 6171746570159008301} - m_RoomSettingsElements: {fileID: 8921512026995007485} - m_GeneralSettingsElements: {fileID: 8667602655244155954} + m_JoinedElements: {fileID: 6171746570159008301} references: version: 2 RefIds: [] @@ -147,13 +147,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 169614} + serializedVersion: 2 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: 499980} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!65 &6569620 BoxCollider: @@ -163,9 +163,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 169614} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1.94, y: 2.55, z: 0.02} m_Center: {x: 0.06, y: -0.06, z: 0} --- !u!1 &173754 @@ -192,13 +200,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 173754} + serializedVersion: 2 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: 415082} - m_RootOrder: 5 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!65 &6536704 BoxCollider: @@ -208,9 +216,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 173754} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 2.4, y: 3.3, z: 0.5} m_Center: {x: 0, y: 0, z: 0} --- !u!1 &176750 @@ -237,13 +253,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 176750} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0.05} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 499980} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &3378504 MeshFilter: @@ -276,6 +292,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 184986} + serializedVersion: 2 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} @@ -287,7 +304,6 @@ Transform: - {fileID: 4696385544466816} - {fileID: 4410849844686322} m_Father: {fileID: 415082} - m_RootOrder: 6 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1000013067707022 GameObject: @@ -314,13 +330,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000013067707022} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 2, y: 2.5, z: 2.55} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 415082} - m_RootOrder: 4 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000014291051348 MeshFilter: @@ -397,13 +413,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1007290988937762} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 0.65, y: 0.65, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4410849844686322} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33058761823212030 MeshFilter: @@ -482,15 +498,15 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1204492690482570} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: 0.907, y: -1.206, z: -0.01} + m_LocalPosition: {x: 0.907, y: -0.255, z: -0.01} m_LocalScale: {x: 0.29999998, y: 0.29999998, z: 0.29999998} m_ConstrainProportionsScale: 0 m_Children: - {fileID: 4153922631777284} - {fileID: 4402564000880478} m_Father: {fileID: 499980} - m_RootOrder: 4 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33802401890004714 MeshFilter: @@ -550,9 +566,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1204492690482570} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1.0000001, y: 1, z: 0.01} m_Center: {x: 0, y: 0, z: -0.01} --- !u!114 &114889102552369014 @@ -658,13 +682,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1744263076835604} + serializedVersion: 2 m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} m_LocalPosition: {x: -0, y: 0, z: 0} m_LocalScale: {x: 100, y: 100, z: 100} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4696385544466816} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33937921472497080 MeshFilter: @@ -673,7 +697,7 @@ MeshFilter: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1744263076835604} - m_Mesh: {fileID: 4300002, guid: 3911a5e0ccf20e0489d2e0c46768cc74, type: 3} + m_Mesh: {fileID: 4300002, guid: 90e92f1d849f0b448ac3e9a402f08bed, type: 3} --- !u!23 &23425245546884932 MeshRenderer: m_ObjectHideFlags: 0 @@ -741,13 +765,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1850095996710182} + serializedVersion: 2 m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} m_LocalPosition: {x: -0, y: 0, z: 0} m_LocalScale: {x: 100, y: 100, z: 100} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4696385544466816} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33901426776633756 MeshFilter: @@ -756,7 +780,7 @@ MeshFilter: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1850095996710182} - m_Mesh: {fileID: 4300000, guid: 3911a5e0ccf20e0489d2e0c46768cc74, type: 3} + m_Mesh: {fileID: 4300000, guid: 90e92f1d849f0b448ac3e9a402f08bed, type: 3} --- !u!23 &23979510237070564 MeshRenderer: m_ObjectHideFlags: 0 @@ -822,15 +846,15 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1852253978840482} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalPosition: {x: 0, y: 0.448, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: - {fileID: 4887086521611946} - {fileID: 4148024965627128} m_Father: {fileID: 499980} - m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1889327720883680 GameObject: @@ -857,13 +881,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1889327720883680} + serializedVersion: 2 m_LocalRotation: {x: 0.7071068, y: -0, z: -0, w: 0.7071068} m_LocalPosition: {x: 0, y: 0, z: 0.025} m_LocalScale: {x: 0.79999995, y: 0.02, z: 0.8} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4410849844686322} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 90, y: 0, z: 0} --- !u!33 &33054958406634006 MeshFilter: @@ -915,7 +939,7 @@ MeshRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_AdditionalVertexStreams: {fileID: 0} ---- !u!1 &3644177191085397888 +--- !u!1 &1697630693985053451 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -923,142 +947,49 @@ GameObject: m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - - component: {fileID: 2965947989537662772} + - component: {fileID: 1141409664942745479} + - component: {fileID: 1760323139199521700} + - component: {fileID: 4490950674500302730} + - component: {fileID: 6816865797365254487} + - component: {fileID: 7781273403086602364} m_Layer: 16 - m_Name: LobbyElements + m_Name: join Button m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!4 &2965947989537662772 +--- !u!4 &1141409664942745479 Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 3644177191085397888} + m_GameObject: {fileID: 1697630693985053451} + serializedVersion: 2 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_LocalPosition: {x: -0.329, y: 0.33, z: -0.03} + m_LocalScale: {x: 0.4, y: 0.4, z: 0.35} m_ConstrainProportionsScale: 0 m_Children: - - {fileID: 4742402761985048582} - m_Father: {fileID: 415082} - m_RootOrder: 0 - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!1 &6171746570159008301 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 438733611317437076} - m_Layer: 16 - m_Name: CreateRoomElements - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!4 &438733611317437076 -Transform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 6171746570159008301} - 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: 415082} - m_RootOrder: 1 - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!1 &8667602655244155954 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 1954481780995984980} - m_Layer: 16 - m_Name: GeneralSettingsElements - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!4 &1954481780995984980 -Transform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 8667602655244155954} - 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: 415082} - m_RootOrder: 3 - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!1 &8790421672380404409 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 4742402761985048582} - - component: {fileID: 2186148485191086047} - - component: {fileID: 3184411263972264924} - - component: {fileID: 1674489288884736589} - - component: {fileID: 5392421505523717988} - m_Layer: 16 - m_Name: PanelButton_Large_Help - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!4 &4742402761985048582 -Transform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 8790421672380404409} - m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: 0, y: 0.5700002, z: -0.03} - m_LocalScale: {x: 0.35, y: 0.35, z: 0.35} - m_ConstrainProportionsScale: 0 - m_Children: [] + - {fileID: 7015233605674447413} m_Father: {fileID: 2965947989537662772} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!33 &2186148485191086047 +--- !u!33 &1760323139199521700 MeshFilter: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 8790421672380404409} - m_Mesh: {fileID: 4300000, guid: 5501f437160666942ae970f3648fbeb8, type: 3} ---- !u!23 &3184411263972264924 + m_GameObject: {fileID: 1697630693985053451} + m_Mesh: {fileID: 4300000, guid: 70d5ab47bcc3aa0439e20bd40350c05a, type: 3} +--- !u!23 &4490950674500302730 MeshRenderer: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 8790421672380404409} + m_GameObject: {fileID: 1697630693985053451} m_Enabled: 1 m_CastShadows: 0 m_ReceiveShadows: 0 @@ -1094,26 +1025,34 @@ MeshRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_AdditionalVertexStreams: {fileID: 0} ---- !u!65 &1674489288884736589 +--- !u!65 &6816865797365254487 BoxCollider: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 8790421672380404409} + m_GameObject: {fileID: 1697630693985053451} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 - m_Size: {x: 1, y: 1, z: 0.1} + serializedVersion: 3 + m_Size: {x: 1.4, y: 1, z: 0.1} m_Center: {x: -0.000000074505806, y: 0.000000022351742, z: 0} ---- !u!114 &5392421505523717988 +--- !u!114 &7781273403086602364 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 8790421672380404409} + m_GameObject: {fileID: 1697630693985053451} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: b78a8d7209bbdc546979b549a875d550, type: 3} @@ -1143,7 +1082,7 @@ MonoBehaviour: m_LocalVariables: [] m_DescriptionActivateSpeed: 12 m_DescriptionZScale: 1 - m_ButtonTexture: {fileID: 0} + m_ButtonTexture: {fileID: 2800000, guid: cdc7d38d9e0b30443961d1cc27195586, type: 3} m_AtlasTexture: 1 m_ToggleButton: 0 m_LongPressReleaseButton: 0 @@ -1153,7 +1092,7 @@ MonoBehaviour: m_HoverScale: 1.1 m_HoverBoxColliderGrow: 0.2 m_AddOverlay: 0 - m_Command: 1002 + m_Command: 1004 m_CommandParam: 1 m_CommandParam2: -1 m_RequiresPopup: 0 @@ -1186,7 +1125,7 @@ MonoBehaviour: references: version: 2 RefIds: [] ---- !u!1 &8921512026995007485 +--- !u!1 &2571361884171689698 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -1194,34 +1133,1538 @@ GameObject: m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - - component: {fileID: 3405719730669880946} + - component: {fileID: 6876281661055682962} + - component: {fileID: 5708211407015971906} + - component: {fileID: 3923005959821366055} m_Layer: 16 - m_Name: RoomSettingsElements + m_Name: Label m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!4 &3405719730669880946 -Transform: +--- !u!224 &6876281661055682962 +RectTransform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 8921512026995007485} + m_GameObject: {fileID: 2571361884171689698} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: -0.057142857} + m_LocalScale: {x: 2.5, y: 2.5, z: 2.8571427} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 7383649761766971007} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: -0.56, y: 0.8399991} + m_SizeDelta: {x: 1.104868, y: 1.4460607} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!23 &5708211407015971906 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2571361884171689698} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, 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: 0 + 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!114 &3923005959821366055 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2571361884171689698} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9541d86e2fd84c1d9990edf0852d74ab, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_text: Edit + m_isRightToLeft: 0 + m_fontAsset: {fileID: 11400000, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + m_sharedMaterial: {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + m_fontSharedMaterials: [] + m_fontMaterial: {fileID: 0} + m_fontMaterials: [] + m_fontColor32: + serializedVersion: 2 + rgba: 4294967295 + m_fontColor: {r: 1, g: 1, b: 1, a: 1} + m_enableVertexGradient: 0 + m_colorMode: 3 + m_fontColorGradient: + topLeft: {r: 1, g: 1, b: 1, a: 1} + topRight: {r: 1, g: 1, b: 1, a: 1} + bottomLeft: {r: 1, g: 1, b: 1, a: 1} + bottomRight: {r: 1, g: 1, b: 1, a: 1} + m_fontColorGradientPreset: {fileID: 0} + m_spriteAsset: {fileID: 0} + m_tintAllSprites: 0 + m_StyleSheet: {fileID: 0} + m_TextStyleHashCode: -1183493901 + m_overrideHtmlColors: 0 + m_faceColor: + serializedVersion: 2 + rgba: 4294967295 + m_fontSize: 1.4 + m_fontSizeBase: 1.4 + m_fontWeight: 400 + m_enableAutoSizing: 0 + m_fontSizeMin: 18 + m_fontSizeMax: 72 + m_fontStyle: 0 + m_HorizontalAlignment: 2 + m_VerticalAlignment: 512 + m_textAlignment: 65535 + m_characterSpacing: 0 + m_wordSpacing: 0 + m_lineSpacing: -30 + m_lineSpacingMax: 0 + m_paragraphSpacing: 0 + m_charWidthMaxAdj: 0 + m_enableWordWrapping: 1 + m_wordWrappingRatios: 0.4 + m_overflowMode: 0 + m_linkedTextComponent: {fileID: 0} + parentLinkedComponent: {fileID: 0} + m_enableKerning: 1 + m_enableExtraPadding: 0 + checkPaddingRequired: 0 + m_isRichText: 1 + m_parseCtrlCharacters: 1 + m_isOrthographic: 0 + m_isCullingEnabled: 0 + m_horizontalMapping: 0 + m_verticalMapping: 0 + m_uvLineOffset: 0 + m_geometrySortingOrder: 0 + m_IsTextObjectScaleStatic: 0 + m_VertexBufferAutoSizeReduction: 0 + m_useMaxVisibleDescender: 1 + m_pageToDisplay: 1 + m_margin: {x: 0.48827845, y: 0.8604479, z: 0.054796636, w: 0.19184232} + m_isUsingLegacyAnimationComponent: 0 + m_isVolumetricText: 0 + _SortingLayer: 0 + _SortingLayerID: 0 + _SortingOrder: 0 + m_hasFontAssetChanged: 0 + m_renderer: {fileID: 5708211407015971906} + m_maskType: 0 +--- !u!1 &2994060372226562491 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3068395288206314123} + - component: {fileID: 3015414681375869025} + - component: {fileID: 2890656081255549429} + - component: {fileID: 942730275356831299} + m_Layer: 16 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &3068395288206314123 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2994060372226562491} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: -0.05} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 438733611317437076} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0.104, y: 0.625} + m_SizeDelta: {x: 1.104868, y: 1.4460607} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!23 &3015414681375869025 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2994060372226562491} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, 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: 0 + 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!114 &2890656081255549429 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2994060372226562491} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9541d86e2fd84c1d9990edf0852d74ab, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_text: 'You are in! + + Share the room number with your sketch collaborators + to join ' + m_isRightToLeft: 0 + m_fontAsset: {fileID: 11400000, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + m_sharedMaterial: {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + m_fontSharedMaterials: [] + m_fontMaterial: {fileID: 0} + m_fontMaterials: [] + m_fontColor32: + serializedVersion: 2 + rgba: 4294967295 + m_fontColor: {r: 1, g: 1, b: 1, a: 1} + m_enableVertexGradient: 0 + m_colorMode: 3 + m_fontColorGradient: + topLeft: {r: 1, g: 1, b: 1, a: 1} + topRight: {r: 1, g: 1, b: 1, a: 1} + bottomLeft: {r: 1, g: 1, b: 1, a: 1} + bottomRight: {r: 1, g: 1, b: 1, a: 1} + m_fontColorGradientPreset: {fileID: 0} + m_spriteAsset: {fileID: 0} + m_tintAllSprites: 0 + m_StyleSheet: {fileID: 0} + m_TextStyleHashCode: -1183493901 + m_overrideHtmlColors: 0 + m_faceColor: + serializedVersion: 2 + rgba: 4294967295 + m_fontSize: 1.4 + m_fontSizeBase: 1.4 + m_fontWeight: 400 + m_enableAutoSizing: 0 + m_fontSizeMin: 18 + m_fontSizeMax: 72 + m_fontStyle: 0 + m_HorizontalAlignment: 2 + m_VerticalAlignment: 512 + m_textAlignment: 65535 + m_characterSpacing: 0 + m_wordSpacing: 0 + m_lineSpacing: -30 + m_lineSpacingMax: 0 + m_paragraphSpacing: 0 + m_charWidthMaxAdj: 0 + m_enableWordWrapping: 1 + m_wordWrappingRatios: 0.4 + m_overflowMode: 0 + m_linkedTextComponent: {fileID: 0} + parentLinkedComponent: {fileID: 0} + m_enableKerning: 1 + m_enableExtraPadding: 0 + checkPaddingRequired: 0 + m_isRichText: 1 + m_parseCtrlCharacters: 1 + m_isOrthographic: 0 + m_isCullingEnabled: 0 + m_horizontalMapping: 0 + m_verticalMapping: 0 + m_uvLineOffset: 0 + m_geometrySortingOrder: 0 + m_IsTextObjectScaleStatic: 0 + m_VertexBufferAutoSizeReduction: 0 + m_useMaxVisibleDescender: 1 + m_pageToDisplay: 1 + m_margin: {x: -0.31141979, y: 0.6332202, z: -0.094255626, w: -0.027681828} + m_isUsingLegacyAnimationComponent: 0 + m_isVolumetricText: 0 + _SortingLayer: 0 + _SortingLayerID: 0 + _SortingOrder: 0 + m_hasFontAssetChanged: 0 + m_renderer: {fileID: 3015414681375869025} + m_maskType: 0 +--- !u!114 &942730275356831299 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2994060372226562491} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 69beb381e244f92449b8c4cf954630e9, type: 3} + m_Name: + m_EditorClassIdentifier: + m_TrackedObjects: [] + references: + version: 2 + RefIds: [] +--- !u!1 &3134540981707160973 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 7015233605674447413} + - component: {fileID: 6931700069523288738} + - component: {fileID: 8426405893577217330} + m_Layer: 16 + m_Name: Label + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &7015233605674447413 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3134540981707160973} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: -0.057142857} + m_LocalScale: {x: 2.5, y: 2.5, z: 2.8571427} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1141409664942745479} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: -0.56, y: 0.8399991} + m_SizeDelta: {x: 1.104868, y: 1.4460607} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!23 &6931700069523288738 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3134540981707160973} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, 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: 0 + 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!114 &8426405893577217330 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3134540981707160973} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9541d86e2fd84c1d9990edf0852d74ab, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_text: Join + m_isRightToLeft: 0 + m_fontAsset: {fileID: 11400000, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + m_sharedMaterial: {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + m_fontSharedMaterials: [] + m_fontMaterial: {fileID: 0} + m_fontMaterials: [] + m_fontColor32: + serializedVersion: 2 + rgba: 4294967295 + m_fontColor: {r: 1, g: 1, b: 1, a: 1} + m_enableVertexGradient: 0 + m_colorMode: 3 + m_fontColorGradient: + topLeft: {r: 1, g: 1, b: 1, a: 1} + topRight: {r: 1, g: 1, b: 1, a: 1} + bottomLeft: {r: 1, g: 1, b: 1, a: 1} + bottomRight: {r: 1, g: 1, b: 1, a: 1} + m_fontColorGradientPreset: {fileID: 0} + m_spriteAsset: {fileID: 0} + m_tintAllSprites: 0 + m_StyleSheet: {fileID: 0} + m_TextStyleHashCode: -1183493901 + m_overrideHtmlColors: 0 + m_faceColor: + serializedVersion: 2 + rgba: 4294967295 + m_fontSize: 1.4 + m_fontSizeBase: 1.4 + m_fontWeight: 400 + m_enableAutoSizing: 0 + m_fontSizeMin: 18 + m_fontSizeMax: 72 + m_fontStyle: 0 + m_HorizontalAlignment: 2 + m_VerticalAlignment: 512 + m_textAlignment: 65535 + m_characterSpacing: 0 + m_wordSpacing: 0 + m_lineSpacing: -30 + m_lineSpacingMax: 0 + m_paragraphSpacing: 0 + m_charWidthMaxAdj: 0 + m_enableWordWrapping: 1 + m_wordWrappingRatios: 0.4 + m_overflowMode: 0 + m_linkedTextComponent: {fileID: 0} + parentLinkedComponent: {fileID: 0} + m_enableKerning: 1 + m_enableExtraPadding: 0 + checkPaddingRequired: 0 + m_isRichText: 1 + m_parseCtrlCharacters: 1 + m_isOrthographic: 0 + m_isCullingEnabled: 0 + m_horizontalMapping: 0 + m_verticalMapping: 0 + m_uvLineOffset: 0 + m_geometrySortingOrder: 0 + m_IsTextObjectScaleStatic: 0 + m_VertexBufferAutoSizeReduction: 0 + m_useMaxVisibleDescender: 1 + m_pageToDisplay: 1 + m_margin: {x: 0.48827845, y: 0.8604479, z: 0.054796636, w: 0.19184232} + m_isUsingLegacyAnimationComponent: 0 + m_isVolumetricText: 0 + _SortingLayer: 0 + _SortingLayerID: 0 + _SortingOrder: 0 + m_hasFontAssetChanged: 0 + m_renderer: {fileID: 6931700069523288738} + m_maskType: 0 +--- !u!1 &3644177191085397888 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2965947989537662772} + m_Layer: 16 + m_Name: LobbyElements + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &2965947989537662772 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3644177191085397888} + serializedVersion: 2 + 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: 1141409664942745479} + - {fileID: 7383649761766971007} + - {fileID: 1616599776894361023} + - {fileID: 7584752043559634152} + m_Father: {fileID: 415082} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &4076170750158864688 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 7584752043559634152} + - component: {fileID: 8238648932876523510} + - component: {fileID: 1974621732855197313} + m_Layer: 16 + m_Name: RoomNumber + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &7584752043559634152 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4076170750158864688} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: -0.05} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 2965947989537662772} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0.586, y: 0.145} + m_SizeDelta: {x: 1.104868, y: 1.4460607} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!23 &8238648932876523510 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4076170750158864688} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, 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: 0 + 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!114 &1974621732855197313 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4076170750158864688} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9541d86e2fd84c1d9990edf0852d74ab, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_text: 202020 + m_isRightToLeft: 0 + m_fontAsset: {fileID: 11400000, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + m_sharedMaterial: {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + m_fontSharedMaterials: [] + m_fontMaterial: {fileID: 0} + m_fontMaterials: [] + m_fontColor32: + serializedVersion: 2 + rgba: 4294967295 + m_fontColor: {r: 1, g: 1, b: 1, a: 1} + m_enableVertexGradient: 0 + m_colorMode: 3 + m_fontColorGradient: + topLeft: {r: 1, g: 1, b: 1, a: 1} + topRight: {r: 1, g: 1, b: 1, a: 1} + bottomLeft: {r: 1, g: 1, b: 1, a: 1} + bottomRight: {r: 1, g: 1, b: 1, a: 1} + m_fontColorGradientPreset: {fileID: 0} + m_spriteAsset: {fileID: 0} + m_tintAllSprites: 0 + m_StyleSheet: {fileID: 0} + m_TextStyleHashCode: -1183493901 + m_overrideHtmlColors: 0 + m_faceColor: + serializedVersion: 2 + rgba: 4294967295 + m_fontSize: 1.7 + m_fontSizeBase: 1.7 + m_fontWeight: 400 + m_enableAutoSizing: 0 + m_fontSizeMin: 18 + m_fontSizeMax: 72 + m_fontStyle: 0 + m_HorizontalAlignment: 2 + m_VerticalAlignment: 512 + m_textAlignment: 65535 + m_characterSpacing: 0 + m_wordSpacing: 0 + m_lineSpacing: -30 + m_lineSpacingMax: 0 + m_paragraphSpacing: 0 + m_charWidthMaxAdj: 0 + m_enableWordWrapping: 1 + m_wordWrappingRatios: 0.4 + m_overflowMode: 0 + m_linkedTextComponent: {fileID: 0} + parentLinkedComponent: {fileID: 0} + m_enableKerning: 1 + m_enableExtraPadding: 0 + checkPaddingRequired: 0 + m_isRichText: 1 + m_parseCtrlCharacters: 1 + m_isOrthographic: 0 + m_isCullingEnabled: 0 + m_horizontalMapping: 0 + m_verticalMapping: 0 + m_uvLineOffset: 0 + m_geometrySortingOrder: 0 + m_IsTextObjectScaleStatic: 0 + m_VertexBufferAutoSizeReduction: 0 + m_useMaxVisibleDescender: 1 + m_pageToDisplay: 1 + m_margin: {x: -0.31141979, y: -0.12023544, z: 0.49373662, w: 1.1764753} + m_isUsingLegacyAnimationComponent: 0 + m_isVolumetricText: 0 + _SortingLayer: 0 + _SortingLayerID: 0 + _SortingOrder: 0 + m_hasFontAssetChanged: 0 + m_renderer: {fileID: 8238648932876523510} + m_maskType: 0 +--- !u!1 &4649946342403727185 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3577413846294365787} + - component: {fileID: 1001195632747271077} + - component: {fileID: 404572183770810615} + m_Layer: 16 + m_Name: Label + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &3577413846294365787 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4649946342403727185} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: -0.05} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 438733611317437076} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0.15199947} + m_SizeDelta: {x: 1.104868, y: 1.4460607} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!23 &1001195632747271077 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4649946342403727185} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, 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: 0 + 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!114 &404572183770810615 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4649946342403727185} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9541d86e2fd84c1d9990edf0852d74ab, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_text: ' Room :' + m_isRightToLeft: 0 + m_fontAsset: {fileID: 11400000, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + m_sharedMaterial: {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + m_fontSharedMaterials: [] + m_fontMaterial: {fileID: 0} + m_fontMaterials: [] + m_fontColor32: + serializedVersion: 2 + rgba: 4294967295 + m_fontColor: {r: 1, g: 1, b: 1, a: 1} + m_enableVertexGradient: 0 + m_colorMode: 3 + m_fontColorGradient: + topLeft: {r: 1, g: 1, b: 1, a: 1} + topRight: {r: 1, g: 1, b: 1, a: 1} + bottomLeft: {r: 1, g: 1, b: 1, a: 1} + bottomRight: {r: 1, g: 1, b: 1, a: 1} + m_fontColorGradientPreset: {fileID: 0} + m_spriteAsset: {fileID: 0} + m_tintAllSprites: 0 + m_StyleSheet: {fileID: 0} + m_TextStyleHashCode: -1183493901 + m_overrideHtmlColors: 0 + m_faceColor: + serializedVersion: 2 + rgba: 4294967295 + m_fontSize: 1.7 + m_fontSizeBase: 1.7 + m_fontWeight: 400 + m_enableAutoSizing: 0 + m_fontSizeMin: 18 + m_fontSizeMax: 72 + m_fontStyle: 0 + m_HorizontalAlignment: 2 + m_VerticalAlignment: 512 + m_textAlignment: 65535 + m_characterSpacing: 0 + m_wordSpacing: 0 + m_lineSpacing: -30 + m_lineSpacingMax: 0 + m_paragraphSpacing: 0 + m_charWidthMaxAdj: 0 + m_enableWordWrapping: 1 + m_wordWrappingRatios: 0.4 + m_overflowMode: 0 + m_linkedTextComponent: {fileID: 0} + parentLinkedComponent: {fileID: 0} + m_enableKerning: 1 + m_enableExtraPadding: 0 + checkPaddingRequired: 0 + m_isRichText: 1 + m_parseCtrlCharacters: 1 + m_isOrthographic: 0 + m_isCullingEnabled: 0 + m_horizontalMapping: 0 + m_verticalMapping: 0 + m_uvLineOffset: 0 + m_geometrySortingOrder: 0 + m_IsTextObjectScaleStatic: 0 + m_VertexBufferAutoSizeReduction: 0 + m_useMaxVisibleDescender: 1 + m_pageToDisplay: 1 + m_margin: {x: -0.31141979, y: -0.12023544, z: 0.49373662, w: 1.1764753} + m_isUsingLegacyAnimationComponent: 0 + m_isVolumetricText: 0 + _SortingLayer: 0 + _SortingLayerID: 0 + _SortingOrder: 0 + m_hasFontAssetChanged: 0 + m_renderer: {fileID: 1001195632747271077} + m_maskType: 0 +--- !u!1 &5921902206488690915 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 7383649761766971007} + - component: {fileID: 2926107150197885019} + - component: {fileID: 8582249604316496249} + - component: {fileID: 3106886073238925572} + - component: {fileID: 1443552876586975744} + m_Layer: 16 + m_Name: Edit Button + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &7383649761766971007 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5921902206488690915} + serializedVersion: 2 + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0.337, y: 0.33, z: -0.03} + m_LocalScale: {x: 0.4, y: 0.4, z: 0.35} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 6876281661055682962} + m_Father: {fileID: 2965947989537662772} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &2926107150197885019 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5921902206488690915} + m_Mesh: {fileID: 4300000, guid: 70d5ab47bcc3aa0439e20bd40350c05a, type: 3} +--- !u!23 &8582249604316496249 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5921902206488690915} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 40d29de2bdc11f04dbfa25059165916e, 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: 0 + 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!65 &3106886073238925572 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5921902206488690915} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1.4, y: 1, z: 0.1} + m_Center: {x: -0.000000074505806, y: 0.000000022351742, z: 0} +--- !u!114 &1443552876586975744 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5921902206488690915} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: b78a8d7209bbdc546979b549a875d550, type: 3} + m_Name: + m_EditorClassIdentifier: + m_DescriptionType: 0 + m_DescriptionYOffset: 0 + m_DescriptionText: + m_LocalizedDescription: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_DescriptionTextExtra: + m_LocalizedDescriptionExtra: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_DescriptionActivateSpeed: 12 + m_DescriptionZScale: 1 + m_ButtonTexture: {fileID: 2800000, guid: cdc7d38d9e0b30443961d1cc27195586, type: 3} + m_AtlasTexture: 1 + m_ToggleButton: 0 + m_LongPressReleaseButton: 0 + m_ButtonHasPressedAudio: 1 + m_ZAdjustHover: -0.02 + m_ZAdjustClick: 0.02 + m_HoverScale: 1.1 + m_HoverBoxColliderGrow: 0.2 + m_AddOverlay: 0 + m_Command: 1004 + m_CommandParam: 1 + m_CommandParam2: -1 + m_RequiresPopup: 0 + m_CenterPopupOnButton: 0 + m_PopupOffset: {x: 0, y: 0, z: 0} + m_PopupText: + m_LocalizedPopup: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_ToggleOnDescription: + m_LocalizedToggleOnDescription: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_ToggleOnTexture: {fileID: 0} + m_AllowUnavailable: 0 + m_LinkedUIObject: {fileID: 0} + m_CommandIgnored: 0 + references: + version: 2 + RefIds: [] +--- !u!1 &6171746570159008301 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 438733611317437076} + m_Layer: 16 + m_Name: JoinedElements + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 0 +--- !u!4 &438733611317437076 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6171746570159008301} + serializedVersion: 2 + 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: 3068395288206314123} + - {fileID: 3577413846294365787} + - {fileID: 4072971569122874247} + m_Father: {fileID: 415082} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &6437768756802778087 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4072971569122874247} + - component: {fileID: 6918572163598264854} + - component: {fileID: 932498118536074667} + m_Layer: 16 + m_Name: RoomNumber + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &4072971569122874247 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6437768756802778087} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: -0.05} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 438733611317437076} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0.586, y: 0.1449995} + m_SizeDelta: {x: 1.104868, y: 1.4460607} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!23 &6918572163598264854 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6437768756802778087} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, 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: 0 + 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!114 &932498118536074667 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6437768756802778087} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9541d86e2fd84c1d9990edf0852d74ab, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_text: 202020 + m_isRightToLeft: 0 + m_fontAsset: {fileID: 11400000, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + m_sharedMaterial: {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + m_fontSharedMaterials: [] + m_fontMaterial: {fileID: 0} + m_fontMaterials: [] + m_fontColor32: + serializedVersion: 2 + rgba: 4294967295 + m_fontColor: {r: 1, g: 1, b: 1, a: 1} + m_enableVertexGradient: 0 + m_colorMode: 3 + m_fontColorGradient: + topLeft: {r: 1, g: 1, b: 1, a: 1} + topRight: {r: 1, g: 1, b: 1, a: 1} + bottomLeft: {r: 1, g: 1, b: 1, a: 1} + bottomRight: {r: 1, g: 1, b: 1, a: 1} + m_fontColorGradientPreset: {fileID: 0} + m_spriteAsset: {fileID: 0} + m_tintAllSprites: 0 + m_StyleSheet: {fileID: 0} + m_TextStyleHashCode: -1183493901 + m_overrideHtmlColors: 0 + m_faceColor: + serializedVersion: 2 + rgba: 4294967295 + m_fontSize: 1.7 + m_fontSizeBase: 1.7 + m_fontWeight: 400 + m_enableAutoSizing: 0 + m_fontSizeMin: 18 + m_fontSizeMax: 72 + m_fontStyle: 0 + m_HorizontalAlignment: 2 + m_VerticalAlignment: 512 + m_textAlignment: 65535 + m_characterSpacing: 0 + m_wordSpacing: 0 + m_lineSpacing: -30 + m_lineSpacingMax: 0 + m_paragraphSpacing: 0 + m_charWidthMaxAdj: 0 + m_enableWordWrapping: 1 + m_wordWrappingRatios: 0.4 + m_overflowMode: 0 + m_linkedTextComponent: {fileID: 0} + parentLinkedComponent: {fileID: 0} + m_enableKerning: 1 + m_enableExtraPadding: 0 + checkPaddingRequired: 0 + m_isRichText: 1 + m_parseCtrlCharacters: 1 + m_isOrthographic: 0 + m_isCullingEnabled: 0 + m_horizontalMapping: 0 + m_verticalMapping: 0 + m_uvLineOffset: 0 + m_geometrySortingOrder: 0 + m_IsTextObjectScaleStatic: 0 + m_VertexBufferAutoSizeReduction: 0 + m_useMaxVisibleDescender: 1 + m_pageToDisplay: 1 + m_margin: {x: -0.31141979, y: -0.12023544, z: 0.49373662, w: 1.1764753} + m_isUsingLegacyAnimationComponent: 0 + m_isVolumetricText: 0 + _SortingLayer: 0 + _SortingLayerID: 0 + _SortingOrder: 0 + m_hasFontAssetChanged: 0 + m_renderer: {fileID: 6918572163598264854} + m_maskType: 0 +--- !u!1 &8016613551395990211 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1616599776894361023} + - component: {fileID: 9202472902960767262} + - component: {fileID: 6387985900290305946} + m_Layer: 16 + m_Name: Label + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1616599776894361023 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8016613551395990211} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: -0.05} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 2965947989537662772} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0.152} + m_SizeDelta: {x: 1.104868, y: 1.4460607} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!23 &9202472902960767262 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8016613551395990211} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, 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: 0 + 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!114 &6387985900290305946 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8016613551395990211} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9541d86e2fd84c1d9990edf0852d74ab, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_text: ' Room :' + m_isRightToLeft: 0 + m_fontAsset: {fileID: 11400000, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + m_sharedMaterial: {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + m_fontSharedMaterials: [] + m_fontMaterial: {fileID: 0} + m_fontMaterials: [] + m_fontColor32: + serializedVersion: 2 + rgba: 4294967295 + m_fontColor: {r: 1, g: 1, b: 1, a: 1} + m_enableVertexGradient: 0 + m_colorMode: 3 + m_fontColorGradient: + topLeft: {r: 1, g: 1, b: 1, a: 1} + topRight: {r: 1, g: 1, b: 1, a: 1} + bottomLeft: {r: 1, g: 1, b: 1, a: 1} + bottomRight: {r: 1, g: 1, b: 1, a: 1} + m_fontColorGradientPreset: {fileID: 0} + m_spriteAsset: {fileID: 0} + m_tintAllSprites: 0 + m_StyleSheet: {fileID: 0} + m_TextStyleHashCode: -1183493901 + m_overrideHtmlColors: 0 + m_faceColor: + serializedVersion: 2 + rgba: 4294967295 + m_fontSize: 1.7 + m_fontSizeBase: 1.7 + m_fontWeight: 400 + m_enableAutoSizing: 0 + m_fontSizeMin: 18 + m_fontSizeMax: 72 + m_fontStyle: 0 + m_HorizontalAlignment: 2 + m_VerticalAlignment: 512 + m_textAlignment: 65535 + m_characterSpacing: 0 + m_wordSpacing: 0 + m_lineSpacing: -30 + m_lineSpacingMax: 0 + m_paragraphSpacing: 0 + m_charWidthMaxAdj: 0 + m_enableWordWrapping: 1 + m_wordWrappingRatios: 0.4 + m_overflowMode: 0 + m_linkedTextComponent: {fileID: 0} + parentLinkedComponent: {fileID: 0} + m_enableKerning: 1 + m_enableExtraPadding: 0 + checkPaddingRequired: 0 + m_isRichText: 1 + m_parseCtrlCharacters: 1 + m_isOrthographic: 0 + m_isCullingEnabled: 0 + m_horizontalMapping: 0 + m_verticalMapping: 0 + m_uvLineOffset: 0 + m_geometrySortingOrder: 0 + m_IsTextObjectScaleStatic: 0 + m_VertexBufferAutoSizeReduction: 0 + m_useMaxVisibleDescender: 1 + m_pageToDisplay: 1 + m_margin: {x: -0.31141979, y: -0.12023544, z: 0.49373662, w: 1.1764753} + m_isUsingLegacyAnimationComponent: 0 + m_isVolumetricText: 0 + _SortingLayer: 0 + _SortingLayerID: 0 + _SortingOrder: 0 + m_hasFontAssetChanged: 0 + m_renderer: {fileID: 9202472902960767262} + m_maskType: 0 +--- !u!1 &8667602655244155954 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1954481780995984980} + m_Layer: 16 + m_Name: GeneralSettingsElements + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 0 +--- !u!4 &1954481780995984980 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8667602655244155954} + serializedVersion: 2 + 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: 415082} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &8921512026995007485 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3405719730669880946} + m_Layer: 16 + m_Name: RoomSettingsElements + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 0 +--- !u!4 &3405719730669880946 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8921512026995007485} + serializedVersion: 2 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: 415082} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1001 &7380239823636675985 PrefabInstance: m_ObjectHideFlags: 0 serializedVersion: 2 m_Modification: + serializedVersion: 3 m_TransformParent: {fileID: 499980} m_Modifications: - target: {fileID: 2206467382688623040, guid: 2538d4a3972b37c4c9e8649481dd9a2b, @@ -1310,6 +2753,9 @@ PrefabInstance: value: 176097607781543936 objectReference: {fileID: 0} m_RemovedComponents: [] + m_RemovedGameObjects: [] + m_AddedGameObjects: [] + m_AddedComponents: [] m_SourcePrefab: {fileID: 100100000, guid: 2538d4a3972b37c4c9e8649481dd9a2b, type: 3} --- !u!4 &6533003833265780531 stripped Transform: diff --git a/Assets/Scripts/GUI/MultiplayerPanel.cs b/Assets/Scripts/GUI/MultiplayerPanel.cs index d4ddd329db..f1a45e3a33 100644 --- a/Assets/Scripts/GUI/MultiplayerPanel.cs +++ b/Assets/Scripts/GUI/MultiplayerPanel.cs @@ -14,25 +14,56 @@ using System; using OpenBrush.Multiplayer; +using TMPro; using UnityEngine; namespace TiltBrush { public class MultiplayerPanel : BasePanel { + [SerializeField] private TextMeshPro m_RoomNumberTextLobby; + [SerializeField] private TextMeshPro m_RoomNumberTextRoomSettings; + + private RoomCreateData data = new RoomCreateData + { + roomName = GenerateRandomRoomName(), + @private = false, + maxPlayers = 4, + voiceDisabled = false + }; + + private static string GenerateRandomRoomName() + { + System.Random random = new System.Random(); + return random.Next(100000, 999999).ToString(); + } + + private void UpdateRoomNumberDisplay() + { + if (m_RoomNumberTextLobby) + { + m_RoomNumberTextLobby.text = data.roomName; + } + if (m_RoomNumberTextRoomSettings) + { + m_RoomNumberTextRoomSettings.text = data.roomName; + } + } + public enum Mode { Null, Lobby, - Create, - RoomSettings, - GeneralSettings + Joined, + //Create, + //RoomSettings, + //GeneralSettings } [SerializeField] private GameObject m_LobbyElements; - [SerializeField] private GameObject m_CreateRoomElements; - [SerializeField] private GameObject m_RoomSettingsElements; - [SerializeField] private GameObject m_GeneralSettingsElements; + [SerializeField] private GameObject m_JoinedElements; + //[SerializeField] private GameObject m_RoomSettingsElements; + //[SerializeField] private GameObject m_GeneralSettingsElements; private Mode m_CurrentMode; @@ -41,6 +72,7 @@ public override void InitPanel() base.InitPanel(); InitMultiplayer(); + UpdateRoomNumberDisplay(); } public async void InitMultiplayer() @@ -48,13 +80,43 @@ public async void InitMultiplayer() bool success = await MultiplayerManager.m_Instance.Init(); } + private async void JoinRoom() + { + if (MultiplayerManager.m_Instance != null) + { + + bool success = await MultiplayerManager.m_Instance.Connect(data); + + if (success) + { + Debug.Log("Connected to room successfully."); + + // Additional UI updates or feedback + UpdateMode(Mode.Joined); + UpdateRoomNumberDisplay(); // Update room number display after joining + } + else + { + Debug.LogError("Failed to connect to room."); + // Provide user feedback with some UI element + } + + } + } + private void UpdateMode(Mode newMode) { m_CurrentMode = newMode; m_LobbyElements.SetActive(m_CurrentMode == Mode.Lobby); - m_CreateRoomElements.SetActive(m_CurrentMode == Mode.Create); - m_RoomSettingsElements.SetActive(m_CurrentMode == Mode.RoomSettings); - m_GeneralSettingsElements.SetActive(m_CurrentMode == Mode.GeneralSettings); + m_JoinedElements.SetActive(m_CurrentMode == Mode.Joined); + //m_RoomSettingsElements.SetActive(m_CurrentMode == Mode.RoomSettings); + //m_GeneralSettingsElements.SetActive(m_CurrentMode == Mode.GeneralSettings); + + // Update room number display if switching to a mode that shows it + if (m_CurrentMode == Mode.Lobby || m_CurrentMode == Mode.Joined) + { + UpdateRoomNumberDisplay(); + } } private void RefreshObjects() @@ -83,6 +145,10 @@ public void OnMultiplayerPanelButtonPressed(MultiplayerPanelButton button) break; } break; + case SketchControlsScript.GlobalCommands.MultiplayerJoinRoom: + JoinRoom(); + Debug.Log("Joining room"); + break; } } } diff --git a/Assets/Scripts/Multiplayer/MultiplayerManager.cs b/Assets/Scripts/Multiplayer/MultiplayerManager.cs index 5c59e71834..c1b44cea49 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerManager.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerManager.cs @@ -115,9 +115,9 @@ public async Task Init() return success; } - public async void Connect(RoomCreateData data) + public async Task Connect(RoomCreateData data) { - var result = await m_Manager.Connect(data); + return await m_Manager.Connect(data); } void Update() diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs index e0b2279950..f25d9a7901 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs @@ -60,7 +60,7 @@ public PhotonManager(MultiplayerManager manager) public async Task Init() { await Task.Yield(); - return true; + //return true; var result = await m_Runner.JoinSessionLobby(SessionLobby.Shared, customAppSettings: m_PhotonAppSettings); if (result.Ok) @@ -89,6 +89,15 @@ public async Task Connect(RoomCreateData roomCreateData) var result = await m_Runner.StartGame(args); + if (result.Ok) + { + ControllerConsoleScript.m_Instance.AddNewLine("Joined Room"); + } + else + { + ControllerConsoleScript.m_Instance.AddNewLine("Failed to join Room!"); + } + return result.Ok; } @@ -258,7 +267,12 @@ public void OnConnectedToServer(NetworkRunner runner) public void OnPlayerJoined(NetworkRunner runner, PlayerRef player) { - if(player == m_Runner.LocalPlayer) + Debug.Log($"OnPlayerJoined called. PlayerRef: {player.PlayerId}"); + + try + { + + if (player == m_Runner.LocalPlayer) { var playerPrefab = Resources.Load("Multiplayer/Photon/PhotonPlayerRig") as GameObject; var playerObj = m_Runner.Spawn(playerPrefab, inputAuthority: m_Runner.LocalPlayer); @@ -271,6 +285,11 @@ public void OnPlayerJoined(NetworkRunner runner, PlayerRef player) { m_PlayersSpawning.Add(player); } + } + catch (Exception ex) + { + Debug.LogError($"Exception in OnPlayerJoined: {ex.Message}"); + } } public void OnPlayerLeft(NetworkRunner runner, PlayerRef player) diff --git a/Assets/Scripts/SketchControlsScript.cs b/Assets/Scripts/SketchControlsScript.cs index f84c80b127..1ade1f0def 100644 --- a/Assets/Scripts/SketchControlsScript.cs +++ b/Assets/Scripts/SketchControlsScript.cs @@ -148,6 +148,7 @@ public enum GlobalCommands LanguagePopup = 1000, MultiplayerTogglePanel = 1001, MultiplayerPanelOptions = 1002, // iParam1: Popup options + MultiplayerJoinRoom = 1004, RenameSketch = 5200, OpenLayerOptionsPopup = 5201, diff --git a/Assets/XR/XRGeneralSettings.asset b/Assets/XR/XRGeneralSettings.asset index ae01ae95ad..f7b92131f1 100644 --- a/Assets/XR/XRGeneralSettings.asset +++ b/Assets/XR/XRGeneralSettings.asset @@ -110,7 +110,7 @@ MonoBehaviour: m_Name: Standalone Settings m_EditorClassIdentifier: m_LoaderManagerInstance: {fileID: 8705794667931902858} - m_InitManagerOnStart: 0 + m_InitManagerOnStart: 1 --- !u!114 &6946956650545175336 MonoBehaviour: m_ObjectHideFlags: 0 From cf83975f58faf4816a31d3c2bc7cba56ee69edd0 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Mon, 7 Oct 2024 13:38:09 +0100 Subject: [PATCH 014/174] Numeric keyboard popup for multiplayer room --- Assets/Prefabs/Panels/MultiplayerPanel.prefab | 9 ++++++--- Assets/Scripts/GUI/MultiplayerPanel.cs | 6 ++++++ Assets/Scripts/SketchControlsScript.cs | 9 +++++++++ 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/Assets/Prefabs/Panels/MultiplayerPanel.prefab b/Assets/Prefabs/Panels/MultiplayerPanel.prefab index a182d7ddae..769a11feba 100644 --- a/Assets/Prefabs/Panels/MultiplayerPanel.prefab +++ b/Assets/Prefabs/Panels/MultiplayerPanel.prefab @@ -58,7 +58,10 @@ MonoBehaviour: m_Border: {fileID: 23425245546884932} m_MeshCollider: {fileID: 6569620} m_ParticleBounds: {x: 0, y: 0, z: 0} - m_PanelPopUpMap: [] + m_PanelPopUpMap: + - m_PopUpPrefab: {fileID: 8644332587479430734, guid: 51ce29df58ccd0343a277023a12c50ff, + type: 3} + m_Command: 1005 m_PanelDescription: m_LocalizedPanelDescription: m_TableReference: @@ -2188,10 +2191,10 @@ MonoBehaviour: m_HoverScale: 1.1 m_HoverBoxColliderGrow: 0.2 m_AddOverlay: 0 - m_Command: 1004 + m_Command: 1005 m_CommandParam: 1 m_CommandParam2: -1 - m_RequiresPopup: 0 + m_RequiresPopup: 1 m_CenterPopupOnButton: 0 m_PopupOffset: {x: 0, y: 0, z: 0} m_PopupText: diff --git a/Assets/Scripts/GUI/MultiplayerPanel.cs b/Assets/Scripts/GUI/MultiplayerPanel.cs index f1a45e3a33..ce3151c813 100644 --- a/Assets/Scripts/GUI/MultiplayerPanel.cs +++ b/Assets/Scripts/GUI/MultiplayerPanel.cs @@ -24,6 +24,12 @@ public class MultiplayerPanel : BasePanel [SerializeField] private TextMeshPro m_RoomNumberTextLobby; [SerializeField] private TextMeshPro m_RoomNumberTextRoomSettings; + public void SetRoomName(string roomName) + { + data.roomName = roomName; + UpdateRoomNumberDisplay(); + } + private RoomCreateData data = new RoomCreateData { roomName = GenerateRandomRoomName(), diff --git a/Assets/Scripts/SketchControlsScript.cs b/Assets/Scripts/SketchControlsScript.cs index 1ade1f0def..58d3e39e9e 100644 --- a/Assets/Scripts/SketchControlsScript.cs +++ b/Assets/Scripts/SketchControlsScript.cs @@ -149,6 +149,7 @@ public enum GlobalCommands MultiplayerTogglePanel = 1001, MultiplayerPanelOptions = 1002, // iParam1: Popup options MultiplayerJoinRoom = 1004, + EditMultiplayerRoomName = 1005, RenameSketch = 5200, OpenLayerOptionsPopup = 5201, @@ -4510,6 +4511,14 @@ public void IssueGlobalCommand(GlobalCommands rEnum, int iParam1 = -1, DismissPopupOnCurrentGazeObject(false); break; } + case GlobalCommands.EditMultiplayerRoomName: + { + var panel = (MultiplayerPanel)m_PanelManager.GetActivePanelByType(BasePanel.PanelType.Multiplayer); + panel.SetRoomName(KeyboardPopUpWindow.m_LastInput); + DismissPopupOnCurrentGazeObject(false); + break; + } + case GlobalCommands.ShowWindowGUI: break; case GlobalCommands.Disco: From 16d3746a54aef553f5181e3f7dca199ee2a2493d Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Mon, 7 Oct 2024 13:39:07 +0100 Subject: [PATCH 015/174] Buttons were too close to panel --- Assets/Prefabs/Panels/MultiplayerPanel.prefab | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Assets/Prefabs/Panels/MultiplayerPanel.prefab b/Assets/Prefabs/Panels/MultiplayerPanel.prefab index 769a11feba..223f67640c 100644 --- a/Assets/Prefabs/Panels/MultiplayerPanel.prefab +++ b/Assets/Prefabs/Panels/MultiplayerPanel.prefab @@ -971,7 +971,7 @@ Transform: m_GameObject: {fileID: 1697630693985053451} serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: -0.329, y: 0.33, z: -0.03} + m_LocalPosition: {x: -0.329, y: 0.33, z: -0.042} m_LocalScale: {x: 0.4, y: 0.4, z: 0.35} m_ConstrainProportionsScale: 0 m_Children: @@ -2067,7 +2067,7 @@ Transform: m_GameObject: {fileID: 5921902206488690915} serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: 0.337, y: 0.33, z: -0.03} + m_LocalPosition: {x: 0.337, y: 0.33, z: -0.042} m_LocalScale: {x: 0.4, y: 0.4, z: 0.35} m_ConstrainProportionsScale: 0 m_Children: From 1637dc973b6675e2754d089906ec2cc3aa92d8ea Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Mon, 7 Oct 2024 13:40:16 +0100 Subject: [PATCH 016/174] Don't init XR manager on start --- Assets/XR/XRGeneralSettings.asset | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Assets/XR/XRGeneralSettings.asset b/Assets/XR/XRGeneralSettings.asset index 96c69f9a3b..42542c8510 100644 --- a/Assets/XR/XRGeneralSettings.asset +++ b/Assets/XR/XRGeneralSettings.asset @@ -110,7 +110,7 @@ MonoBehaviour: m_Name: Standalone Settings m_EditorClassIdentifier: m_LoaderManagerInstance: {fileID: 8705794667931902858} - m_InitManagerOnStart: 1 + m_InitManagerOnStart: 0 --- !u!114 &6946956650545175336 MonoBehaviour: m_ObjectHideFlags: 0 From ba14137e91d6aefb64f4196d3297dd54b4cb4796 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Mon, 7 Oct 2024 13:41:12 +0100 Subject: [PATCH 017/174] OpenXR should be the default in the editor --- Assets/XR/XRGeneralSettings.asset | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Assets/XR/XRGeneralSettings.asset b/Assets/XR/XRGeneralSettings.asset index 42542c8510..4c02ba5d2d 100644 --- a/Assets/XR/XRGeneralSettings.asset +++ b/Assets/XR/XRGeneralSettings.asset @@ -157,7 +157,7 @@ MonoBehaviour: m_AutomaticLoading: 0 m_AutomaticRunning: 0 m_Loaders: - - {fileID: 11400000, guid: df02db4506fe02b4892b2f1dab25abe6, type: 2} + - {fileID: 11400000, guid: 0aff50989b9ead845bdb50daaf977456, type: 2} --- !u!114 &8915350425150617969 MonoBehaviour: m_ObjectHideFlags: 0 From ada058c8059be13cad1fd54195933d852d12bcc4 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Mon, 7 Oct 2024 13:51:21 +0100 Subject: [PATCH 018/174] Admin panel label tweaks --- Assets/Prefabs/Panels/AdminPanel.prefab | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Assets/Prefabs/Panels/AdminPanel.prefab b/Assets/Prefabs/Panels/AdminPanel.prefab index 1e8c7aceed..410bd34c98 100644 --- a/Assets/Prefabs/Panels/AdminPanel.prefab +++ b/Assets/Prefabs/Panels/AdminPanel.prefab @@ -2242,9 +2242,9 @@ MonoBehaviour: m_DescriptionText: m_LocalizedDescription: m_TableReference: - m_TableCollectionName: + m_TableCollectionName: GUID:c84355079ab3f3e4f8f3812258805f86 m_TableEntryReference: - m_KeyId: 0 + m_KeyId: 5065626072358912 m_Key: m_FallbackState: 0 m_WaitForCompletion: 0 @@ -3224,7 +3224,7 @@ GameObject: - component: {fileID: 6605348980898369502} - component: {fileID: 8695917633644188848} m_Layer: 16 - m_Name: Button_SaveOptions (1) + m_Name: Button_News m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 @@ -3328,7 +3328,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 6c6859eec74651247968d56b594ac313, type: 3} m_Name: m_EditorClassIdentifier: - m_DescriptionType: 0 + m_DescriptionType: -1 m_DescriptionYOffset: 0 m_DescriptionText: ADMIN_PANEL_SAVE_SKETCH_BUTTON_DESCRIPTION m_LocalizedDescription: @@ -3352,7 +3352,7 @@ MonoBehaviour: m_LocalVariables: [] m_DescriptionActivateSpeed: 12 m_DescriptionZScale: 1 - m_ButtonTexture: {fileID: 0} + m_ButtonTexture: {fileID: 2800000, guid: 78bb99e2fe13ca44d9d0b4ab7882f4ce, type: 3} m_AtlasTexture: 1 m_ToggleButton: 0 m_LongPressReleaseButton: 0 From 7a2b8322ba6aa43990c2dbb2b01b032d411694c7 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Mon, 7 Oct 2024 20:39:32 +0100 Subject: [PATCH 019/174] Initialize keyboard with room name and refactor room name handling - Added initialization of the keyboard with the current room name in `MultiplayerPanelButton.cs`. - Changed `SetRoomName` to a property (`RoomName`) in `MultiplayerPanel.cs` for better readability and encapsulation. - Updated `SketchControlScript.cs` to use the new `RoomName` property. --- Assets/Scripts/GUI/MultiplayerPanel.cs | 22 +++++++++----------- Assets/Scripts/GUI/MultiplayerPanelButton.cs | 13 ++++++++++-- Assets/Scripts/SketchControlsScript.cs | 3 +-- 3 files changed, 22 insertions(+), 16 deletions(-) diff --git a/Assets/Scripts/GUI/MultiplayerPanel.cs b/Assets/Scripts/GUI/MultiplayerPanel.cs index ce3151c813..144eb8654b 100644 --- a/Assets/Scripts/GUI/MultiplayerPanel.cs +++ b/Assets/Scripts/GUI/MultiplayerPanel.cs @@ -16,6 +16,7 @@ using OpenBrush.Multiplayer; using TMPro; using UnityEngine; +using static TiltBrush.SketchControlsScript; namespace TiltBrush { @@ -23,11 +24,16 @@ public class MultiplayerPanel : BasePanel { [SerializeField] private TextMeshPro m_RoomNumberTextLobby; [SerializeField] private TextMeshPro m_RoomNumberTextRoomSettings; + private PanelManager m_PanelManager; - public void SetRoomName(string roomName) + public string RoomName { - data.roomName = roomName; - UpdateRoomNumberDisplay(); + get { return data.roomName; } + set + { + data.roomName = value; + UpdateRoomNumberDisplay(); + } } private RoomCreateData data = new RoomCreateData @@ -60,16 +66,11 @@ public enum Mode { Null, Lobby, - Joined, - //Create, - //RoomSettings, - //GeneralSettings + Joined } [SerializeField] private GameObject m_LobbyElements; [SerializeField] private GameObject m_JoinedElements; - //[SerializeField] private GameObject m_RoomSettingsElements; - //[SerializeField] private GameObject m_GeneralSettingsElements; private Mode m_CurrentMode; @@ -115,8 +116,6 @@ private void UpdateMode(Mode newMode) m_CurrentMode = newMode; m_LobbyElements.SetActive(m_CurrentMode == Mode.Lobby); m_JoinedElements.SetActive(m_CurrentMode == Mode.Joined); - //m_RoomSettingsElements.SetActive(m_CurrentMode == Mode.RoomSettings); - //m_GeneralSettingsElements.SetActive(m_CurrentMode == Mode.GeneralSettings); // Update room number display if switching to a mode that shows it if (m_CurrentMode == Mode.Lobby || m_CurrentMode == Mode.Joined) @@ -153,7 +152,6 @@ public void OnMultiplayerPanelButtonPressed(MultiplayerPanelButton button) break; case SketchControlsScript.GlobalCommands.MultiplayerJoinRoom: JoinRoom(); - Debug.Log("Joining room"); break; } } diff --git a/Assets/Scripts/GUI/MultiplayerPanelButton.cs b/Assets/Scripts/GUI/MultiplayerPanelButton.cs index afc40b2aea..925240841c 100644 --- a/Assets/Scripts/GUI/MultiplayerPanelButton.cs +++ b/Assets/Scripts/GUI/MultiplayerPanelButton.cs @@ -13,6 +13,7 @@ // limitations under the License. using UnityEngine; +using static UnityEditor.Experimental.GraphView.GraphView; namespace TiltBrush { @@ -21,15 +22,23 @@ public class MultiplayerPanelButton : OptionButton [SerializeField] private bool m_CommandIgnored = false; override protected void OnButtonPressed() - { + { + + MultiplayerPanel popup = m_Manager.GetComponent(); + // For some circumstances on mobile, we want to ignore the command, but still // notify the popup that we were pressed. Which happens below. if (!m_CommandIgnored) { + if (m_RequiresPopup & m_Command == SketchControlsScript.GlobalCommands.EditMultiplayerRoomName) + { + KeyboardPopUpWindow.m_InitialText = popup.RoomName; + } + base.OnButtonPressed(); } - MultiplayerPanel popup = m_Manager.GetComponent(); + Debug.Assert(popup != null); popup.OnMultiplayerPanelButtonPressed(this); } diff --git a/Assets/Scripts/SketchControlsScript.cs b/Assets/Scripts/SketchControlsScript.cs index 58d3e39e9e..e6677d4e5d 100644 --- a/Assets/Scripts/SketchControlsScript.cs +++ b/Assets/Scripts/SketchControlsScript.cs @@ -4514,11 +4514,10 @@ public void IssueGlobalCommand(GlobalCommands rEnum, int iParam1 = -1, case GlobalCommands.EditMultiplayerRoomName: { var panel = (MultiplayerPanel)m_PanelManager.GetActivePanelByType(BasePanel.PanelType.Multiplayer); - panel.SetRoomName(KeyboardPopUpWindow.m_LastInput); + panel.RoomName = KeyboardPopUpWindow.m_LastInput; DismissPopupOnCurrentGazeObject(false); break; } - case GlobalCommands.ShowWindowGUI: break; case GlobalCommands.Disco: From 02dbcfe7eababbc8af754adadee0c97f74e2ca28 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Mon, 7 Oct 2024 21:26:16 +0100 Subject: [PATCH 020/174] Does Room exist -MultiplayerManager: Added DoesRoomNameExist() and updated m_RoomData for room validation. -MultiplayerPanel: Added UpdateRoomExistenceMessage() to indicate if a room exists or will be created (added a text to the prefab) --- Assets/Prefabs/Panels/MultiplayerPanel.prefab | 206 +++++++++++++++++- Assets/Scripts/GUI/MultiplayerPanel.cs | 35 ++- .../Scripts/Multiplayer/MultiplayerManager.cs | 11 + 3 files changed, 241 insertions(+), 11 deletions(-) diff --git a/Assets/Prefabs/Panels/MultiplayerPanel.prefab b/Assets/Prefabs/Panels/MultiplayerPanel.prefab index 223f67640c..5c9e6274f4 100644 --- a/Assets/Prefabs/Panels/MultiplayerPanel.prefab +++ b/Assets/Prefabs/Panels/MultiplayerPanel.prefab @@ -109,6 +109,7 @@ MonoBehaviour: m_PromoBorders: [] m_RoomNumberTextLobby: {fileID: 1974621732855197313} m_RoomNumberTextRoomSettings: {fileID: 932498118536074667} + m_DoesRoomNumberExist: {fileID: 5365545856024163458} m_LobbyElements: {fileID: 3644177191085397888} m_JoinedElements: {fileID: 6171746570159008301} references: @@ -971,7 +972,7 @@ Transform: m_GameObject: {fileID: 1697630693985053451} serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: -0.329, y: 0.33, z: -0.042} + m_LocalPosition: {x: -0.329, y: 0.31899986, z: -0.042} m_LocalScale: {x: 0.4, y: 0.4, z: 0.35} m_ConstrainProportionsScale: 0 m_Children: @@ -1399,10 +1400,8 @@ MonoBehaviour: m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] - m_text: 'You are in! - - Share the room number with your sketch collaborators - to join ' + m_text: You have successfully joined the room! Share the room number with your + collaborators so they can join too. m_isRightToLeft: 0 m_fontAsset: {fileID: 11400000, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} m_sharedMaterial: {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} @@ -1686,10 +1685,11 @@ Transform: m_GameObject: {fileID: 3644177191085397888} serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalPosition: {x: 0, y: -0.113, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: + - {fileID: 6321725960906406704} - {fileID: 1141409664942745479} - {fileID: 7383649761766971007} - {fileID: 1616599776894361023} @@ -1730,7 +1730,7 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: 0.586, y: 0.145} + m_AnchoredPosition: {x: 0.586, y: 0.13399984} m_SizeDelta: {x: 1.104868, y: 1.4460607} m_Pivot: {x: 0.5, y: 0.5} --- !u!23 &8238648932876523510 @@ -1867,6 +1867,194 @@ MonoBehaviour: m_hasFontAssetChanged: 0 m_renderer: {fileID: 8238648932876523510} m_maskType: 0 +--- !u!1 &4597522067460985994 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 6321725960906406704} + - component: {fileID: 2972459032532661851} + - component: {fileID: 5365545856024163458} + - component: {fileID: 2149637936205218271} + m_Layer: 16 + m_Name: DoesRoomExistLabel + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &6321725960906406704 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4597522067460985994} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: -0.05} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 2965947989537662772} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0.143, y: 1.179} + m_SizeDelta: {x: 1.104868, y: 1.4460607} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!23 &2972459032532661851 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4597522067460985994} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, 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: 0 + 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!114 &5365545856024163458 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4597522067460985994} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9541d86e2fd84c1d9990edf0852d74ab, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_text: This room does not exist yet. By pressing join, the room will be created. + m_isRightToLeft: 0 + m_fontAsset: {fileID: 11400000, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + m_sharedMaterial: {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + m_fontSharedMaterials: [] + m_fontMaterial: {fileID: 0} + m_fontMaterials: [] + m_fontColor32: + serializedVersion: 2 + rgba: 4294967295 + m_fontColor: {r: 1, g: 1, b: 1, a: 1} + m_enableVertexGradient: 0 + m_colorMode: 3 + m_fontColorGradient: + topLeft: {r: 1, g: 1, b: 1, a: 1} + topRight: {r: 1, g: 1, b: 1, a: 1} + bottomLeft: {r: 1, g: 1, b: 1, a: 1} + bottomRight: {r: 1, g: 1, b: 1, a: 1} + m_fontColorGradientPreset: {fileID: 0} + m_spriteAsset: {fileID: 0} + m_tintAllSprites: 0 + m_StyleSheet: {fileID: 0} + m_TextStyleHashCode: -1183493901 + m_overrideHtmlColors: 0 + m_faceColor: + serializedVersion: 2 + rgba: 4294967295 + m_fontSize: 0.8 + m_fontSizeBase: 0.8 + m_fontWeight: 400 + m_enableAutoSizing: 0 + m_fontSizeMin: 18 + m_fontSizeMax: 72 + m_fontStyle: 0 + m_HorizontalAlignment: 2 + m_VerticalAlignment: 1024 + m_textAlignment: 65535 + m_characterSpacing: 0 + m_wordSpacing: 0 + m_lineSpacing: -30 + m_lineSpacingMax: 0 + m_paragraphSpacing: 0 + m_charWidthMaxAdj: 0 + m_enableWordWrapping: 1 + m_wordWrappingRatios: 0.4 + m_overflowMode: 0 + m_linkedTextComponent: {fileID: 0} + parentLinkedComponent: {fileID: 0} + m_enableKerning: 1 + m_enableExtraPadding: 0 + checkPaddingRequired: 0 + m_isRichText: 1 + m_parseCtrlCharacters: 1 + m_isOrthographic: 0 + m_isCullingEnabled: 0 + m_horizontalMapping: 0 + m_verticalMapping: 0 + m_uvLineOffset: 0 + m_geometrySortingOrder: 0 + m_IsTextObjectScaleStatic: 0 + m_VertexBufferAutoSizeReduction: 0 + m_useMaxVisibleDescender: 1 + m_pageToDisplay: 1 + m_margin: {x: -0.31141979, y: 0.6332202, z: 0.083378464, w: 0.49275255} + m_isUsingLegacyAnimationComponent: 0 + m_isVolumetricText: 0 + _SortingLayer: 0 + _SortingLayerID: 0 + _SortingOrder: 0 + m_hasFontAssetChanged: 0 + m_renderer: {fileID: 2972459032532661851} + m_maskType: 0 +--- !u!114 &2149637936205218271 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4597522067460985994} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 69beb381e244f92449b8c4cf954630e9, type: 3} + m_Name: + m_EditorClassIdentifier: + m_TrackedObjects: [] + references: + version: 2 + RefIds: [] --- !u!1 &4649946342403727185 GameObject: m_ObjectHideFlags: 0 @@ -2067,7 +2255,7 @@ Transform: m_GameObject: {fileID: 5921902206488690915} serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: 0.337, y: 0.33, z: -0.042} + m_LocalPosition: {x: 0.337, y: 0.319, z: -0.042} m_LocalScale: {x: 0.4, y: 0.4, z: 0.35} m_ConstrainProportionsScale: 0 m_Children: @@ -2463,7 +2651,7 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: 0, y: 0.152} + m_AnchoredPosition: {x: 0, y: 0.14099984} m_SizeDelta: {x: 1.104868, y: 1.4460607} m_Pivot: {x: 0.5, y: 0.5} --- !u!23 &9202472902960767262 diff --git a/Assets/Scripts/GUI/MultiplayerPanel.cs b/Assets/Scripts/GUI/MultiplayerPanel.cs index 144eb8654b..9148311c1f 100644 --- a/Assets/Scripts/GUI/MultiplayerPanel.cs +++ b/Assets/Scripts/GUI/MultiplayerPanel.cs @@ -24,6 +24,8 @@ public class MultiplayerPanel : BasePanel { [SerializeField] private TextMeshPro m_RoomNumberTextLobby; [SerializeField] private TextMeshPro m_RoomNumberTextRoomSettings; + [SerializeField] private TextMeshPro m_DoesRoomNumberExist; + private PanelManager m_PanelManager; public string RoomName @@ -32,18 +34,30 @@ public string RoomName set { data.roomName = value; - UpdateRoomNumberDisplay(); + UpdateRoomNumberDisplay(); + UpdateRoomExistenceMessage(); } } private RoomCreateData data = new RoomCreateData { - roomName = GenerateRandomRoomName(), + roomName = GenerateUniqueRoomName(), @private = false, maxPlayers = 4, voiceDisabled = false }; + private static string GenerateUniqueRoomName() + { + string roomName; + do + { + roomName = GenerateRandomRoomName(); + } while (MultiplayerManager.m_Instance.DoesRoomNameExist(roomName)); + + return roomName; + } + private static string GenerateRandomRoomName() { System.Random random = new System.Random(); @@ -111,6 +125,23 @@ private async void JoinRoom() } } + private void UpdateRoomExistenceMessage() + { + if (m_RoomNumberTextLobby) return; + + if (MultiplayerManager.m_Instance != null && m_DoesRoomNumberExist != null) + { + if (MultiplayerManager.m_Instance.DoesRoomNameExist(data.roomName)) + { + m_DoesRoomNumberExist.text = "This room exists. You will be joining an active session. You can change the room number by pressing edit."; + } + else + { + m_DoesRoomNumberExist.text = "This room does not exist yet. By pressing join, the room will be created."; + } + } + } + private void UpdateMode(Mode newMode) { m_CurrentMode = newMode; diff --git a/Assets/Scripts/Multiplayer/MultiplayerManager.cs b/Assets/Scripts/Multiplayer/MultiplayerManager.cs index c1b44cea49..edf2352bc6 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerManager.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerManager.cs @@ -46,6 +46,7 @@ public class MultiplayerManager : MonoBehaviour public Action> remotePlayerJoined; public Action playerLeft; public Action> roomDataRefreshed; + private List m_RoomData = new List(); ulong myOculusUserId; @@ -120,6 +121,16 @@ public async Task Connect(RoomCreateData data) return await m_Manager.Connect(data); } + public bool DoesRoomNameExist(string roomName) + { + return m_RoomData.Any(room => room.roomName == roomName); + } + + void OnRoomDataRefreshed(List rooms) + { + m_RoomData = rooms; + } + void Update() { if (App.CurrentState != App.AppState.Standard || m_Manager == null) From af158cc0d99307be58853ca4ad4a52c392067d0f Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Mon, 7 Oct 2024 22:05:42 +0100 Subject: [PATCH 021/174] Update MultiplayerManager.cs remove 'GraphView' --- Assets/Scripts/Multiplayer/MultiplayerManager.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Assets/Scripts/Multiplayer/MultiplayerManager.cs b/Assets/Scripts/Multiplayer/MultiplayerManager.cs index edf2352bc6..79775f33bd 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerManager.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerManager.cs @@ -271,5 +271,15 @@ async void ShareAnchors() } #endif // OCULUS_SUPPORTED } + + public async Task Disconnect(bool force = false) + { + if (m_Manager != null) + { + return await m_Manager.Disconnect(force); + } + return true; + } + } } From 3e3bc6f6c3d4dbbf37dc4b052b663871a79f91cd Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Wed, 9 Oct 2024 10:21:51 +0100 Subject: [PATCH 022/174] Remove graphview reference --- Assets/Scripts/GUI/MultiplayerPanelButton.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Assets/Scripts/GUI/MultiplayerPanelButton.cs b/Assets/Scripts/GUI/MultiplayerPanelButton.cs index 925240841c..7177119595 100644 --- a/Assets/Scripts/GUI/MultiplayerPanelButton.cs +++ b/Assets/Scripts/GUI/MultiplayerPanelButton.cs @@ -13,7 +13,6 @@ // limitations under the License. using UnityEngine; -using static UnityEditor.Experimental.GraphView.GraphView; namespace TiltBrush { From 562b5bd88c14a37197024bc333ea8d9e14695d0b Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Wed, 9 Oct 2024 10:22:41 +0100 Subject: [PATCH 023/174] dotnet format --- Assets/Scripts/GUI/MultiplayerPanel.cs | 6 +++--- Assets/Scripts/GUI/MultiplayerPanelButton.cs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Assets/Scripts/GUI/MultiplayerPanel.cs b/Assets/Scripts/GUI/MultiplayerPanel.cs index 9148311c1f..7f2f4056d0 100644 --- a/Assets/Scripts/GUI/MultiplayerPanel.cs +++ b/Assets/Scripts/GUI/MultiplayerPanel.cs @@ -35,7 +35,7 @@ public string RoomName { data.roomName = value; UpdateRoomNumberDisplay(); - UpdateRoomExistenceMessage(); + UpdateRoomExistenceMessage(); } } @@ -157,7 +157,7 @@ private void UpdateMode(Mode newMode) private void RefreshObjects() { - + } // This function serves as a callback from ProfilePopUpButtons that want to @@ -172,7 +172,7 @@ public void OnMultiplayerPanelButtonPressed(MultiplayerPanelButton button) RefreshObjects(); break; case SketchControlsScript.GlobalCommands.MultiplayerPanelOptions: - switch((Mode)button.m_CommandParam) + switch ((Mode)button.m_CommandParam) { case Mode.Lobby: UpdateMode(Mode.Lobby); diff --git a/Assets/Scripts/GUI/MultiplayerPanelButton.cs b/Assets/Scripts/GUI/MultiplayerPanelButton.cs index 7177119595..2a78479bcb 100644 --- a/Assets/Scripts/GUI/MultiplayerPanelButton.cs +++ b/Assets/Scripts/GUI/MultiplayerPanelButton.cs @@ -21,10 +21,10 @@ public class MultiplayerPanelButton : OptionButton [SerializeField] private bool m_CommandIgnored = false; override protected void OnButtonPressed() - { + { MultiplayerPanel popup = m_Manager.GetComponent(); - + // For some circumstances on mobile, we want to ignore the command, but still // notify the popup that we were pressed. Which happens below. if (!m_CommandIgnored) @@ -37,7 +37,7 @@ override protected void OnButtonPressed() base.OnButtonPressed(); } - + Debug.Assert(popup != null); popup.OnMultiplayerPanelButtonPressed(this); } From a8e177b31d1455c432ad73138b2df886f7bf9396 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Thu, 10 Oct 2024 17:53:38 +0100 Subject: [PATCH 024/174] Add Disconnection Handling for Multiplayer Mode Update This change set introduces event-driven handling for multiplayer disconnections and ensures that the MultiplayerPanel properly updates its UI when a disconnection occurs. --- Assets/Scripts/GUI/MultiplayerPanel.cs | 9 +++++++-- Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs | 3 +++ Assets/Scripts/Multiplayer/MultiplayerManager.cs | 10 ++++++++++ Assets/Scripts/Multiplayer/Photon/PhotonManager.cs | 6 +++++- 4 files changed, 25 insertions(+), 3 deletions(-) diff --git a/Assets/Scripts/GUI/MultiplayerPanel.cs b/Assets/Scripts/GUI/MultiplayerPanel.cs index 7f2f4056d0..03c8e8036b 100644 --- a/Assets/Scripts/GUI/MultiplayerPanel.cs +++ b/Assets/Scripts/GUI/MultiplayerPanel.cs @@ -12,11 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -using System; using OpenBrush.Multiplayer; using TMPro; using UnityEngine; -using static TiltBrush.SketchControlsScript; namespace TiltBrush { @@ -99,6 +97,12 @@ public override void InitPanel() public async void InitMultiplayer() { bool success = await MultiplayerManager.m_Instance.Init(); + MultiplayerManager.m_Instance.Disconnected += OnDisconnected; + } + + private void OnDisconnected() + { + UpdateMode(Mode.Lobby); } private async void JoinRoom() @@ -160,6 +164,7 @@ private void RefreshObjects() } + // This function serves as a callback from ProfilePopUpButtons that want to // change the mode of the popup on click. public void OnMultiplayerPanelButtonPressed(MultiplayerPanelButton button) diff --git a/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs b/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs index 421b00067c..432d5f7ad5 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs @@ -35,6 +35,9 @@ public interface IConnectionHandler Task RedoCommand(BaseCommand command); Task RpcSyncToSharedAnchor(string uuid); + event Action Disconnected; + + //ITransientData SpawnPlayer(); } diff --git a/Assets/Scripts/Multiplayer/MultiplayerManager.cs b/Assets/Scripts/Multiplayer/MultiplayerManager.cs index 79775f33bd..995861e674 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerManager.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerManager.cs @@ -36,6 +36,7 @@ public class MultiplayerManager : MonoBehaviour { public static MultiplayerManager m_Instance; public MultiplayerType m_MultiplayerType; + public event Action Disconnected; private IConnectionHandler m_Manager; @@ -48,6 +49,8 @@ public class MultiplayerManager : MonoBehaviour public Action> roomDataRefreshed; private List m_RoomData = new List(); + + ulong myOculusUserId; List oculusPlayerIds; @@ -82,6 +85,7 @@ void Start() case MultiplayerType.Photon: #if FUSION_WEAVER m_Manager = new PhotonManager(this); + m_Manager.Disconnected += OnConnectionHandlerDisconnected; #endif // FUSION_WEAVER break; default: @@ -281,5 +285,11 @@ public async Task Disconnect(bool force = false) return true; } + private void OnConnectionHandlerDisconnected() + { + // Invoke the Disconnected event + Disconnected?.Invoke(); + } + } } diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs index f25d9a7901..635688d31b 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs @@ -38,6 +38,8 @@ public class PhotonManager : IConnectionHandler, INetworkRunnerCallbacks AppSettings m_PhotonAppSettings; + public event Action Disconnected; + public PhotonManager(MultiplayerManager manager) { m_Manager = manager; @@ -318,7 +320,9 @@ public void OnSessionListUpdated(NetworkRunner runner, List session #endregion #region Unused Photon Callbacks - public void OnShutdown(NetworkRunner runner, ShutdownReason shutdownReason) { } + public void OnShutdown(NetworkRunner runner, ShutdownReason shutdownReason) { + Disconnected?.Invoke(); + } public void OnDisconnectedFromServer(NetworkRunner runner) { } public void OnConnectFailed(NetworkRunner runner, NetAddress remoteAddress, NetConnectFailedReason reason) { } public void OnInput(NetworkRunner runner, NetworkInput input) { } From 438a276f6ef1c787c02408018d6d4a73753f6ba6 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Thu, 10 Oct 2024 18:14:29 +0100 Subject: [PATCH 025/174] Add Leave Room button Add Leave Room button Add Leave Room Global Command --- Assets/Prefabs/Panels/MultiplayerPanel.prefab | 368 +++++++++++++++++- Assets/Scripts/GUI/MultiplayerPanel.cs | 28 ++ Assets/Scripts/SketchControlsScript.cs | 1 + 3 files changed, 392 insertions(+), 5 deletions(-) diff --git a/Assets/Prefabs/Panels/MultiplayerPanel.prefab b/Assets/Prefabs/Panels/MultiplayerPanel.prefab index 5c9e6274f4..b628483d3d 100644 --- a/Assets/Prefabs/Panels/MultiplayerPanel.prefab +++ b/Assets/Prefabs/Panels/MultiplayerPanel.prefab @@ -1335,7 +1335,7 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: 0.104, y: 0.625} + m_AnchoredPosition: {x: 0.104, y: 0.9} m_SizeDelta: {x: 1.104868, y: 1.4460607} m_Pivot: {x: 0.5, y: 0.5} --- !u!23 &3015414681375869025 @@ -1675,7 +1675,7 @@ GameObject: m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 - m_IsActive: 1 + m_IsActive: 0 --- !u!4 &2965947989537662772 Transform: m_ObjectHideFlags: 0 @@ -2089,7 +2089,7 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: 0, y: 0.15199947} + m_AnchoredPosition: {x: 0, y: 0.36000013} m_SizeDelta: {x: 1.104868, y: 1.4460607} m_Pivot: {x: 0.5, y: 0.5} --- !u!23 &1001195632747271077 @@ -2427,7 +2427,7 @@ GameObject: m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 - m_IsActive: 0 + m_IsActive: 1 --- !u!4 &438733611317437076 Transform: m_ObjectHideFlags: 0 @@ -2444,6 +2444,7 @@ Transform: - {fileID: 3068395288206314123} - {fileID: 3577413846294365787} - {fileID: 4072971569122874247} + - {fileID: 977540286295220915} m_Father: {fileID: 415082} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &6437768756802778087 @@ -2480,7 +2481,7 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: 0.586, y: 0.1449995} + m_AnchoredPosition: {x: 0.586, y: 0.353} m_SizeDelta: {x: 1.104868, y: 1.4460607} m_Pivot: {x: 0.5, y: 0.5} --- !u!23 &6918572163598264854 @@ -2617,6 +2618,192 @@ MonoBehaviour: m_hasFontAssetChanged: 0 m_renderer: {fileID: 6918572163598264854} m_maskType: 0 +--- !u!1 &7389879374748736206 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 977540286295220915} + - component: {fileID: 1862823799823796671} + - component: {fileID: 6441155883621675907} + - component: {fileID: 837553751123664783} + - component: {fileID: 4434619620066107605} + m_Layer: 16 + m_Name: Disconnect Button + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &977540286295220915 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7389879374748736206} + serializedVersion: 2 + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0.01, y: -0.008, z: -0.042} + m_LocalScale: {x: 0.5, y: 0.4, z: 0.35} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 5290855516666066687} + m_Father: {fileID: 438733611317437076} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &1862823799823796671 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7389879374748736206} + m_Mesh: {fileID: 4300000, guid: 70d5ab47bcc3aa0439e20bd40350c05a, type: 3} +--- !u!23 &6441155883621675907 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7389879374748736206} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 40d29de2bdc11f04dbfa25059165916e, 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: 0 + 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!65 &837553751123664783 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7389879374748736206} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1.5, y: 1, z: 0.1} + m_Center: {x: -0.000000074505806, y: 0.000000022351742, z: 0} +--- !u!114 &4434619620066107605 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7389879374748736206} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: b78a8d7209bbdc546979b549a875d550, type: 3} + m_Name: + m_EditorClassIdentifier: + m_DescriptionType: 0 + m_DescriptionYOffset: 0 + m_DescriptionText: + m_LocalizedDescription: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_DescriptionTextExtra: + m_LocalizedDescriptionExtra: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_DescriptionActivateSpeed: 12 + m_DescriptionZScale: 1 + m_ButtonTexture: {fileID: 2800000, guid: cdc7d38d9e0b30443961d1cc27195586, type: 3} + m_AtlasTexture: 1 + m_ToggleButton: 0 + m_LongPressReleaseButton: 0 + m_ButtonHasPressedAudio: 1 + m_ZAdjustHover: -0.02 + m_ZAdjustClick: 0.02 + m_HoverScale: 1.1 + m_HoverBoxColliderGrow: 0.2 + m_AddOverlay: 0 + m_Command: 1006 + m_CommandParam: 1 + m_CommandParam2: -1 + m_RequiresPopup: 0 + m_CenterPopupOnButton: 0 + m_PopupOffset: {x: 0, y: 0, z: 0} + m_PopupText: + m_LocalizedPopup: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_ToggleOnDescription: + m_LocalizedToggleOnDescription: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_ToggleOnTexture: {fileID: 0} + m_AllowUnavailable: 0 + m_LinkedUIObject: {fileID: 0} + m_CommandIgnored: 0 + references: + version: 2 + RefIds: [] --- !u!1 &8016613551395990211 GameObject: m_ObjectHideFlags: 0 @@ -2788,6 +2975,177 @@ MonoBehaviour: m_hasFontAssetChanged: 0 m_renderer: {fileID: 9202472902960767262} m_maskType: 0 +--- !u!1 &8600076904461278434 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 5290855516666066687} + - component: {fileID: 6290074660409098260} + - component: {fileID: 9129926250020437674} + m_Layer: 16 + m_Name: Label + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &5290855516666066687 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8600076904461278434} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: -0.057142857} + m_LocalScale: {x: 2.5, y: 2.5, z: 2.8571427} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 977540286295220915} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: -0.56, y: 0.8399991} + m_SizeDelta: {x: 1.104868, y: 1.4460607} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!23 &6290074660409098260 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8600076904461278434} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, 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: 0 + 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!114 &9129926250020437674 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8600076904461278434} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9541d86e2fd84c1d9990edf0852d74ab, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_text: Disconnect + m_isRightToLeft: 0 + m_fontAsset: {fileID: 11400000, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + m_sharedMaterial: {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + m_fontSharedMaterials: [] + m_fontMaterial: {fileID: 0} + m_fontMaterials: [] + m_fontColor32: + serializedVersion: 2 + rgba: 4294967295 + m_fontColor: {r: 1, g: 1, b: 1, a: 1} + m_enableVertexGradient: 0 + m_colorMode: 3 + m_fontColorGradient: + topLeft: {r: 1, g: 1, b: 1, a: 1} + topRight: {r: 1, g: 1, b: 1, a: 1} + bottomLeft: {r: 1, g: 1, b: 1, a: 1} + bottomRight: {r: 1, g: 1, b: 1, a: 1} + m_fontColorGradientPreset: {fileID: 0} + m_spriteAsset: {fileID: 0} + m_tintAllSprites: 0 + m_StyleSheet: {fileID: 0} + m_TextStyleHashCode: -1183493901 + m_overrideHtmlColors: 0 + m_faceColor: + serializedVersion: 2 + rgba: 4294967295 + m_fontSize: 1.4 + m_fontSizeBase: 1.4 + m_fontWeight: 400 + m_enableAutoSizing: 0 + m_fontSizeMin: 18 + m_fontSizeMax: 72 + m_fontStyle: 0 + m_HorizontalAlignment: 2 + m_VerticalAlignment: 512 + m_textAlignment: 65535 + m_characterSpacing: 0 + m_wordSpacing: 0 + m_lineSpacing: -30 + m_lineSpacingMax: 0 + m_paragraphSpacing: 0 + m_charWidthMaxAdj: 0 + m_enableWordWrapping: 1 + m_wordWrappingRatios: 0.4 + m_overflowMode: 0 + m_linkedTextComponent: {fileID: 0} + parentLinkedComponent: {fileID: 0} + m_enableKerning: 1 + m_enableExtraPadding: 0 + checkPaddingRequired: 0 + m_isRichText: 1 + m_parseCtrlCharacters: 1 + m_isOrthographic: 0 + m_isCullingEnabled: 0 + m_horizontalMapping: 0 + m_verticalMapping: 0 + m_uvLineOffset: 0 + m_geometrySortingOrder: 0 + m_IsTextObjectScaleStatic: 0 + m_VertexBufferAutoSizeReduction: 0 + m_useMaxVisibleDescender: 1 + m_pageToDisplay: 1 + m_margin: {x: 0.48827845, y: 0.8604479, z: 0.054796636, w: 0.19184232} + m_isUsingLegacyAnimationComponent: 0 + m_isVolumetricText: 0 + _SortingLayer: 0 + _SortingLayerID: 0 + _SortingOrder: 0 + m_hasFontAssetChanged: 0 + m_renderer: {fileID: 6290074660409098260} + m_maskType: 0 --- !u!1 &8667602655244155954 GameObject: m_ObjectHideFlags: 0 diff --git a/Assets/Scripts/GUI/MultiplayerPanel.cs b/Assets/Scripts/GUI/MultiplayerPanel.cs index 03c8e8036b..0996a7f4b0 100644 --- a/Assets/Scripts/GUI/MultiplayerPanel.cs +++ b/Assets/Scripts/GUI/MultiplayerPanel.cs @@ -123,6 +123,31 @@ private async void JoinRoom() else { Debug.LogError("Failed to connect to room."); + + // Provide user feedback with some UI element + } + + } + } + + private async void LeaveRoom() + { + if (MultiplayerManager.m_Instance != null) + { + + bool success = await MultiplayerManager.m_Instance.Disconnect(false); + + if (success) + { + Debug.Log("Left room successfully."); + + // Additional UI updates or feedback + UpdateMode(Mode.Lobby); + UpdateRoomNumberDisplay(); // Update room number display after joining + } + else + { + Debug.LogError("Failed to leave to room."); // Provide user feedback with some UI element } @@ -189,6 +214,9 @@ public void OnMultiplayerPanelButtonPressed(MultiplayerPanelButton button) case SketchControlsScript.GlobalCommands.MultiplayerJoinRoom: JoinRoom(); break; + case SketchControlsScript.GlobalCommands.MultiplayerLeaveRoom: + LeaveRoom(); + break; } } } diff --git a/Assets/Scripts/SketchControlsScript.cs b/Assets/Scripts/SketchControlsScript.cs index b32ab97bcd..fd61b1b30f 100644 --- a/Assets/Scripts/SketchControlsScript.cs +++ b/Assets/Scripts/SketchControlsScript.cs @@ -150,6 +150,7 @@ public enum GlobalCommands MultiplayerPanelOptions = 1002, // iParam1: Popup options MultiplayerJoinRoom = 1004, EditMultiplayerRoomName = 1005, + MultiplayerLeaveRoom = 1006, RenameSketch = 5200, OpenLayerOptionsPopup = 5201, From f2d21086fe8d7394d2ba841556faa5e8377a1e4a Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Sun, 13 Oct 2024 19:04:42 +0100 Subject: [PATCH 026/174] Update PhotonPlayerRig.prefab --- .../Multiplayer/Photon/PhotonPlayerRig.prefab | 43 +++++++++++++------ 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/Assets/Resources/Multiplayer/Photon/PhotonPlayerRig.prefab b/Assets/Resources/Multiplayer/Photon/PhotonPlayerRig.prefab index a042c63202..8542ca6955 100644 --- a/Assets/Resources/Multiplayer/Photon/PhotonPlayerRig.prefab +++ b/Assets/Resources/Multiplayer/Photon/PhotonPlayerRig.prefab @@ -25,6 +25,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 303516855785969295} + serializedVersion: 2 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} @@ -33,7 +34,6 @@ Transform: - {fileID: 5041384770245166357} - {fileID: 1457871284126546349} m_Father: {fileID: 0} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!114 &7533779467632377196 MonoBehaviour: @@ -47,6 +47,8 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: f250b0d6a1df335439813ad43ce3dbdb, type: 3} m_Name: m_EditorClassIdentifier: + WordOffset: 0 + WordCount: 0 _interpolationDataSource: 0 m_PlayArea: {fileID: 8895232112724846442} m_PlayerHead: {fileID: 5083753190464579125} @@ -55,6 +57,7 @@ MonoBehaviour: m_Tool: {fileID: 5771162771103583132} _oculusPlayerId: 0 headTransform: {fileID: 5041384770245166357} + m_PlayerId: 0 --- !u!114 &6752862290110515822 MonoBehaviour: m_ObjectHideFlags: 0 @@ -108,6 +111,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 459460118330075700} + serializedVersion: 2 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} @@ -115,7 +119,6 @@ Transform: m_Children: - {fileID: 3005319065403690882} m_Father: {fileID: 1457871284126546349} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!114 &8895232112724846442 MonoBehaviour: @@ -129,6 +132,8 @@ MonoBehaviour: m_Script: {fileID: 158639473, guid: e725a070cec140c4caffb81624c8c787, type: 3} m_Name: m_EditorClassIdentifier: + WordOffset: 0 + WordCount: 0 _interpolationDataSource: 0 InterpolationSpace: 0 InterpolationTarget: {fileID: 3005319065403690882} @@ -169,6 +174,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1184365666732702344} + serializedVersion: 2 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} @@ -176,7 +182,6 @@ Transform: m_Children: - {fileID: 4501806461527917157} m_Father: {fileID: 1457871284126546349} - m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!114 &4999079164026519325 MonoBehaviour: @@ -190,6 +195,8 @@ MonoBehaviour: m_Script: {fileID: 158639473, guid: e725a070cec140c4caffb81624c8c787, type: 3} m_Name: m_EditorClassIdentifier: + WordOffset: 0 + WordCount: 0 _interpolationDataSource: 0 InterpolationSpace: 0 InterpolationTarget: {fileID: 4501806461527917157} @@ -229,13 +236,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 2181518286534520838} + serializedVersion: 2 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: @@ -261,6 +268,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 2546328019691961597} + serializedVersion: 2 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} @@ -268,7 +276,6 @@ Transform: m_Children: - {fileID: 2514485848220458462} m_Father: {fileID: 1457871284126546349} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!114 &5083753190464579125 MonoBehaviour: @@ -282,6 +289,8 @@ MonoBehaviour: m_Script: {fileID: 158639473, guid: e725a070cec140c4caffb81624c8c787, type: 3} m_Name: m_EditorClassIdentifier: + WordOffset: 0 + WordCount: 0 _interpolationDataSource: 0 InterpolationSpace: 0 InterpolationTarget: {fileID: 2514485848220458462} @@ -322,6 +331,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 2568790808486584579} + serializedVersion: 2 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} @@ -329,7 +339,6 @@ Transform: m_Children: - {fileID: 1310333546402417676} m_Father: {fileID: 1457871284126546349} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!114 &6268094594865570998 MonoBehaviour: @@ -343,6 +352,8 @@ MonoBehaviour: m_Script: {fileID: 158639473, guid: e725a070cec140c4caffb81624c8c787, type: 3} m_Name: m_EditorClassIdentifier: + WordOffset: 0 + WordCount: 0 _interpolationDataSource: 0 InterpolationSpace: 0 InterpolationTarget: {fileID: 1310333546402417676} @@ -382,13 +393,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 3333659737751141766} + serializedVersion: 2 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: @@ -413,6 +424,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 5681254972916902966} + serializedVersion: 2 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} @@ -420,7 +432,6 @@ Transform: m_Children: - {fileID: 7120707258077581554} m_Father: {fileID: 6718544642020818535} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &5736389435991655852 GameObject: @@ -445,13 +456,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 5736389435991655852} + serializedVersion: 2 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: @@ -476,13 +487,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 6003437268994292288} + serializedVersion: 2 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: @@ -508,6 +519,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 7803168780329886017} + serializedVersion: 2 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} @@ -515,7 +527,6 @@ Transform: m_Children: - {fileID: 2439271445520760248} m_Father: {fileID: 1457871284126546349} - m_RootOrder: 4 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!114 &5771162771103583132 MonoBehaviour: @@ -529,6 +540,8 @@ MonoBehaviour: m_Script: {fileID: 158639473, guid: e725a070cec140c4caffb81624c8c787, type: 3} m_Name: m_EditorClassIdentifier: + WordOffset: 0 + WordCount: 0 _interpolationDataSource: 0 InterpolationSpace: 0 InterpolationTarget: {fileID: 2439271445520760248} @@ -568,6 +581,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8601996063017238699} + serializedVersion: 2 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} @@ -579,7 +593,6 @@ Transform: - {fileID: 7668165333132264083} - {fileID: 8617970912018780043} m_Father: {fileID: 6718544642020818535} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &8742815544225261104 GameObject: @@ -604,19 +617,20 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8742815544225261104} + serializedVersion: 2 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: + serializedVersion: 3 m_TransformParent: {fileID: 5041384770245166357} m_Modifications: - target: {fileID: 1448111865499013753, guid: 834f4ac71ec35d148acacfca28f04405, @@ -695,6 +709,9 @@ PrefabInstance: value: 0 objectReference: {fileID: 0} m_RemovedComponents: [] + m_RemovedGameObjects: [] + m_AddedGameObjects: [] + m_AddedComponents: [] m_SourcePrefab: {fileID: 100100000, guid: 834f4ac71ec35d148acacfca28f04405, type: 3} --- !u!4 &7120707258077581554 stripped Transform: From b9fcd275024d703aeaaf53fc87a87a9f11887b57 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Mon, 14 Oct 2024 12:12:30 +0100 Subject: [PATCH 027/174] Update MultiplayerPanel.prefab --- Assets/Prefabs/Panels/MultiplayerPanel.prefab | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Assets/Prefabs/Panels/MultiplayerPanel.prefab b/Assets/Prefabs/Panels/MultiplayerPanel.prefab index b628483d3d..3a2aa510c4 100644 --- a/Assets/Prefabs/Panels/MultiplayerPanel.prefab +++ b/Assets/Prefabs/Panels/MultiplayerPanel.prefab @@ -1675,7 +1675,7 @@ GameObject: m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 - m_IsActive: 0 + m_IsActive: 1 --- !u!4 &2965947989537662772 Transform: m_ObjectHideFlags: 0 @@ -2427,7 +2427,7 @@ GameObject: m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 - m_IsActive: 1 + m_IsActive: 0 --- !u!4 &438733611317437076 Transform: m_ObjectHideFlags: 0 From fc4d4310c4fc124170d99dbf3e5abac288da59f9 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Mon, 14 Oct 2024 15:52:28 +0100 Subject: [PATCH 028/174] Editor UI to test connection to the room --- Assets/Editor/MultiplayerManagerEditor.cs | 61 +++++++++++++++++++ .../Editor/MultiplayerManagerEditor.cs.meta | 11 ++++ 2 files changed, 72 insertions(+) create mode 100644 Assets/Editor/MultiplayerManagerEditor.cs create mode 100644 Assets/Editor/MultiplayerManagerEditor.cs.meta diff --git a/Assets/Editor/MultiplayerManagerEditor.cs b/Assets/Editor/MultiplayerManagerEditor.cs new file mode 100644 index 0000000000..909d5963ba --- /dev/null +++ b/Assets/Editor/MultiplayerManagerEditor.cs @@ -0,0 +1,61 @@ +// MultiplayerManagerInspector.cs +using UnityEditor; +using UnityEngine; +using OpenBrush.Multiplayer; +using System.Threading.Tasks; + +#if UNITY_EDITOR +[CustomEditor(typeof(MultiplayerManager))] +public class MultiplayerManagerInspector : Editor +{ + private MultiplayerManager multiplayerManager; + private string roomName = "1234"; + private bool isPrivate = false; + private int maxPlayers = 4; + private bool voiceDisabled = false; + + public override void OnInspectorGUI() + { + // Get the target object (MultiplayerManager) + multiplayerManager = (MultiplayerManager)target; + + GUILayout.Label("Multiplayer Manager Controls", EditorStyles.boldLabel); + + // Room data input fields + roomName = EditorGUILayout.TextField("Room Name", roomName); + + + if (GUILayout.Button("Connect to Room")) + { + ConnectToRoom(); + } + + // Draw default inspector below + DrawDefaultInspector(); + } + + private async void ConnectToRoom() + { + if (multiplayerManager != null) + { + RoomCreateData roomData = new RoomCreateData + { + roomName = roomName, + @private = isPrivate, + maxPlayers = maxPlayers, + voiceDisabled = voiceDisabled + }; + + bool success = await multiplayerManager.Connect(roomData); + if (success) + { + Debug.Log($"Successfully connected to room: {roomName}"); + } + else + { + Debug.LogError($"Failed to connect to room: {roomName}"); + } + } + } +} +#endif diff --git a/Assets/Editor/MultiplayerManagerEditor.cs.meta b/Assets/Editor/MultiplayerManagerEditor.cs.meta new file mode 100644 index 0000000000..48c328620f --- /dev/null +++ b/Assets/Editor/MultiplayerManagerEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: dd27756f885442546a0ff09f78c36081 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From 321325470e64ada8c362bc193277c904f0b16f9b Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Mon, 14 Oct 2024 19:05:08 +0100 Subject: [PATCH 029/174] Reducing payload per Stroke Chunk - Reducing payload per Stroke Chunk from 128 control points to 10 control points - Adding a single point where the payload can be edited called NetworkingConstants this is then used both in PhotonStructs.cs and PhotonManager.cs --- .../Multiplayer/Photon/NetworkingConstants.cs | 42 +++++++++++++++++++ .../Photon/NetworkingConstants.cs.meta | 11 +++++ .../Multiplayer/Photon/PhotonManager.cs | 16 +++---- .../Multiplayer/Photon/PhotonStructs.cs | 2 +- 4 files changed, 63 insertions(+), 8 deletions(-) create mode 100644 Assets/Scripts/Multiplayer/Photon/NetworkingConstants.cs create mode 100644 Assets/Scripts/Multiplayer/Photon/NetworkingConstants.cs.meta diff --git a/Assets/Scripts/Multiplayer/Photon/NetworkingConstants.cs b/Assets/Scripts/Multiplayer/Photon/NetworkingConstants.cs new file mode 100644 index 0000000000..365c0b0523 --- /dev/null +++ b/Assets/Scripts/Multiplayer/Photon/NetworkingConstants.cs @@ -0,0 +1,42 @@ +// 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.Collections; +using System.Collections.Generic; +using UnityEngine; + +namespace OpenBrush.Multiplayer +{ + public static class NetworkingConstants + { + // Maximum capacity for NetworkedStroke's NetworkArrays + public const int MaxControlPointsPerChunk = 10; + + // each control point is 36 bytes: + + // public Vector3 m_Pos 12 bytes (3 floats x 4 bytes each) + // public Quaternion m_Orient 16 bytes (4 floats x 4 bytes each) + // public float m_Pressure 4 bytes + // public uint m_TimestampMs 4 bytes + + // Given the maximum payload size of 512 bytes for Fusion RPCs + // (let's assume approximately 50 bytes for overhead) + // we can fit 12 control points in a single RPC + + } +} + +#endif // FUSION_WEAVER diff --git a/Assets/Scripts/Multiplayer/Photon/NetworkingConstants.cs.meta b/Assets/Scripts/Multiplayer/Photon/NetworkingConstants.cs.meta new file mode 100644 index 0000000000..d3b014b699 --- /dev/null +++ b/Assets/Scripts/Multiplayer/Photon/NetworkingConstants.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 47b83bde77ea0944890c05128ca9f8a1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs index 635688d31b..fe2a64f23a 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs @@ -201,16 +201,18 @@ private bool ProcessCommand(BaseCommand command) private bool CommandBrushStroke(BrushStrokeCommand command) { var stroke = command.m_Stroke; + int maxPointsPerChunk = NetworkingConstants.MaxControlPointsPerChunk; - if (stroke.m_ControlPoints.Length > 128) + + if (stroke.m_ControlPoints.Length > maxPointsPerChunk) { // Split and Send - int numSplits = stroke.m_ControlPoints.Length / 128; + int numSplits = stroke.m_ControlPoints.Length / maxPointsPerChunk; var firstStroke = new Stroke(stroke) { - m_ControlPoints = stroke.m_ControlPoints.Take(128).ToArray(), - m_ControlPointsToDrop = stroke.m_ControlPointsToDrop.Take(128).ToArray() + m_ControlPoints = stroke.m_ControlPoints.Take(maxPointsPerChunk).ToArray(), + m_ControlPointsToDrop = stroke.m_ControlPointsToDrop.Take(maxPointsPerChunk).ToArray() }; var netStroke = new NetworkedStroke().Init(firstStroke); @@ -223,8 +225,8 @@ private bool CommandBrushStroke(BrushStrokeCommand command) // 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 controlPoints = stroke.m_ControlPoints.Skip(rounds* maxPointsPerChunk).Take(maxPointsPerChunk).ToArray(); + var dropPoints = stroke.m_ControlPointsToDrop.Skip(rounds* maxPointsPerChunk).Take(maxPointsPerChunk).ToArray(); var netControlPoints = new NetworkedControlPoint[controlPoints.Length]; @@ -233,7 +235,7 @@ private bool CommandBrushStroke(BrushStrokeCommand command) netControlPoints[point] = new NetworkedControlPoint().Init(controlPoints[point]); } - PhotonRPC.RPC_BrushStrokeContinue(m_Runner, strokeGuid, rounds * 128, netControlPoints, dropPoints); + PhotonRPC.RPC_BrushStrokeContinue(m_Runner, strokeGuid, rounds * maxPointsPerChunk, netControlPoints, dropPoints); } // End diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonStructs.cs b/Assets/Scripts/Multiplayer/Photon/PhotonStructs.cs index 10d6787740..6fcf181f93 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonStructs.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonStructs.cs @@ -93,7 +93,7 @@ internal static PointerManager.ControlPoint ToControlPoint(NetworkedControlPoint [System.Serializable] public struct NetworkedStroke : INetworkStruct { - public const int k_MaxCapacity = 128; + public const int k_MaxCapacity = NetworkingConstants.MaxControlPointsPerChunk; public Stroke.Type m_Type; [Networked][Capacity(k_MaxCapacity)] public NetworkArray m_ControlPointsToDrop => default; public Color m_Color; From 0c073166cc3a1a636618cfccae054a3a61e70fac Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Tue, 15 Oct 2024 11:23:10 +0100 Subject: [PATCH 030/174] update Build GitHub Actions workflow (build.yml) to use the new photon tag. --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 431c04255a..2607fc0415 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -352,6 +352,7 @@ jobs: with: token: ${{ env.PHOTON_PAT }} repository: icosa-mirror/photon-fusion + ref: v1.1.10 path: photon-fusion-mirror/ - name: Copy photon files From eed2b6a06052baee123c3feaacdcda39a4ef82b1 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Tue, 15 Oct 2024 11:58:27 +0100 Subject: [PATCH 031/174] Update PopUpWindow_NumericKeyboard.prefab aligning the collider --- .../PopUps/PopUpWindow_NumericKeyboard.prefab | 42 ++++++++++++++----- 1 file changed, 31 insertions(+), 11 deletions(-) diff --git a/Assets/Prefabs/PopUps/PopUpWindow_NumericKeyboard.prefab b/Assets/Prefabs/PopUps/PopUpWindow_NumericKeyboard.prefab index 81d84db7e4..97d03a4b77 100644 --- a/Assets/Prefabs/PopUps/PopUpWindow_NumericKeyboard.prefab +++ b/Assets/Prefabs/PopUps/PopUpWindow_NumericKeyboard.prefab @@ -27,6 +27,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 293902843205586148} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: -0.672, y: -0.291, z: -0.018} m_LocalScale: {x: 0.2, y: 0.2, z: 0.2} @@ -35,7 +36,6 @@ Transform: - {fileID: 1534559809702098178} - {fileID: 1332166645596930261} m_Father: {fileID: 8645625735778197046} - m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &6720148685990472896 MeshFilter: @@ -95,9 +95,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 293902843205586148} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1.0000001, y: 1, z: 0.01} m_Center: {x: 0, y: 0, z: -0.01} --- !u!114 &5466685659915710089 @@ -178,13 +186,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 2617002625324559686} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 0.65, y: 0.65, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 334991469541287722} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &1778020965354640619 MeshFilter: @@ -261,13 +269,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 6530336952169636648} + serializedVersion: 2 m_LocalRotation: {x: 0.7071068, y: -0, z: -0, w: 0.7071068} m_LocalPosition: {x: 0, y: 0, z: 0.025} m_LocalScale: {x: 0.79999995, y: 0.02, z: 0.8} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 334991469541287722} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 90, y: 0, z: 0} --- !u!33 &6179665678031837250 MeshFilter: @@ -344,13 +352,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8644332587409793350} + serializedVersion: 2 m_LocalRotation: {x: 0.5, y: 0.5, z: 0.5, w: 0.5} m_LocalPosition: {x: -1.084, y: 0.21799995, z: 0} m_LocalScale: {x: 42.020817, y: 61.109, z: 55.761967} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 8645625735778197046} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 90, z: 90} --- !u!33 &8610995081492312010 MeshFilter: @@ -428,6 +436,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8644332587479430734} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 3, z: 0} m_LocalScale: {x: 1.5, y: 1.5, z: 1.5} @@ -435,7 +444,6 @@ Transform: m_Children: - {fileID: 8645625735778197046} m_Father: {fileID: 0} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!114 &3095268576859284452 MonoBehaviour: @@ -479,11 +487,19 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8644332587479430734} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 - m_Size: {x: 2.5, y: 1.5, z: 0.01} - m_Center: {x: 0, y: 0, z: -0.01} + serializedVersion: 3 + m_Size: {x: 1.1, y: 1.3, z: 0} + m_Center: {x: -1.1, y: 0.2, z: 0} --- !u!114 &8533537593755566908 MonoBehaviour: m_ObjectHideFlags: 0 @@ -519,6 +535,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8644332587709734578} + serializedVersion: 2 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} @@ -529,7 +546,6 @@ Transform: - {fileID: 4947855712410042213} - {fileID: 334991469541287722} m_Father: {fileID: 8645625734765961102} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &8644332588524666284 GameObject: @@ -556,13 +572,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8644332588524666284} + serializedVersion: 2 m_LocalRotation: {x: 0.5, y: 0.5, z: 0.5, w: 0.5} m_LocalPosition: {x: -1.084, y: 0.218, z: 0} m_LocalScale: {x: 42.020817, y: 61.109, z: 55.761967} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 8645625735778197046} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 90, z: 90} --- !u!33 &8610995081988753086 MeshFilter: @@ -619,6 +635,7 @@ PrefabInstance: m_ObjectHideFlags: 0 serializedVersion: 2 m_Modification: + serializedVersion: 3 m_TransformParent: {fileID: 8645625735778197046} m_Modifications: - target: {fileID: 1934243545717122, guid: 8f1667dadbe5d554b83228c3a246c49f, type: 3} @@ -751,6 +768,9 @@ PrefabInstance: value: 0 objectReference: {fileID: 0} m_RemovedComponents: [] + m_RemovedGameObjects: [] + m_AddedGameObjects: [] + m_AddedComponents: [] m_SourcePrefab: {fileID: 100100000, guid: 8f1667dadbe5d554b83228c3a246c49f, type: 3} --- !u!224 &4947855712410042213 stripped RectTransform: From 0bb5dfe8eb288853be718363b0cb55a2ebb7f4e7 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Tue, 15 Oct 2024 14:51:17 +0100 Subject: [PATCH 032/174] Include Photon Fusion project-specific assets in version control --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 48368ea94c..63fdc9292c 100644 --- a/.gitignore +++ b/.gitignore @@ -106,6 +106,7 @@ Packages/com.unity.xr.picoxr /Assets/Photon/** /Assets/Photon.meta !/Assets/Photon/Fusion/Resources/NetworkProjectConfig.fusion* +!/Assets/Photon/Fusion/User/NetworkPrefabAssetCollection.asset* # Cache files *.cache From ba5a48d4a560e2d69d3a724e762989ca2e561528 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Tue, 15 Oct 2024 15:01:41 +0100 Subject: [PATCH 033/174] Include NetworkPrefabAssetCollection.asset in version control --- .../NetworkProjectConfig.fusion.meta | 2 +- .../User/NetworkPrefabAssetCollection.asset | 140 ++++++++++++++++++ .../NetworkPrefabAssetCollection.asset.meta | 8 + 3 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 Assets/Photon/Fusion/User/NetworkPrefabAssetCollection.asset create mode 100644 Assets/Photon/Fusion/User/NetworkPrefabAssetCollection.asset.meta diff --git a/Assets/Photon/Fusion/Resources/NetworkProjectConfig.fusion.meta b/Assets/Photon/Fusion/Resources/NetworkProjectConfig.fusion.meta index 4e2a77bdf0..51d9b65b56 100644 --- a/Assets/Photon/Fusion/Resources/NetworkProjectConfig.fusion.meta +++ b/Assets/Photon/Fusion/Resources/NetworkProjectConfig.fusion.meta @@ -8,4 +8,4 @@ ScriptedImporter: assetBundleName: assetBundleVariant: script: {fileID: 11500000, guid: 66a64a17d0b40f34f9224317a5a84bf2, type: 3} - PrefabAssetsContainerPath: + PrefabAssetsContainerPath: Assets/Photon/Fusion/User/NetworkPrefabAssetCollection.asset diff --git a/Assets/Photon/Fusion/User/NetworkPrefabAssetCollection.asset b/Assets/Photon/Fusion/User/NetworkPrefabAssetCollection.asset new file mode 100644 index 0000000000..83c9fbd469 --- /dev/null +++ b/Assets/Photon/Fusion/User/NetworkPrefabAssetCollection.asset @@ -0,0 +1,140 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &-9083623630631787094 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: -571403812, guid: e725a070cec140c4caffb81624c8c787, type: 3} + m_Name: FusionAvatar + m_EditorClassIdentifier: + AssetGuid: + RawGuidValue: eedb965bcb4e568479a4828d17fbc1c3 +--- !u!114 &-8634165468179655760 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: -571403812, guid: e725a070cec140c4caffb81624c8c787, type: 3} + m_Name: PlayerTransformPrototype + m_EditorClassIdentifier: + AssetGuid: + RawGuidValue: 3251faa3ac8a66642bf6f9e8433499da +--- !u!114 &-4186714766916533902 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: -571403812, guid: e725a070cec140c4caffb81624c8c787, type: 3} + m_Name: FusionColocationDriver + m_EditorClassIdentifier: + AssetGuid: + RawGuidValue: 8ee45e6a80f151e48a85e55c1f12b1b1 +--- !u!114 &-1425436736254753242 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: -571403812, guid: e725a070cec140c4caffb81624c8c787, type: 3} + m_Name: PlayerRB2DPrototype + m_EditorClassIdentifier: + AssetGuid: + RawGuidValue: dbc9b57ea26fbf84b8cc14b0882fe89b +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: -225830702, guid: e725a070cec140c4caffb81624c8c787, type: 3} + m_Name: NetworkPrefabAssetCollection + m_EditorClassIdentifier: +--- !u!114 &978036179255198872 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: -571403812, guid: e725a070cec140c4caffb81624c8c787, type: 3} + m_Name: BallPrototype + m_EditorClassIdentifier: + AssetGuid: + RawGuidValue: 61b6ef4ffff3d8f4498968869591d6bf +--- !u!114 &1917669454651198095 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: -571403812, guid: e725a070cec140c4caffb81624c8c787, type: 3} + m_Name: PlayerPrototype + m_EditorClassIdentifier: + AssetGuid: + RawGuidValue: e4df49d0bf125a740a2c14ab6e887572 +--- !u!114 &4898726858778889578 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: -571403812, guid: e725a070cec140c4caffb81624c8c787, type: 3} + m_Name: PlayerBallPrototype + m_EditorClassIdentifier: + AssetGuid: + RawGuidValue: 6eca58070665ea64797a9b124cb6ab03 +--- !u!114 &7972530008613410773 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: -571403812, guid: e725a070cec140c4caffb81624c8c787, type: 3} + m_Name: NetworkedRig + m_EditorClassIdentifier: + AssetGuid: + RawGuidValue: 7f1f582839553344c8827c8e7f15e4b2 +--- !u!114 &8276825046164498565 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: -571403812, guid: e725a070cec140c4caffb81624c8c787, type: 3} + m_Name: PhotonPlayerRig + m_EditorClassIdentifier: + AssetGuid: + RawGuidValue: 9ee52735aebb6a445b4905c8aba8cfe3 diff --git a/Assets/Photon/Fusion/User/NetworkPrefabAssetCollection.asset.meta b/Assets/Photon/Fusion/User/NetworkPrefabAssetCollection.asset.meta new file mode 100644 index 0000000000..0ed7a910ab --- /dev/null +++ b/Assets/Photon/Fusion/User/NetworkPrefabAssetCollection.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b6fdca296fa434f49b2a39414b802ac0 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: From 29ce9ff79841f6f902bc258c5c141d40702ce015 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Tue, 15 Oct 2024 15:04:34 +0100 Subject: [PATCH 034/174] Update PhotonPlayerRig.prefab --- Assets/Resources/Multiplayer/Photon/PhotonPlayerRig.prefab | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Assets/Resources/Multiplayer/Photon/PhotonPlayerRig.prefab b/Assets/Resources/Multiplayer/Photon/PhotonPlayerRig.prefab index 8542ca6955..bb88f5e4fd 100644 --- a/Assets/Resources/Multiplayer/Photon/PhotonPlayerRig.prefab +++ b/Assets/Resources/Multiplayer/Photon/PhotonPlayerRig.prefab @@ -10,7 +10,7 @@ GameObject: m_Component: - component: {fileID: 6718544642020818535} - component: {fileID: 7533779467632377196} - - component: {fileID: 6752862290110515822} + - component: {fileID: 2967658244113361874} m_Layer: 0 m_Name: PhotonPlayerRig m_TagString: Untagged @@ -58,7 +58,7 @@ MonoBehaviour: _oculusPlayerId: 0 headTransform: {fileID: 5041384770245166357} m_PlayerId: 0 ---- !u!114 &6752862290110515822 +--- !u!114 &2967658244113361874 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} From f45546bf1823bee69943e4b6f0a865228e584940 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Tue, 15 Oct 2024 18:13:11 +0100 Subject: [PATCH 035/174] removing the NetworkPrefbAssetCollection --- .../User/NetworkPrefabAssetCollection.asset | 140 ------------------ .../NetworkPrefabAssetCollection.asset.meta | 8 - 2 files changed, 148 deletions(-) delete mode 100644 Assets/Photon/Fusion/User/NetworkPrefabAssetCollection.asset delete mode 100644 Assets/Photon/Fusion/User/NetworkPrefabAssetCollection.asset.meta diff --git a/Assets/Photon/Fusion/User/NetworkPrefabAssetCollection.asset b/Assets/Photon/Fusion/User/NetworkPrefabAssetCollection.asset deleted file mode 100644 index 83c9fbd469..0000000000 --- a/Assets/Photon/Fusion/User/NetworkPrefabAssetCollection.asset +++ /dev/null @@ -1,140 +0,0 @@ -%YAML 1.1 -%TAG !u! tag:unity3d.com,2011: ---- !u!114 &-9083623630631787094 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 0} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: -571403812, guid: e725a070cec140c4caffb81624c8c787, type: 3} - m_Name: FusionAvatar - m_EditorClassIdentifier: - AssetGuid: - RawGuidValue: eedb965bcb4e568479a4828d17fbc1c3 ---- !u!114 &-8634165468179655760 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 0} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: -571403812, guid: e725a070cec140c4caffb81624c8c787, type: 3} - m_Name: PlayerTransformPrototype - m_EditorClassIdentifier: - AssetGuid: - RawGuidValue: 3251faa3ac8a66642bf6f9e8433499da ---- !u!114 &-4186714766916533902 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 0} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: -571403812, guid: e725a070cec140c4caffb81624c8c787, type: 3} - m_Name: FusionColocationDriver - m_EditorClassIdentifier: - AssetGuid: - RawGuidValue: 8ee45e6a80f151e48a85e55c1f12b1b1 ---- !u!114 &-1425436736254753242 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 0} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: -571403812, guid: e725a070cec140c4caffb81624c8c787, type: 3} - m_Name: PlayerRB2DPrototype - m_EditorClassIdentifier: - AssetGuid: - RawGuidValue: dbc9b57ea26fbf84b8cc14b0882fe89b ---- !u!114 &11400000 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 0} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: -225830702, guid: e725a070cec140c4caffb81624c8c787, type: 3} - m_Name: NetworkPrefabAssetCollection - m_EditorClassIdentifier: ---- !u!114 &978036179255198872 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 0} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: -571403812, guid: e725a070cec140c4caffb81624c8c787, type: 3} - m_Name: BallPrototype - m_EditorClassIdentifier: - AssetGuid: - RawGuidValue: 61b6ef4ffff3d8f4498968869591d6bf ---- !u!114 &1917669454651198095 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 0} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: -571403812, guid: e725a070cec140c4caffb81624c8c787, type: 3} - m_Name: PlayerPrototype - m_EditorClassIdentifier: - AssetGuid: - RawGuidValue: e4df49d0bf125a740a2c14ab6e887572 ---- !u!114 &4898726858778889578 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 0} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: -571403812, guid: e725a070cec140c4caffb81624c8c787, type: 3} - m_Name: PlayerBallPrototype - m_EditorClassIdentifier: - AssetGuid: - RawGuidValue: 6eca58070665ea64797a9b124cb6ab03 ---- !u!114 &7972530008613410773 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 0} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: -571403812, guid: e725a070cec140c4caffb81624c8c787, type: 3} - m_Name: NetworkedRig - m_EditorClassIdentifier: - AssetGuid: - RawGuidValue: 7f1f582839553344c8827c8e7f15e4b2 ---- !u!114 &8276825046164498565 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 0} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: -571403812, guid: e725a070cec140c4caffb81624c8c787, type: 3} - m_Name: PhotonPlayerRig - m_EditorClassIdentifier: - AssetGuid: - RawGuidValue: 9ee52735aebb6a445b4905c8aba8cfe3 diff --git a/Assets/Photon/Fusion/User/NetworkPrefabAssetCollection.asset.meta b/Assets/Photon/Fusion/User/NetworkPrefabAssetCollection.asset.meta deleted file mode 100644 index 0ed7a910ab..0000000000 --- a/Assets/Photon/Fusion/User/NetworkPrefabAssetCollection.asset.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: b6fdca296fa434f49b2a39414b802ac0 -NativeFormatImporter: - externalObjects: {} - mainObjectFileID: 11400000 - userData: - assetBundleName: - assetBundleVariant: From 20e4a8be479c4fea35fcd2a57b639356e5647475 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Thu, 17 Oct 2024 17:32:15 +0100 Subject: [PATCH 036/174] Track PhotonAppSettings --- .gitignore | 1 + .../Fusion/Resources/PhotonAppSettings.asset | 29 +++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 Assets/Photon/Fusion/Resources/PhotonAppSettings.asset diff --git a/.gitignore b/.gitignore index 63fdc9292c..495b2bc0c2 100644 --- a/.gitignore +++ b/.gitignore @@ -106,6 +106,7 @@ Packages/com.unity.xr.picoxr /Assets/Photon/** /Assets/Photon.meta !/Assets/Photon/Fusion/Resources/NetworkProjectConfig.fusion* +!/Assets/Photon/Fusion/Resources/PhotonAppSettings.asset* !/Assets/Photon/Fusion/User/NetworkPrefabAssetCollection.asset* # Cache files diff --git a/Assets/Photon/Fusion/Resources/PhotonAppSettings.asset b/Assets/Photon/Fusion/Resources/PhotonAppSettings.asset new file mode 100644 index 0000000000..90885d32de --- /dev/null +++ b/Assets/Photon/Fusion/Resources/PhotonAppSettings.asset @@ -0,0 +1,29 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 1878438611, guid: 7de3b8b9e1263ad479e2d0c4261b7646, type: 3} + m_Name: PhotonAppSettings + m_EditorClassIdentifier: + AppSettings: + AppIdFusion: + AppIdChat: + AppIdVoice: + AppVersion: + UseNameServer: 1 + FixedRegion: + Server: + Port: 0 + ProxyServer: + Protocol: 0 + EnableProtocolFallback: 1 + AuthMode: 0 + EnableLobbyStatistics: 0 + NetworkLogging: 1 From 2543d85d8685e4f6da21f5bf1c6cb60f4c557617 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Thu, 17 Oct 2024 17:53:59 +0100 Subject: [PATCH 037/174] Rebake PhotonPlayerRig Rebake attempt --- .../Multiplayer/Photon/PhotonPlayerRig.prefab | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/Assets/Resources/Multiplayer/Photon/PhotonPlayerRig.prefab b/Assets/Resources/Multiplayer/Photon/PhotonPlayerRig.prefab index bb88f5e4fd..35a6e9432e 100644 --- a/Assets/Resources/Multiplayer/Photon/PhotonPlayerRig.prefab +++ b/Assets/Resources/Multiplayer/Photon/PhotonPlayerRig.prefab @@ -9,7 +9,7 @@ GameObject: serializedVersion: 6 m_Component: - component: {fileID: 6718544642020818535} - - component: {fileID: 7533779467632377196} + - component: {fileID: 7215016235458371273} - component: {fileID: 2967658244113361874} m_Layer: 0 m_Name: PhotonPlayerRig @@ -35,7 +35,7 @@ Transform: - {fileID: 1457871284126546349} m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!114 &7533779467632377196 +--- !u!114 &7215016235458371273 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -50,11 +50,11 @@ MonoBehaviour: WordOffset: 0 WordCount: 0 _interpolationDataSource: 0 - m_PlayArea: {fileID: 8895232112724846442} - m_PlayerHead: {fileID: 5083753190464579125} - m_Left: {fileID: 6268094594865570998} - m_Right: {fileID: 4999079164026519325} - m_Tool: {fileID: 5771162771103583132} + m_PlayArea: {fileID: 1840530316396516229} + m_PlayerHead: {fileID: 2866855712475842972} + m_Left: {fileID: 6993845821128094923} + m_Right: {fileID: 1621605232836574636} + m_Tool: {fileID: 4282572128721117942} _oculusPlayerId: 0 headTransform: {fileID: 5041384770245166357} m_PlayerId: 0 @@ -80,12 +80,12 @@ MonoBehaviour: RawGuidValue: 9ee52735aebb6a445b4905c8aba8cfe3 NestedObjects: [] NetworkedBehaviours: - - {fileID: 7533779467632377196} - - {fileID: 8895232112724846442} - - {fileID: 5083753190464579125} - - {fileID: 6268094594865570998} - - {fileID: 4999079164026519325} - - {fileID: 5771162771103583132} + - {fileID: 7215016235458371273} + - {fileID: 1840530316396516229} + - {fileID: 2866855712475842972} + - {fileID: 6993845821128094923} + - {fileID: 1621605232836574636} + - {fileID: 4282572128721117942} SimulationBehaviours: [] --- !u!1 &459460118330075700 GameObject: @@ -96,7 +96,7 @@ GameObject: serializedVersion: 6 m_Component: - component: {fileID: 6539275587195953435} - - component: {fileID: 8895232112724846442} + - component: {fileID: 1840530316396516229} m_Layer: 0 m_Name: Area m_TagString: Untagged @@ -120,7 +120,7 @@ Transform: - {fileID: 3005319065403690882} m_Father: {fileID: 1457871284126546349} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!114 &8895232112724846442 +--- !u!114 &1840530316396516229 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -159,7 +159,7 @@ GameObject: serializedVersion: 6 m_Component: - component: {fileID: 7668165333132264083} - - component: {fileID: 4999079164026519325} + - component: {fileID: 1621605232836574636} m_Layer: 0 m_Name: RightHand m_TagString: Untagged @@ -183,7 +183,7 @@ Transform: - {fileID: 4501806461527917157} m_Father: {fileID: 1457871284126546349} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!114 &4999079164026519325 +--- !u!114 &1621605232836574636 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -253,7 +253,7 @@ GameObject: serializedVersion: 6 m_Component: - component: {fileID: 1030654070696327489} - - component: {fileID: 5083753190464579125} + - component: {fileID: 2866855712475842972} m_Layer: 0 m_Name: Head m_TagString: Untagged @@ -277,7 +277,7 @@ Transform: - {fileID: 2514485848220458462} m_Father: {fileID: 1457871284126546349} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!114 &5083753190464579125 +--- !u!114 &2866855712475842972 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -316,7 +316,7 @@ GameObject: serializedVersion: 6 m_Component: - component: {fileID: 5887326793658353859} - - component: {fileID: 6268094594865570998} + - component: {fileID: 6993845821128094923} m_Layer: 0 m_Name: LeftHand m_TagString: Untagged @@ -340,7 +340,7 @@ Transform: - {fileID: 1310333546402417676} m_Father: {fileID: 1457871284126546349} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!114 &6268094594865570998 +--- !u!114 &6993845821128094923 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -504,7 +504,7 @@ GameObject: serializedVersion: 6 m_Component: - component: {fileID: 8617970912018780043} - - component: {fileID: 5771162771103583132} + - component: {fileID: 4282572128721117942} m_Layer: 0 m_Name: Tool m_TagString: Untagged @@ -528,7 +528,7 @@ Transform: - {fileID: 2439271445520760248} m_Father: {fileID: 1457871284126546349} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!114 &5771162771103583132 +--- !u!114 &4282572128721117942 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} From 69f666bcc7c1bb5aea94f1728d1f67be1451d5e4 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Sun, 20 Oct 2024 17:38:34 +0100 Subject: [PATCH 038/174] Update SketchControlsScript.cs 1- Adding blank commands to avoid Unrecognized command Error --- Assets/Scripts/SketchControlsScript.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Assets/Scripts/SketchControlsScript.cs b/Assets/Scripts/SketchControlsScript.cs index fd61b1b30f..4f81641aca 100644 --- a/Assets/Scripts/SketchControlsScript.cs +++ b/Assets/Scripts/SketchControlsScript.cs @@ -4849,6 +4849,9 @@ public void IssueGlobalCommand(GlobalCommands rEnum, int iParam1 = -1, break; case GlobalCommands.RepaintOptions: break; // Intentionally blank. case GlobalCommands.Null: break; // Intentionally blank. + case GlobalCommands.MultiplayerPanelOptions: break; // Intentionally blank. + case GlobalCommands.MultiplayerJoinRoom: break; // Intentionally blank. + case GlobalCommands.MultiplayerLeaveRoom: break; // Intentionally blank. default: Debug.LogError($"Unrecognized command {rEnum}"); break; From 6f13bb7c80a9836d65f909f442587658f3dae86e Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Sun, 20 Oct 2024 17:38:24 +0100 Subject: [PATCH 039/174] Update MultiplayerPanel.cs Multiplayer Panel 1 - Handle the Case When MultiplayerManager.m_Instance is null 2 - Avoid Accessing MultiplayerManager.m_Instance in Field Initializers --- Assets/Scripts/GUI/MultiplayerPanel.cs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/Assets/Scripts/GUI/MultiplayerPanel.cs b/Assets/Scripts/GUI/MultiplayerPanel.cs index 0996a7f4b0..55f144752e 100644 --- a/Assets/Scripts/GUI/MultiplayerPanel.cs +++ b/Assets/Scripts/GUI/MultiplayerPanel.cs @@ -37,13 +37,18 @@ public string RoomName } } - private RoomCreateData data = new RoomCreateData + private RoomCreateData data; + + public void Awake() { - roomName = GenerateUniqueRoomName(), - @private = false, - maxPlayers = 4, - voiceDisabled = false - }; + data = new RoomCreateData + { + roomName = GenerateUniqueRoomName(), + @private = false, + maxPlayers = 4, + voiceDisabled = false + }; + } private static string GenerateUniqueRoomName() { @@ -51,7 +56,7 @@ private static string GenerateUniqueRoomName() do { roomName = GenerateRandomRoomName(); - } while (MultiplayerManager.m_Instance.DoesRoomNameExist(roomName)); + } while (MultiplayerManager.m_Instance != null && MultiplayerManager.m_Instance.DoesRoomNameExist(roomName)) ; return roomName; } From ee77722de49fe09bac47b9b13455d2b689d77e7e Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Sun, 20 Oct 2024 18:19:20 +0100 Subject: [PATCH 040/174] Handle the removal of the player rig when leaving the room handle the removal of the player rig to ensure proper Clean-up --- Assets/Scripts/Multiplayer/MultiplayerManager.cs | 4 ++++ Assets/Scripts/Multiplayer/Photon/PhotonManager.cs | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/Assets/Scripts/Multiplayer/MultiplayerManager.cs b/Assets/Scripts/Multiplayer/MultiplayerManager.cs index 995861e674..0c64d1f682 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerManager.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerManager.cs @@ -211,6 +211,7 @@ void OnPlayerLeft(int id) { if (m_LocalPlayer.PlayerId == id) { + m_LocalPlayer = null; Debug.Log("Possible to get here!"); return; } @@ -287,6 +288,9 @@ public async Task Disconnect(bool force = false) private void OnConnectionHandlerDisconnected() { + // Clean up local player reference + m_LocalPlayer = null; + // Invoke the Disconnected event Disconnected?.Invoke(); } diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs index fe2a64f23a..b728bd9b64 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs @@ -23,6 +23,7 @@ using Fusion.Photon.Realtime; using Fusion.Sockets; using TiltBrush; +using UnityEditor.Localization.Platform.Android; namespace OpenBrush.Multiplayer { @@ -117,6 +118,13 @@ public async Task Disconnect(bool force) { if(m_Runner != null) { + + if (m_LocalPlayer != null) + { + m_Runner.Despawn(m_LocalPlayer.Object); + m_LocalPlayer = null; + } + await m_Runner.Shutdown(forceShutdownProcedure: force); return m_Runner.IsShutdown; } From 8a589a0882a66b4fb296d4db6233e00ce2964bfb Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Sun, 20 Oct 2024 19:24:45 +0100 Subject: [PATCH 041/174] Update PhotonManager.cs removing UnityEditor.Localization.Platform.Android --- Assets/Scripts/Multiplayer/Photon/PhotonManager.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs index b728bd9b64..ab202ceb7a 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs @@ -23,7 +23,6 @@ using Fusion.Photon.Realtime; using Fusion.Sockets; using TiltBrush; -using UnityEditor.Localization.Platform.Android; namespace OpenBrush.Multiplayer { From e7843f2b42cad0f7b481f89b2227c627077b98e3 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Sun, 20 Oct 2024 20:00:04 +0100 Subject: [PATCH 042/174] Update SketchControlsScript.cs pass pre-commit --- Assets/Scripts/SketchControlsScript.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Assets/Scripts/SketchControlsScript.cs b/Assets/Scripts/SketchControlsScript.cs index 4f81641aca..e8c5e1a4ff 100644 --- a/Assets/Scripts/SketchControlsScript.cs +++ b/Assets/Scripts/SketchControlsScript.cs @@ -4849,9 +4849,9 @@ public void IssueGlobalCommand(GlobalCommands rEnum, int iParam1 = -1, break; case GlobalCommands.RepaintOptions: break; // Intentionally blank. case GlobalCommands.Null: break; // Intentionally blank. - case GlobalCommands.MultiplayerPanelOptions: break; // Intentionally blank. + case GlobalCommands.MultiplayerPanelOptions: break; // Intentionally blank. case GlobalCommands.MultiplayerJoinRoom: break; // Intentionally blank. - case GlobalCommands.MultiplayerLeaveRoom: break; // Intentionally blank. + case GlobalCommands.MultiplayerLeaveRoom: break; // Intentionally blank. default: Debug.LogError($"Unrecognized command {rEnum}"); break; From e5de53b7d76f1d89c1b604eecc40837165787347 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Mon, 21 Oct 2024 17:05:49 +0100 Subject: [PATCH 043/174] Create NetworkPrefabAssetCollection.asset Adding this to solve the --- .../User/NetworkPrefabAssetCollection.asset | 154 ++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 Assets/Photon/Fusion/User/NetworkPrefabAssetCollection.asset diff --git a/Assets/Photon/Fusion/User/NetworkPrefabAssetCollection.asset b/Assets/Photon/Fusion/User/NetworkPrefabAssetCollection.asset new file mode 100644 index 0000000000..dc997b4672 --- /dev/null +++ b/Assets/Photon/Fusion/User/NetworkPrefabAssetCollection.asset @@ -0,0 +1,154 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &-7851828500534389341 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: -571403812, guid: e725a070cec140c4caffb81624c8c787, type: 3} + m_Name: NetworkedRig + m_EditorClassIdentifier: + AssetGuid: + RawGuidValue: 7f1f582839553344c8827c8e7f15e4b2 +--- !u!114 &-6679037723637155746 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: -571403812, guid: e725a070cec140c4caffb81624c8c787, type: 3} + m_Name: PhotonPlayerRig + m_EditorClassIdentifier: + AssetGuid: + RawGuidValue: 9ee52735aebb6a445b4905c8aba8cfe3 +--- !u!114 &-4876441823125888161 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: -571403812, guid: e725a070cec140c4caffb81624c8c787, type: 3} + m_Name: PlayerBallPrototype + m_EditorClassIdentifier: + AssetGuid: + RawGuidValue: 6eca58070665ea64797a9b124cb6ab03 +--- !u!114 &-3882056053279745226 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: -571403812, guid: e725a070cec140c4caffb81624c8c787, type: 3} + m_Name: FusionAvatar + m_EditorClassIdentifier: + AssetGuid: + RawGuidValue: eedb965bcb4e568479a4828d17fbc1c3 +--- !u!114 &-2813734544996678366 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 929548324, guid: e725a070cec140c4caffb81624c8c787, type: 3} + m_Name: ~MISSING~PhotonPlayerRigBkp + m_EditorClassIdentifier: + AssetGuid: + RawGuidValue: a5876108ffed6db47892c3ae59497b56 +--- !u!114 &-2200370131318246874 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: -571403812, guid: e725a070cec140c4caffb81624c8c787, type: 3} + m_Name: BallPrototype + m_EditorClassIdentifier: + AssetGuid: + RawGuidValue: 61b6ef4ffff3d8f4498968869591d6bf +--- !u!114 &-272827184848844876 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: -571403812, guid: e725a070cec140c4caffb81624c8c787, type: 3} + m_Name: FusionColocationDriver + m_EditorClassIdentifier: + AssetGuid: + RawGuidValue: 8ee45e6a80f151e48a85e55c1f12b1b1 +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: -225830702, guid: e725a070cec140c4caffb81624c8c787, type: 3} + m_Name: NetworkPrefabAssetCollection + m_EditorClassIdentifier: +--- !u!114 &1613190245493424334 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: -571403812, guid: e725a070cec140c4caffb81624c8c787, type: 3} + m_Name: PlayerPrototype + m_EditorClassIdentifier: + AssetGuid: + RawGuidValue: e4df49d0bf125a740a2c14ab6e887572 +--- !u!114 &3549518608579063576 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: -571403812, guid: e725a070cec140c4caffb81624c8c787, type: 3} + m_Name: PlayerTransformPrototype + m_EditorClassIdentifier: + AssetGuid: + RawGuidValue: 3251faa3ac8a66642bf6f9e8433499da +--- !u!114 &4563594632895129475 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: -571403812, guid: e725a070cec140c4caffb81624c8c787, type: 3} + m_Name: PlayerRB2DPrototype + m_EditorClassIdentifier: + AssetGuid: + RawGuidValue: dbc9b57ea26fbf84b8cc14b0882fe89b From c93c7baab0b4c724ae5637dcd70fad843f41ebba Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Tue, 22 Oct 2024 10:42:59 +0100 Subject: [PATCH 044/174] Update build.yml predefine FUSION_WEAVER if env.PHOTON_PAT --- .github/workflows/build.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2607fc0415..c0776acab5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -448,6 +448,11 @@ jobs: - name: Enable keystore run: | sed -e 's/androidUseCustomKeystore.*$/androidUseCustomKeystore: 1/' -i ProjectSettings/ProjectSettings.asset + + - name: Add PHOTON_PAT specific define + if: ${{ env.PHOTON_PAT }} + run: | + echo -e "\n-define:FUSION_WEAVER" | tee -a Assets/csc.rsp - name: Update build matrix specific defines in csc.rsp if: ${{ matrix.extra_defines }} From 0febc5f2ceeacd1d5d106fc73d2aae06524b646d Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Tue, 22 Oct 2024 13:06:21 +0100 Subject: [PATCH 045/174] Fix reconnection issue by re-initializing NetworkRunner after shutdown - Modified Disconnect() to set m_Runner to null after calling Shutdown() and destroy its GameObject to ensure proper cleanup. - Updated Connect() to check if m_Runner is null and re-initialize it if necessary before starting a new game. - Extracted runner initialization logic into a new method InitializeRunner() to avoid code duplication and ensure consistent setup. This resolves the error encountered when attempting to reconnect after disconnecting, as m_Runner now correctly handles its lifecycle by being re-instantiated after shutdown. --- .../Multiplayer/Photon/PhotonManager.cs | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs index ab202ceb7a..b6eab6dd45 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs @@ -45,11 +45,7 @@ public PhotonManager(MultiplayerManager manager) m_Manager = manager; m_PlayersSpawning = new List(); - var runnerGO = new GameObject("Photon Network Components"); - m_Runner = runnerGO.AddComponent(); - m_Runner.gameObject.AddComponent(); - m_Runner.ProvideInput = true; - m_Runner.AddCallbacks(this); + InitializeRunner(); m_PhotonAppSettings = new AppSettings { @@ -59,6 +55,16 @@ public PhotonManager(MultiplayerManager manager) }; } + private void InitializeRunner() + { + var runnerGO = new GameObject("Photon Network Components"); + m_Runner = runnerGO.AddComponent(); + m_Runner.gameObject.AddComponent(); + m_Runner.ProvideInput = true; + m_Runner.AddCallbacks(this); + ControllerConsoleScript.m_Instance.AddNewLine("Runner Initialized"); + } + public async Task Init() { await Task.Yield(); @@ -78,7 +84,12 @@ public async Task Init() } public async Task Connect(RoomCreateData roomCreateData) - { + { + if (m_Runner == null) + { + InitializeRunner(); + } + var args = new StartGameArgs() { GameMode = GameMode.Shared, @@ -125,6 +136,7 @@ public async Task Disconnect(bool force) } await m_Runner.Shutdown(forceShutdownProcedure: force); + GameObject.Destroy(m_Runner.gameObject); return m_Runner.IsShutdown; } return true; From be87fc02cce6f7064715e3bddf9e8e1c67295cb1 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Wed, 23 Oct 2024 19:17:21 +0100 Subject: [PATCH 046/174] Lock panels to beginner mode when a user enters a multiplayer room To block the ability to join a multiplayer session when in advance mode: I've changed the availability of the Globalcommand.MultiplayerJoinRoom to only be available when the user is beginner mode. As part of this I also changed the MultiplayerPanel.Prefab button (join room) to AllowUnavailable to True. This change is also supported by a check that happens every time the multiplayer panel is open and a UI alert message explaining to switch to beginner mode. To block the ability to switch to advance mode during a multiplayer session: I've changed the availability of the Globalcommand.AdvancedPanelsToggle in the IsCommandAvailable() essentially I check if the user is in a multiplayer room and if it is the advancePanelsToggle becomes unvalaible. As part of this I also changed the Admin panel prefab buttons (advance beginner mode) to AllowUnavailable to True. --- Assets/Prefabs/Panels/AdminPanel.prefab | 4 +- Assets/Prefabs/Panels/MultiplayerPanel.prefab | 194 +++++++++++++++++- Assets/Scripts/GUI/MultiplayerPanel.cs | 34 ++- .../Multiplayer/MultiplayerInterfaces.cs | 2 + .../Scripts/Multiplayer/MultiplayerManager.cs | 3 +- .../Multiplayer/Photon/PhotonManager.cs | 10 + Assets/Scripts/SketchControlsScript.cs | 3 + 7 files changed, 243 insertions(+), 7 deletions(-) diff --git a/Assets/Prefabs/Panels/AdminPanel.prefab b/Assets/Prefabs/Panels/AdminPanel.prefab index 410bd34c98..34683efa54 100644 --- a/Assets/Prefabs/Panels/AdminPanel.prefab +++ b/Assets/Prefabs/Panels/AdminPanel.prefab @@ -3021,7 +3021,7 @@ MonoBehaviour: m_WaitForCompletion: 0 m_LocalVariables: [] m_ToggleOnTexture: {fileID: 0} - m_AllowUnavailable: 0 + m_AllowUnavailable: 1 m_LinkedUIObject: {fileID: 0} references: version: 2 @@ -3205,7 +3205,7 @@ MonoBehaviour: m_WaitForCompletion: 0 m_LocalVariables: [] m_ToggleOnTexture: {fileID: 0} - m_AllowUnavailable: 0 + m_AllowUnavailable: 1 m_LinkedUIObject: {fileID: 0} references: version: 2 diff --git a/Assets/Prefabs/Panels/MultiplayerPanel.prefab b/Assets/Prefabs/Panels/MultiplayerPanel.prefab index 3a2aa510c4..93afd45381 100644 --- a/Assets/Prefabs/Panels/MultiplayerPanel.prefab +++ b/Assets/Prefabs/Panels/MultiplayerPanel.prefab @@ -110,6 +110,7 @@ MonoBehaviour: m_RoomNumberTextLobby: {fileID: 1974621732855197313} m_RoomNumberTextRoomSettings: {fileID: 932498118536074667} m_DoesRoomNumberExist: {fileID: 5365545856024163458} + m_AlertUserInBeginnerMode: {fileID: 5985317331042495584} m_LobbyElements: {fileID: 3644177191085397888} m_JoinedElements: {fileID: 6171746570159008301} references: @@ -1123,12 +1124,200 @@ MonoBehaviour: m_WaitForCompletion: 0 m_LocalVariables: [] m_ToggleOnTexture: {fileID: 0} - m_AllowUnavailable: 0 + m_AllowUnavailable: 1 m_LinkedUIObject: {fileID: 0} m_CommandIgnored: 0 references: version: 2 RefIds: [] +--- !u!1 &2296016956045778647 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 47429484269554977} + - component: {fileID: 3374390498020739628} + - component: {fileID: 5985317331042495584} + - component: {fileID: 2841728730450484306} + m_Layer: 16 + m_Name: AllertBeginnerMode + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 0 +--- !u!224 &47429484269554977 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2296016956045778647} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: -0.05} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 2965947989537662772} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0.143, y: 1.355} + m_SizeDelta: {x: 1.104868, y: 1.4460607} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!23 &3374390498020739628 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2296016956045778647} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, 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: 0 + 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!114 &5985317331042495584 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2296016956045778647} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9541d86e2fd84c1d9990edf0852d74ab, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_text: To enter a room turn first set to beginner mode + m_isRightToLeft: 0 + m_fontAsset: {fileID: 11400000, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + m_sharedMaterial: {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + m_fontSharedMaterials: [] + m_fontMaterial: {fileID: 0} + m_fontMaterials: [] + m_fontColor32: + serializedVersion: 2 + rgba: 4278190335 + m_fontColor: {r: 1, g: 0, b: 0, a: 1} + m_enableVertexGradient: 0 + m_colorMode: 3 + m_fontColorGradient: + topLeft: {r: 1, g: 1, b: 1, a: 1} + topRight: {r: 1, g: 1, b: 1, a: 1} + bottomLeft: {r: 1, g: 1, b: 1, a: 1} + bottomRight: {r: 1, g: 1, b: 1, a: 1} + m_fontColorGradientPreset: {fileID: 0} + m_spriteAsset: {fileID: 0} + m_tintAllSprites: 0 + m_StyleSheet: {fileID: 0} + m_TextStyleHashCode: -1183493901 + m_overrideHtmlColors: 0 + m_faceColor: + serializedVersion: 2 + rgba: 4294967295 + m_fontSize: 0.8 + m_fontSizeBase: 0.8 + m_fontWeight: 400 + m_enableAutoSizing: 0 + m_fontSizeMin: 18 + m_fontSizeMax: 72 + m_fontStyle: 0 + m_HorizontalAlignment: 2 + m_VerticalAlignment: 1024 + m_textAlignment: 65535 + m_characterSpacing: 0 + m_wordSpacing: 0 + m_lineSpacing: -30 + m_lineSpacingMax: 0 + m_paragraphSpacing: 0 + m_charWidthMaxAdj: 0 + m_enableWordWrapping: 1 + m_wordWrappingRatios: 0.4 + m_overflowMode: 0 + m_linkedTextComponent: {fileID: 0} + parentLinkedComponent: {fileID: 0} + m_enableKerning: 1 + m_enableExtraPadding: 0 + checkPaddingRequired: 0 + m_isRichText: 1 + m_parseCtrlCharacters: 1 + m_isOrthographic: 0 + m_isCullingEnabled: 0 + m_horizontalMapping: 0 + m_verticalMapping: 0 + m_uvLineOffset: 0 + m_geometrySortingOrder: 0 + m_IsTextObjectScaleStatic: 0 + m_VertexBufferAutoSizeReduction: 0 + m_useMaxVisibleDescender: 1 + m_pageToDisplay: 1 + m_margin: {x: -0.31141979, y: 0.74942493, z: 0.083378464, w: 0.49275255} + m_isUsingLegacyAnimationComponent: 0 + m_isVolumetricText: 0 + _SortingLayer: 0 + _SortingLayerID: 0 + _SortingOrder: 0 + m_hasFontAssetChanged: 0 + m_renderer: {fileID: 3374390498020739628} + m_maskType: 0 +--- !u!114 &2841728730450484306 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2296016956045778647} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 69beb381e244f92449b8c4cf954630e9, type: 3} + m_Name: + m_EditorClassIdentifier: + m_TrackedObjects: [] + references: + version: 2 + RefIds: [] --- !u!1 &2571361884171689698 GameObject: m_ObjectHideFlags: 0 @@ -1690,6 +1879,7 @@ Transform: m_ConstrainProportionsScale: 0 m_Children: - {fileID: 6321725960906406704} + - {fileID: 47429484269554977} - {fileID: 1141409664942745479} - {fileID: 7383649761766971007} - {fileID: 1616599776894361023} @@ -1902,7 +2092,7 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: 0.143, y: 1.179} + m_AnchoredPosition: {x: 0.143, y: 1.138} m_SizeDelta: {x: 1.104868, y: 1.4460607} m_Pivot: {x: 0.5, y: 0.5} --- !u!23 &2972459032532661851 diff --git a/Assets/Scripts/GUI/MultiplayerPanel.cs b/Assets/Scripts/GUI/MultiplayerPanel.cs index 55f144752e..895f7ac5b8 100644 --- a/Assets/Scripts/GUI/MultiplayerPanel.cs +++ b/Assets/Scripts/GUI/MultiplayerPanel.cs @@ -23,8 +23,8 @@ public class MultiplayerPanel : BasePanel [SerializeField] private TextMeshPro m_RoomNumberTextLobby; [SerializeField] private TextMeshPro m_RoomNumberTextRoomSettings; [SerializeField] private TextMeshPro m_DoesRoomNumberExist; + [SerializeField] private TextMeshPro m_AlertUserInBeginnerMode; - private PanelManager m_PanelManager; public string RoomName { @@ -48,15 +48,36 @@ public void Awake() maxPlayers = 4, voiceDisabled = false }; + + } + + protected override void OnEnablePanel() + { + base.OnEnablePanel(); + + if (m_CurrentMode == Mode.Lobby) + { + UserInBeginnerMode(); + } } + private void UserInBeginnerMode() + { + if (m_AlertUserInBeginnerMode) + { + PanelManager panelManager = PanelManager.m_Instance; + m_AlertUserInBeginnerMode.gameObject.SetActive(panelManager.AdvancedModeActive()); + } + } + + private static string GenerateUniqueRoomName() { string roomName; do { roomName = GenerateRandomRoomName(); - } while (MultiplayerManager.m_Instance != null && MultiplayerManager.m_Instance.DoesRoomNameExist(roomName)) ; + } while (MultiplayerManager.m_Instance != null && MultiplayerManager.m_Instance.DoesRoomNameExist(roomName)); return roomName; } @@ -79,6 +100,8 @@ private void UpdateRoomNumberDisplay() } } + + public enum Mode { Null, @@ -97,6 +120,7 @@ public override void InitPanel() InitMultiplayer(); UpdateRoomNumberDisplay(); + UserInBeginnerMode(); } public async void InitMultiplayer() @@ -112,6 +136,7 @@ private void OnDisconnected() private async void JoinRoom() { + if (MultiplayerManager.m_Instance != null) { @@ -187,6 +212,11 @@ private void UpdateMode(Mode newMode) { UpdateRoomNumberDisplay(); } + + if (m_CurrentMode == Mode.Lobby) + { + UserInBeginnerMode(); + } } private void RefreshObjects() diff --git a/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs b/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs index 432d5f7ad5..7d5ef3e16d 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs @@ -26,6 +26,8 @@ public interface IConnectionHandler Task Connect(RoomCreateData data); bool IsConnected(); + bool IsInRoom(); + Task Disconnect(bool force = false); void Update(); diff --git a/Assets/Scripts/Multiplayer/MultiplayerManager.cs b/Assets/Scripts/Multiplayer/MultiplayerManager.cs index 0c64d1f682..93cd29db28 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerManager.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerManager.cs @@ -55,7 +55,8 @@ public class MultiplayerManager : MonoBehaviour List oculusPlayerIds; - private bool IsConnected { get { return m_Manager != null && m_Manager.IsConnected(); } } + public bool IsConnected { get { return m_Manager != null && m_Manager.IsConnected(); } } + public bool IsInRoom { get { return m_Manager != null && m_Manager.IsInRoom(); } } void Awake() { diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs index b6eab6dd45..0a1bdb202f 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs @@ -124,6 +124,16 @@ public bool IsConnected() return m_Runner.IsRunning; } + public bool IsInRoom() + { + if (m_Runner == null) + { + return false; + } + return m_Runner.IsInSession; + } + + public async Task Disconnect(bool force) { if(m_Runner != null) diff --git a/Assets/Scripts/SketchControlsScript.cs b/Assets/Scripts/SketchControlsScript.cs index e8c5e1a4ff..0d37c9f1c3 100644 --- a/Assets/Scripts/SketchControlsScript.cs +++ b/Assets/Scripts/SketchControlsScript.cs @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +using OpenBrush.Multiplayer; using System; using System.Collections; using System.Collections.Generic; @@ -5063,6 +5064,8 @@ public bool IsCommandAvailable(GlobalCommands rEnum, int iParam = -1) case GlobalCommands.GoogleDriveSync: return App.GoogleIdentity.LoggedIn; case GlobalCommands.RecordCameraPath: return m_WidgetManager.CameraPathsVisible; + case GlobalCommands.AdvancedPanelsToggle: return !MultiplayerManager.m_Instance.IsInRoom; + case GlobalCommands.MultiplayerJoinRoom: return !PanelManager.m_Instance.AdvancedModeActive(); } return true; } From ea32df6e969bd5314a514d13d945eed4b9ed0ffc Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Wed, 23 Oct 2024 20:44:32 +0100 Subject: [PATCH 047/174] Update MultiplayerPanel.cs initialize the multiplayer panel in lobby mode --- Assets/Scripts/GUI/MultiplayerPanel.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Assets/Scripts/GUI/MultiplayerPanel.cs b/Assets/Scripts/GUI/MultiplayerPanel.cs index 895f7ac5b8..a3cf8834ea 100644 --- a/Assets/Scripts/GUI/MultiplayerPanel.cs +++ b/Assets/Scripts/GUI/MultiplayerPanel.cs @@ -66,7 +66,9 @@ private void UserInBeginnerMode() if (m_AlertUserInBeginnerMode) { PanelManager panelManager = PanelManager.m_Instance; - m_AlertUserInBeginnerMode.gameObject.SetActive(panelManager.AdvancedModeActive()); + bool IsAdavancedModeActive = panelManager.AdvancedModeActive(); + Debug.Log(IsAdavancedModeActive); + m_AlertUserInBeginnerMode.gameObject.SetActive(IsAdavancedModeActive); } } @@ -112,7 +114,7 @@ public enum Mode [SerializeField] private GameObject m_LobbyElements; [SerializeField] private GameObject m_JoinedElements; - private Mode m_CurrentMode; + private Mode m_CurrentMode = Mode.Lobby; public override void InitPanel() { From 70a829f39fce41af68dd17e5ccba07aba6c5a201 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Thu, 24 Oct 2024 12:58:38 +0100 Subject: [PATCH 048/174] RPC Implementation of the Switch Environment Command Implemented RPC_SwitchEnvironment using environment Guid. --- .../Multiplayer/Photon/PhotonManager.cs | 14 +++++++++-- .../Scripts/Multiplayer/Photon/PhotonRPC.cs | 23 ++++++++++++++++++- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs index 0a1bdb202f..67668c04a0 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs @@ -207,6 +207,9 @@ private bool ProcessCommand(BaseCommand command) case DeleteStrokeCommand: success = CommandDeleteStroke(command as DeleteStrokeCommand); break; + case SwitchEnvironmentCommand: + success = CommandSwitchEnvironment(command as SwitchEnvironmentCommand); + break; case BaseCommand: success = CommandBase(command); break; @@ -289,9 +292,16 @@ 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 + private bool CommandSwitchEnvironment(SwitchEnvironmentCommand command) + { + Guid environmentGuid = command.m_NextEnvironment.m_Guid; + PhotonRPC.RPC_SwitchEnvironment(m_Runner, environmentGuid, command.Guid, command.ParentGuid, command.ChildrenCount); + return true; + } + #endregion + + #region Photon Callbacks public void OnConnectedToServer(NetworkRunner runner) { var rpc = m_Runner.gameObject.AddComponent(); diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs b/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs index 8125812a57..031b94331f 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs @@ -21,6 +21,7 @@ using UnityEngine; using Fusion; using TiltBrush; +using System.Windows.Input; namespace OpenBrush.Multiplayer { @@ -293,7 +294,27 @@ public static void RPC_DeleteStroke(NetworkRunner runner, int seed, Guid command Debug.LogError($"couldn't find stroke with seed: {seed}"); } } -#endregion + + [Rpc(InvokeLocal = false)] + public static void RPC_SwitchEnvironment(NetworkRunner runner, Guid environmentGuid, Guid commandGuid, Guid parentGuid = default, int childCount = 0) + { + TiltBrush.Environment environment = EnvironmentCatalog.m_Instance.GetEnvironment(environmentGuid); + + if (environment != null) + { + + var parentCommand = FindParentCommand(parentGuid); + var command = new SwitchEnvironmentCommand(environment, parent: parentCommand); + + AddPendingCommand(() => { }, commandGuid, parentGuid, command, childCount); + } + else + { + Debug.LogError($"Environment with Guid {environmentGuid} not found."); + } + } + + #endregion } } From c9b0d08b68089c2b5cc7f2687a6291bf2d5f8fd6 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Thu, 24 Oct 2024 13:09:43 +0100 Subject: [PATCH 049/174] fix pre-commit yml --- .github/workflows/build.yml | 2 +- .github/workflows/get_license.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c0776acab5..47785da161 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -448,7 +448,7 @@ jobs: - name: Enable keystore run: | sed -e 's/androidUseCustomKeystore.*$/androidUseCustomKeystore: 1/' -i ProjectSettings/ProjectSettings.asset - + - name: Add PHOTON_PAT specific define if: ${{ env.PHOTON_PAT }} run: | diff --git a/.github/workflows/get_license.yml b/.github/workflows/get_license.yml index 13ee4ee968..a86886bf28 100644 --- a/.github/workflows/get_license.yml +++ b/.github/workflows/get_license.yml @@ -14,8 +14,8 @@ jobs: uses: game-ci/unity-request-activation-file@v2 with: unityVersion: 2019.4.25f1 - # Upload artifact (Unity_v20XX.X.XXXX.alf) - name: Expose as artifact + # Upload artifact (Unity_v20XX.X.XXXX.alf) uses: actions/upload-artifact@v4 with: name: ${{ steps.getManualLicenseFile.outputs.filePath }} From da36f7819daa13152f7f004f0064456e21a0c204 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Thu, 24 Oct 2024 13:18:55 +0100 Subject: [PATCH 050/174] fixing RPC Implementation of the Switch Environment Command --- Assets/Scripts/Commands/SwitchEnvironmentCommand.cs | 2 +- Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Assets/Scripts/Commands/SwitchEnvironmentCommand.cs b/Assets/Scripts/Commands/SwitchEnvironmentCommand.cs index f4d721dea2..5ff82667e5 100644 --- a/Assets/Scripts/Commands/SwitchEnvironmentCommand.cs +++ b/Assets/Scripts/Commands/SwitchEnvironmentCommand.cs @@ -19,7 +19,7 @@ public class SwitchEnvironmentCommand : BaseCommand private CustomLights m_PrevLights; private CustomEnvironment m_PrevBackdrop; private Environment m_PrevEnvironment; - private Environment m_NextEnvironment; + public Environment m_NextEnvironment; public SwitchEnvironmentCommand(Environment nextEnv, BaseCommand parent = null) : base(parent) { diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs b/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs index 031b94331f..f65d80b16c 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs @@ -21,7 +21,6 @@ using UnityEngine; using Fusion; using TiltBrush; -using System.Windows.Input; namespace OpenBrush.Multiplayer { From dca4e6560806052644aaf38d1d2ddc3d71c07998 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Thu, 24 Oct 2024 17:21:06 +0100 Subject: [PATCH 051/174] Add Photon Voice Secrets --- Assets/Scripts/Config.cs | 1 + Assets/Scripts/SecretsConfig.cs | 1 + 2 files changed, 2 insertions(+) diff --git a/Assets/Scripts/Config.cs b/Assets/Scripts/Config.cs index efadeeb3db..67188735c8 100644 --- a/Assets/Scripts/Config.cs +++ b/Assets/Scripts/Config.cs @@ -132,6 +132,7 @@ private class UserConfigChange 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 SecretsConfig.ServiceAuthData PhotonVoiceSecrets => Secrets[SecretsConfig.Service.PhotonVoice]; public bool DisableAccountLogins; diff --git a/Assets/Scripts/SecretsConfig.cs b/Assets/Scripts/SecretsConfig.cs index 6b494ceabf..b29f36ca78 100644 --- a/Assets/Scripts/SecretsConfig.cs +++ b/Assets/Scripts/SecretsConfig.cs @@ -27,6 +27,7 @@ public enum Service OculusMobile = 3, Pimax = 4, PhotonFusion = 5, + PhotonVoice = 6, } [Serializable] From c37fe15053780afc1ff8dfc817c8233ae188fee2 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Fri, 25 Oct 2024 11:34:51 +0100 Subject: [PATCH 052/174] Update AndroidManifest.xml Adding microphone permissions --- Assets/Plugins/Android/AndroidManifest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Assets/Plugins/Android/AndroidManifest.xml b/Assets/Plugins/Android/AndroidManifest.xml index 179339006b..b50dec3524 100644 --- a/Assets/Plugins/Android/AndroidManifest.xml +++ b/Assets/Plugins/Android/AndroidManifest.xml @@ -18,7 +18,7 @@ - + From f79c34615b46b9593c53b19486192b88c66cdf71 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Fri, 25 Oct 2024 11:36:09 +0100 Subject: [PATCH 053/174] Photon Voice Integration --- .../Resources/NetworkProjectConfig.fusion | 3 +- .../User/NetworkPrefabAssetCollection.asset | 14 ++ .../Multiplayer/Photon/SpeakerPrefab.prefab | 148 +++++++++++++ .../Photon/SpeakerPrefab.prefab.meta | 7 + Assets/Scenes/Main.unity | 187 +++++++++++++++- .../Multiplayer/MultiplayerInterfaces.cs | 12 ++ .../Scripts/Multiplayer/MultiplayerManager.cs | 36 +++- .../Multiplayer/Photon/PhotonManager.cs | 11 +- .../Multiplayer/Photon/PhotonVoiceManager.cs | 199 ++++++++++++++++++ .../Photon/PhotonVoiceManager.cs.meta | 11 + 10 files changed, 613 insertions(+), 15 deletions(-) create mode 100644 Assets/Resources/Multiplayer/Photon/SpeakerPrefab.prefab create mode 100644 Assets/Resources/Multiplayer/Photon/SpeakerPrefab.prefab.meta create mode 100644 Assets/Scripts/Multiplayer/Photon/PhotonVoiceManager.cs create mode 100644 Assets/Scripts/Multiplayer/Photon/PhotonVoiceManager.cs.meta diff --git a/Assets/Photon/Fusion/Resources/NetworkProjectConfig.fusion b/Assets/Photon/Fusion/Resources/NetworkProjectConfig.fusion index 526a559a17..b5e94af29c 100644 --- a/Assets/Photon/Fusion/Resources/NetworkProjectConfig.fusion +++ b/Assets/Photon/Fusion/Resources/NetworkProjectConfig.fusion @@ -155,7 +155,8 @@ }, "AssembliesToWeave": [ "Assembly-CSharp", - "Assembly-CSharp-firstpass" + "Assembly-CSharp-firstpass", + "PhotonVoice.Fusion" ], "UseSerializableDictionary": true, "NullChecksForNetworkedProperties": true, diff --git a/Assets/Photon/Fusion/User/NetworkPrefabAssetCollection.asset b/Assets/Photon/Fusion/User/NetworkPrefabAssetCollection.asset index dc997b4672..9fa148b93e 100644 --- a/Assets/Photon/Fusion/User/NetworkPrefabAssetCollection.asset +++ b/Assets/Photon/Fusion/User/NetworkPrefabAssetCollection.asset @@ -124,6 +124,20 @@ MonoBehaviour: m_EditorClassIdentifier: AssetGuid: RawGuidValue: e4df49d0bf125a740a2c14ab6e887572 +--- !u!114 &3073148362435255882 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: -571403812, guid: e725a070cec140c4caffb81624c8c787, type: 3} + m_Name: NetworkObject + m_EditorClassIdentifier: + AssetGuid: + RawGuidValue: e63233a416c734342a837267990c58aa --- !u!114 &3549518608579063576 MonoBehaviour: m_ObjectHideFlags: 0 diff --git a/Assets/Resources/Multiplayer/Photon/SpeakerPrefab.prefab b/Assets/Resources/Multiplayer/Photon/SpeakerPrefab.prefab new file mode 100644 index 0000000000..6644b3839e --- /dev/null +++ b/Assets/Resources/Multiplayer/Photon/SpeakerPrefab.prefab @@ -0,0 +1,148 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &9139372413509065517 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3083929738384175756} + - component: {fileID: 5056828011079605640} + - component: {fileID: 2878855519606601759} + m_Layer: 0 + m_Name: SpeakerPrefab + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &3083929738384175756 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9139372413509065517} + 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_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!82 &5056828011079605640 +AudioSource: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9139372413509065517} + m_Enabled: 1 + serializedVersion: 4 + OutputAudioMixerGroup: {fileID: 0} + m_audioClip: {fileID: 0} + m_PlayOnAwake: 1 + m_Volume: 1 + m_Pitch: 1 + Loop: 0 + Mute: 0 + Spatialize: 0 + SpatializePostEffects: 0 + Priority: 128 + DopplerLevel: 1 + MinDistance: 1 + MaxDistance: 500 + Pan2D: 0 + rolloffMode: 0 + BypassEffects: 0 + BypassListenerEffects: 0 + BypassReverbZones: 0 + rolloffCustomCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: 0 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + panLevelCustomCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 0 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + spreadCustomCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 0 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + reverbZoneMixCustomCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 +--- !u!114 &2878855519606601759 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9139372413509065517} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: dd7fea91fe63f4e1b884ef1e16a975c3, type: 3} + m_Name: + m_EditorClassIdentifier: + playDelayConfig: + Low: 200 + High: 400 + Max: 1000 + SpeedUpPerc: 5 + restartOnDeviceChange: 1 diff --git a/Assets/Resources/Multiplayer/Photon/SpeakerPrefab.prefab.meta b/Assets/Resources/Multiplayer/Photon/SpeakerPrefab.prefab.meta new file mode 100644 index 0000000000..a41da51989 --- /dev/null +++ b/Assets/Resources/Multiplayer/Photon/SpeakerPrefab.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 482e38719c8825849a31f10f4f27c046 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scenes/Main.unity b/Assets/Scenes/Main.unity index 5cb1012e08..b0fdb24eee 100644 --- a/Assets/Scenes/Main.unity +++ b/Assets/Scenes/Main.unity @@ -38,7 +38,6 @@ RenderSettings: m_ReflectionIntensity: 1 m_CustomReflection: {fileID: 8900000, guid: 378efb751ea39e14cb1fd93f49ead278, type: 3} m_Sun: {fileID: 0} - m_IndirectSpecularColor: {r: 0, g: 0, b: 0, a: 1} m_UseRadianceAmbientProbe: 0 --- !u!157 &4 LightmapSettings: @@ -3655,7 +3654,7 @@ MonoBehaviour: m_SdkMode: 0 m_AutoProfile: 0 m_AutoProfileWaitTime: 10 - Secrets: {fileID: 11400000, guid: 2f5f8e93c6bc7be49a5023148b50e7a0, type: 2} + Secrets: {fileID: 11400000, guid: 3196e9bd8aa430e42af6fe061aa7323b, type: 2} m_SketchFiles: [] DisableAccountLogins: 0 m_OdsNumFrames: 0 @@ -15177,6 +15176,8 @@ GameObject: m_Component: - component: {fileID: 1052269831} - component: {fileID: 1052269832} + - component: {fileID: 1052269836} + - component: {fileID: 1052269835} m_Layer: 0 m_Name: MultiplayerManager m_TagString: Untagged @@ -15212,6 +15213,105 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: m_MultiplayerType: 2 + CurrentRoomName: +--- !u!114 &1052269835 +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: 91175dcc15224463780e01a8a98b1b60, type: 3} + m_Name: + m_EditorClassIdentifier: + voiceDetection: 0 + voiceDetectionThreshold: 0.01 + voiceDetectionDelayMs: 500 + interestGroup: 0 + useTargetPlayers: 0 + targetPlayers: + debugEchoMode: 0 + reliableMode: 0 + encrypt: 0 + transmitEnabled: 1 + samplingRate: 24000 + frameDuration: 20000 + bitrate: 30000 + sourceType: 0 + microphoneType: 0 + audioClip: {fileID: 0} + loopAudioClip: 1 + recordingEnabled: 1 + audioSessionParameters: + Category: 4 + Mode: 1 + CategoryOptions: 0800000004000000 + editorAudioSessionPreset: 1 + androidMicrophoneSettings: + EnableAEC: 1 + EnableAGC: 1 + EnableNS: 1 + stopRecordingWhenPaused: 0 + useOnAudioFilterRead: 0 + useMicrophoneTypeFallback: 1 + recordWhenJoined: 1 +--- !u!114 &1052269836 +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: 697c1cda529a1419782f2a2e3afe37a2, type: 3} + m_Name: + m_EditorClassIdentifier: + DisconnectAfterKeepAlive: 0 + KeepAliveInBackground: 60000 + ApplyDontDestroyOnLoad: 1 + runInBackground: 1 + statsResetInterval: 1000 + speakerPrefab: {fileID: 9139372413509065517, guid: 482e38719c8825849a31f10f4f27c046, + type: 3} + primaryRecorder: {fileID: 1052269835} + usePrimaryRecorder: 0 + cppCompatibilityMode: 0 + Settings: + AppIdRealtime: + AppIdFusion: + AppIdChat: + AppIdVoice: + AppVersion: + UseNameServer: 1 + FixedRegion: + Server: + Port: 0 + ProxyServer: + Protocol: 0 + EnableProtocolFallback: 1 + AuthMode: 0 + EnableLobbyStatistics: 0 + NetworkLogging: 1 + ShowSettings: 0 + AppSettings: + AppIdFusion: + AppIdChat: + AppIdVoice: + AppVersion: + UseNameServer: 1 + FixedRegion: + Server: + Port: 0 + ProxyServer: + Protocol: 0 + EnableProtocolFallback: 1 + AuthMode: 0 + EnableLobbyStatistics: 0 + NetworkLogging: 1 + UseVoiceAppSettings: 0 --- !u!1 &1057179852 GameObject: m_ObjectHideFlags: 0 @@ -29156,6 +29256,37 @@ MeshFilter: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1726887557} m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &1728487473 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1728487474} + m_Layer: 0 + m_Name: RegionHandler + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1728487474 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1728487473} + serializedVersion: 2 + 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_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1731474005 GameObject: m_ObjectHideFlags: 0 @@ -30328,11 +30459,8 @@ MonoBehaviour: m_Advanced: 0 - m_PanelPrefab: {fileID: 160214, guid: 5a442232cda8d79489436e6d45958790, type: 3} m_ModeVr: 1 - m_ModeVrExperimental: 1 - m_ModeQuestExperimental: 1 m_ModeMono: 1 m_ModeQuest: 1 - m_ModeGvr: 1 m_Basic: 1 m_Advanced: 1 - m_PanelPrefab: {fileID: 1272310558547734, guid: 38a4b95a5f6824c41994709bfbd012ad, @@ -30347,7 +30475,7 @@ MonoBehaviour: m_ModeMono: 1 m_ModeQuest: 1 m_Basic: 0 - m_Advanced: 1 + m_Advanced: 0 - m_PanelPrefab: {fileID: 199434, guid: de22d465caf3f20419b98ce5290a7e57, type: 3} m_ModeVr: 1 m_ModeMono: 1 @@ -34426,6 +34554,51 @@ MeshRenderer: type: 3} m_PrefabInstance: {fileID: 2052658242} m_PrefabAsset: {fileID: 0} +--- !u!1 &2066419199 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2066419201} + - component: {fileID: 2066419200} + m_Layer: 0 + m_Name: VoiceLogger + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &2066419200 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2066419199} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: a86f533fd9917dd4da8601e9eb542c96, type: 3} + m_Name: + m_EditorClassIdentifier: + LogLevel: 4 +--- !u!4 &2066419201 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2066419199} + serializedVersion: 2 + 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_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &2073328682 GameObject: m_ObjectHideFlags: 0 @@ -36964,3 +37137,5 @@ SceneRoots: - {fileID: 1802399861} - {fileID: 106206546} - {fileID: 1848773840} + - {fileID: 2066419201} + - {fileID: 1728487474} diff --git a/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs b/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs index 7d5ef3e16d..30350e2549 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs @@ -39,10 +39,22 @@ public interface IConnectionHandler event Action Disconnected; + string Id { get; } + //ITransientData SpawnPlayer(); } + public interface IVoiceManager + { + void InitializeVoice(); + Task ConnectToVoiceServer(); + Task JoinRoom(string roomName); + void StartSpeaking(); + void StopSpeaking(); + + } + public interface ITransientData { int PlayerId { get; set; } diff --git a/Assets/Scripts/Multiplayer/MultiplayerManager.cs b/Assets/Scripts/Multiplayer/MultiplayerManager.cs index 93cd29db28..bce4ea47d4 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerManager.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerManager.cs @@ -39,6 +39,7 @@ public class MultiplayerManager : MonoBehaviour public event Action Disconnected; private IConnectionHandler m_Manager; + private IVoiceManager m_VoiceManager; private ITransientData m_LocalPlayer; private List> m_RemotePlayers; @@ -49,15 +50,17 @@ public class MultiplayerManager : MonoBehaviour public Action> roomDataRefreshed; private List m_RoomData = new List(); - - ulong myOculusUserId; List oculusPlayerIds; + internal string UserId; + public string CurrentRoomName; public bool IsConnected { get { return m_Manager != null && m_Manager.IsConnected(); } } public bool IsInRoom { get { return m_Manager != null && m_Manager.IsInRoom(); } } + public string Id { get { return m_Manager.Id; } } + void Awake() { m_Instance = this; @@ -86,6 +89,7 @@ void Start() case MultiplayerType.Photon: #if FUSION_WEAVER m_Manager = new PhotonManager(this); + m_VoiceManager = new PhotonVoiceManager(this); m_Manager.Disconnected += OnConnectionHandlerDisconnected; #endif // FUSION_WEAVER break; @@ -123,7 +127,14 @@ public async Task Init() public async Task Connect(RoomCreateData data) { - return await m_Manager.Connect(data); + bool success = await m_Manager.Connect(data); + if (success) + { + CurrentRoomName = data.roomName; + StartSpeaking(); + } + + return success; } public bool DoesRoomNameExist(string roomName) @@ -282,7 +293,9 @@ public async Task Disconnect(bool force = false) { if (m_Manager != null) { - return await m_Manager.Disconnect(force); + var success = await m_Manager.Disconnect(force); + if (success) StopSpeaking(); + return success; } return true; } @@ -296,5 +309,20 @@ private void OnConnectionHandlerDisconnected() Disconnected?.Invoke(); } + public void JoinVoiceRoom(RoomCreateData data) + { + m_VoiceManager?.JoinRoom(data.roomName); + } + + public void StartSpeaking() + { + m_VoiceManager?.StartSpeaking(); + } + + public void StopSpeaking() + { + m_VoiceManager?.StopSpeaking(); + } + } } diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs index 67668c04a0..a5fd786dd8 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs @@ -30,16 +30,18 @@ public class PhotonManager : IConnectionHandler, INetworkRunnerCallbacks { private NetworkRunner m_Runner; - MultiplayerManager m_Manager; + private MultiplayerManager m_Manager; - List m_PlayersSpawning; + private List m_PlayersSpawning; - PhotonPlayerRig m_LocalPlayer; + private PhotonPlayerRig m_LocalPlayer; - AppSettings m_PhotonAppSettings; + private AppSettings m_PhotonAppSettings; public event Action Disconnected; + public string Id { get { return m_Runner.UserId; } } + public PhotonManager(MultiplayerManager manager) { m_Manager = manager; @@ -105,6 +107,7 @@ public async Task Connect(RoomCreateData roomCreateData) if (result.Ok) { ControllerConsoleScript.m_Instance.AddNewLine("Joined Room"); + m_Manager.JoinVoiceRoom(roomCreateData); // i'd like to solve this in a different way } else { diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonVoiceManager.cs b/Assets/Scripts/Multiplayer/Photon/PhotonVoiceManager.cs new file mode 100644 index 0000000000..ccf6638b8f --- /dev/null +++ b/Assets/Scripts/Multiplayer/Photon/PhotonVoiceManager.cs @@ -0,0 +1,199 @@ +#if PHOTON_UNITY_NETWORKING && PHOTON_VOICE_DEFINED +using OpenBrush.Multiplayer; +using Photon.Realtime; +using Photon.Voice.Unity; +using System.Collections.Generic; +using System.Threading.Tasks; +using TiltBrush; +using UnityEngine; + +public class PhotonVoiceManager : IVoiceManager, IConnectionCallbacks, IMatchmakingCallbacks +{ + private VoiceConnection m_VoiceConnection; + private MultiplayerManager m_Manager; + private AppSettings m_PhotonVoiceAppSettings; + private Recorder m_Recorder; + + public PhotonVoiceManager(MultiplayerManager manager) + { + m_Manager = manager; + InitializeVoice(); + + } + + public void InitializeVoice() + { + m_VoiceConnection = GameObject.FindFirstObjectByType(); + if (m_VoiceConnection == null) + { + ControllerConsoleScript.m_Instance.AddNewLine("VoiceConnection not found! Ensure the component is attached to a GameObject."); + return; + } + + m_VoiceConnection.Settings = new AppSettings + { + AppIdVoice = App.Config.PhotonVoiceSecrets.ClientId, + FixedRegion = "", + }; + + + + m_VoiceConnection.Client.AddCallbackTarget(this); + } + + public async Task ConnectToVoiceServer() + { + m_VoiceConnection.Client.UserId = m_Manager.Id; + + if (!m_VoiceConnection.Client.IsConnected) + { + ControllerConsoleScript.m_Instance.AddNewLine("Attempting to connect Voice Server..."); + m_VoiceConnection.ConnectUsingSettings(); + while (!m_VoiceConnection.Client.IsConnected && !m_VoiceConnection.Client.IsConnectedAndReady) + { + ControllerConsoleScript.m_Instance.AddNewLine("Waiting for Voice Connection to establish..."); + await Task.Delay(100); + } + } + + bool connectedAndReady = m_VoiceConnection.Client.IsConnectedAndReady; + if (connectedAndReady) ControllerConsoleScript.m_Instance.AddNewLine("Voice Connection successfully established."); + else ControllerConsoleScript.m_Instance.AddNewLine("Failed to connect to Voice Server."); + return connectedAndReady; + } + + + public async Task JoinRoom(string roomName) + { + if (!m_VoiceConnection.Client.IsConnected) + { + bool connected = await ConnectToVoiceServer(); + if (!connected) + { + return false; + } + } + + while (m_VoiceConnection.ClientState != ClientState.JoinedLobby) + { + ControllerConsoleScript.m_Instance.AddNewLine("Waiting to join lobby..."); + await Task.Delay(100); + } + ControllerConsoleScript.m_Instance.AddNewLine("Joined lobby..."); + + bool roomJoined = m_VoiceConnection.Client.OpJoinOrCreateRoom(new EnterRoomParams + { + RoomName = roomName + }); + + if (roomJoined) ControllerConsoleScript.m_Instance.AddNewLine($"Successfully joined room: {roomName}"); + else ControllerConsoleScript.m_Instance.AddNewLine($"Failed to join or create room: {roomName}"); + + + return roomJoined; + } + + + public void StartSpeaking() + { + m_Recorder = m_VoiceConnection.PrimaryRecorder; + if (m_Recorder == null) + { + ControllerConsoleScript.m_Instance.AddNewLine("Recorder not found! Ensure it's attached to a GameObject."); + return; + } + + // m_Recorder.DebugEchoMode = true; + m_Recorder.TransmitEnabled = true; + } + + public void StopSpeaking() + { + if (m_Recorder != null) + { + m_Recorder.TransmitEnabled = false; + } + } + + + #region MatchmakingCallbacks + + public void OnCreatedRoom() + { + + } + + public void OnCreateRoomFailed(short returnCode, string message) + { + Debug.LogErrorFormat("OnCreateRoomFailed errorCode={0} errorMessage={1}", returnCode, message); + } + + public void OnFriendListUpdate(List friendList) + { + + } + + public void OnJoinedRoom() + { + } + + public void OnJoinRandomFailed(short returnCode, string message) + { + Debug.LogErrorFormat("OnJoinRandomFailed errorCode={0} errorMessage={1}", returnCode, message); + } + + public void OnJoinRoomFailed(short returnCode, string message) + { + Debug.LogErrorFormat("OnJoinRoomFailed errorCode={1} errorMessage={2}", returnCode, message); + } + + public void OnLeftRoom() + { + + } + + #endregion + + #region ConnectionCallbacks + + public void OnConnected() + { + + } + + public void OnConnectedToMaster() + { + m_VoiceConnection.Client.OpJoinOrCreateRoom(new EnterRoomParams + { + RoomName = m_Manager.CurrentRoomName, + }); + } + + public void OnDisconnected(DisconnectCause cause) + { + if (cause == DisconnectCause.None || cause == DisconnectCause.DisconnectByClientLogic || cause == DisconnectCause.ApplicationQuit) + { + return; + } + Debug.LogErrorFormat("OnDisconnected cause={0}", cause); + } + + public void OnRegionListReceived(RegionHandler regionHandler) + { + + } + + public void OnCustomAuthenticationResponse(Dictionary data) + { + + } + + public void OnCustomAuthenticationFailed(string debugMessage) + { + + } + + #endregion + +} +#endif \ No newline at end of file diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonVoiceManager.cs.meta b/Assets/Scripts/Multiplayer/Photon/PhotonVoiceManager.cs.meta new file mode 100644 index 0000000000..c5d392c1bf --- /dev/null +++ b/Assets/Scripts/Multiplayer/Photon/PhotonVoiceManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a6f5caeaa755bc948b340e09bc685673 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From 93ae3a26610197eb51e867d1c5e1e3a4e00b5f08 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Fri, 25 Oct 2024 12:42:05 +0100 Subject: [PATCH 054/174] Update tag icosa-mirror/photon-fusion on build.yml [skip ci] Update tag icosa-mirror/photon-fusion --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 47785da161..bc72ba7666 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -352,7 +352,7 @@ jobs: with: token: ${{ env.PHOTON_PAT }} repository: icosa-mirror/photon-fusion - ref: v1.1.10 + ref: Fusion_v1.1.10_Voice_2 path: photon-fusion-mirror/ - name: Copy photon files From daec52ce361e29e2e5f3567e432954b4198acc77 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Fri, 25 Oct 2024 21:39:04 +0100 Subject: [PATCH 055/174] Trigger CI build From 2a9a33a79d88fd3350982a7a19b7efb18b7dfe36 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Fri, 25 Oct 2024 22:13:57 +0100 Subject: [PATCH 056/174] Update list of symbols Update list of symbols --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index bc72ba7666..6932c71821 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -452,7 +452,7 @@ jobs: - name: Add PHOTON_PAT specific define if: ${{ env.PHOTON_PAT }} run: | - echo -e "\n-define:FUSION_WEAVER" | tee -a Assets/csc.rsp + echo -e "\n -define:PHOTON_UNITY_NETWORKING \n-define:PUN_2_0_OR_NEWER \n-define:PUN_2_OR_NEWER \n-define:PUN_2_19_OR_NEWER \n-define:FUSION_WEAVER \n-define:CROSS_PLATFORM_INPUT \n-define:MOBILE_INPUT \n-define:PHOTON_VOICE_DEFINED" | tee -a Assets/csc.rsp - name: Update build matrix specific defines in csc.rsp if: ${{ matrix.extra_defines }} From ef6c58598b2362303cc2fc78a1ac5a8f1f83cafd Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Sat, 26 Oct 2024 17:14:46 +0100 Subject: [PATCH 057/174] Update MultiplayerManager.cs Check if PhotonVoiceManager get loaded --- Assets/Scripts/Multiplayer/MultiplayerManager.cs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/Assets/Scripts/Multiplayer/MultiplayerManager.cs b/Assets/Scripts/Multiplayer/MultiplayerManager.cs index bce4ea47d4..c4f1f73fe2 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerManager.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerManager.cs @@ -89,9 +89,15 @@ void Start() case MultiplayerType.Photon: #if FUSION_WEAVER m_Manager = new PhotonManager(this); - m_VoiceManager = new PhotonVoiceManager(this); - m_Manager.Disconnected += OnConnectionHandlerDisconnected; -#endif // FUSION_WEAVER + m_Manager.Disconnected += OnConnectionHandlerDisconnected; + if (m_Manager != null) ControllerConsoleScript.m_Instance.AddNewLine("PhotonManager Loaded"); + else ControllerConsoleScript.m_Instance.AddNewLine("PhotonManager Not Loaded"); +#endif +#if PHOTON_UNITY_NETWORKING && PHOTON_VOICE_DEFINED + m_VoiceManager = new PhotonVoiceManager(this); + if (m_Manager != null) ControllerConsoleScript.m_Instance.AddNewLine("PhotonVoiceManager Loaded"); + else ControllerConsoleScript.m_Instance.AddNewLine("PhotonVoiceManager Not Loaded"); +#endif break; default: return; From 15a6992a864a0c727688f69a74331817d7b51549 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Sun, 27 Oct 2024 11:46:12 +0000 Subject: [PATCH 058/174] Trigger CI Build From 69179af0eeaa5b99fddc604b92804c56082716cf Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Sun, 27 Oct 2024 11:49:52 +0000 Subject: [PATCH 059/174] [CI BUILD] Trigger CI Build From 88d92e32371d98a6defa77e2f77c9a5c25924374 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Tue, 29 Oct 2024 09:17:35 +0000 Subject: [PATCH 060/174] Tiding Up Interface Connection Handler --- Assets/Editor/MultiplayerManagerEditor.cs | 150 ++-- Assets/Scripts/GUI/MultiplayerPanel.cs | 512 ++++++------ .../Multiplayer/MultiplayerDataStructs.cs | 147 ++-- .../Multiplayer/MultiplayerInterfaces.cs | 137 +-- .../Scripts/Multiplayer/MultiplayerManager.cs | 672 +++++++-------- .../Multiplayer/Photon/PhotonManager.cs | 784 +++++++++--------- .../Multiplayer/Photon/PhotonVoiceManager.cs | 475 ++++++----- Assets/Scripts/SketchControlsScript.cs | 10 +- 8 files changed, 1516 insertions(+), 1371 deletions(-) diff --git a/Assets/Editor/MultiplayerManagerEditor.cs b/Assets/Editor/MultiplayerManagerEditor.cs index 909d5963ba..a2e2e32611 100644 --- a/Assets/Editor/MultiplayerManagerEditor.cs +++ b/Assets/Editor/MultiplayerManagerEditor.cs @@ -1,61 +1,89 @@ -// MultiplayerManagerInspector.cs -using UnityEditor; -using UnityEngine; -using OpenBrush.Multiplayer; -using System.Threading.Tasks; - -#if UNITY_EDITOR -[CustomEditor(typeof(MultiplayerManager))] -public class MultiplayerManagerInspector : Editor -{ - private MultiplayerManager multiplayerManager; - private string roomName = "1234"; - private bool isPrivate = false; - private int maxPlayers = 4; - private bool voiceDisabled = false; - - public override void OnInspectorGUI() - { - // Get the target object (MultiplayerManager) - multiplayerManager = (MultiplayerManager)target; - - GUILayout.Label("Multiplayer Manager Controls", EditorStyles.boldLabel); - - // Room data input fields - roomName = EditorGUILayout.TextField("Room Name", roomName); - - - if (GUILayout.Button("Connect to Room")) - { - ConnectToRoom(); - } - - // Draw default inspector below - DrawDefaultInspector(); - } - - private async void ConnectToRoom() - { - if (multiplayerManager != null) - { - RoomCreateData roomData = new RoomCreateData - { - roomName = roomName, - @private = isPrivate, - maxPlayers = maxPlayers, - voiceDisabled = voiceDisabled - }; - - bool success = await multiplayerManager.Connect(roomData); - if (success) - { - Debug.Log($"Successfully connected to room: {roomName}"); - } - else - { - Debug.LogError($"Failed to connect to room: {roomName}"); - } - } - } -} -#endif +// MultiplayerManagerInspector.cs +using UnityEditor; +using UnityEngine; +using OpenBrush.Multiplayer; +using System.Threading.Tasks; + +#if UNITY_EDITOR +[CustomEditor(typeof(MultiplayerManager))] +public class MultiplayerManagerInspector : Editor +{ + private MultiplayerManager multiplayerManager; + private string roomName = "1234"; + private bool isPrivate = false; + private int maxPlayers = 4; + private bool voiceDisabled = false; + + public override void OnInspectorGUI() + { + // Get the target object (MultiplayerManager) + multiplayerManager = (MultiplayerManager)target; + + GUILayout.Label("Multiplayer Manager Controls", EditorStyles.boldLabel); + + // Room data input fields + roomName = EditorGUILayout.TextField("Room Name", roomName); + + // Button to join the lobby + if (GUILayout.Button("Join Lobby") && multiplayerManager.State == ConnectionState.INITIALIZED) + { + ConnectToLobby(); + EditorUtility.SetDirty(target); // Mark the object as dirty to recognize state changes + } + + // Button to join the room + if (GUILayout.Button("Join Room") && multiplayerManager.State == ConnectionState.IN_LOBBY) + { + ConnectToRoom(); + EditorUtility.SetDirty(target); // Update inspector on state change + } + + // Button to exit the room + if (GUILayout.Button("Exit Room") && multiplayerManager.State == ConnectionState.IN_ROOM) + { + DisconnectFromRoom(); + EditorUtility.SetDirty(target); // Update inspector on state change + } + + // Force the inspector to repaint to reflect the latest state + Repaint(); + + // Draw default inspector below + DrawDefaultInspector(); + } + + private async void ConnectToLobby() + { + if (multiplayerManager != null) + { + bool success = await multiplayerManager.Connect(); + } + } + + private async void ConnectToRoom() + { + if (multiplayerManager != null) + { + RoomCreateData roomData = new RoomCreateData + { + roomName = roomName, + @private = isPrivate, + maxPlayers = maxPlayers, + voiceDisabled = voiceDisabled + }; + + bool success = await multiplayerManager.JoinRoom(roomData); + + } + } + + private async void DisconnectFromRoom() + { + if (multiplayerManager != null) + { + bool success = await multiplayerManager.LeaveRoom(); + + } + } +} +#endif diff --git a/Assets/Scripts/GUI/MultiplayerPanel.cs b/Assets/Scripts/GUI/MultiplayerPanel.cs index a3cf8834ea..cf8134465b 100644 --- a/Assets/Scripts/GUI/MultiplayerPanel.cs +++ b/Assets/Scripts/GUI/MultiplayerPanel.cs @@ -1,260 +1,252 @@ -// 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 OpenBrush.Multiplayer; -using TMPro; -using UnityEngine; - -namespace TiltBrush -{ - public class MultiplayerPanel : BasePanel - { - [SerializeField] private TextMeshPro m_RoomNumberTextLobby; - [SerializeField] private TextMeshPro m_RoomNumberTextRoomSettings; - [SerializeField] private TextMeshPro m_DoesRoomNumberExist; - [SerializeField] private TextMeshPro m_AlertUserInBeginnerMode; - - - public string RoomName - { - get { return data.roomName; } - set - { - data.roomName = value; - UpdateRoomNumberDisplay(); - UpdateRoomExistenceMessage(); - } - } - - private RoomCreateData data; - - public void Awake() - { - data = new RoomCreateData - { - roomName = GenerateUniqueRoomName(), - @private = false, - maxPlayers = 4, - voiceDisabled = false - }; - - } - - protected override void OnEnablePanel() - { - base.OnEnablePanel(); - - if (m_CurrentMode == Mode.Lobby) - { - UserInBeginnerMode(); - } - } - - private void UserInBeginnerMode() - { - if (m_AlertUserInBeginnerMode) - { - PanelManager panelManager = PanelManager.m_Instance; - bool IsAdavancedModeActive = panelManager.AdvancedModeActive(); - Debug.Log(IsAdavancedModeActive); - m_AlertUserInBeginnerMode.gameObject.SetActive(IsAdavancedModeActive); - } - } - - - private static string GenerateUniqueRoomName() - { - string roomName; - do - { - roomName = GenerateRandomRoomName(); - } while (MultiplayerManager.m_Instance != null && MultiplayerManager.m_Instance.DoesRoomNameExist(roomName)); - - return roomName; - } - - private static string GenerateRandomRoomName() - { - System.Random random = new System.Random(); - return random.Next(100000, 999999).ToString(); - } - - private void UpdateRoomNumberDisplay() - { - if (m_RoomNumberTextLobby) - { - m_RoomNumberTextLobby.text = data.roomName; - } - if (m_RoomNumberTextRoomSettings) - { - m_RoomNumberTextRoomSettings.text = data.roomName; - } - } - - - - public enum Mode - { - Null, - Lobby, - Joined - } - - [SerializeField] private GameObject m_LobbyElements; - [SerializeField] private GameObject m_JoinedElements; - - private Mode m_CurrentMode = Mode.Lobby; - - public override void InitPanel() - { - base.InitPanel(); - - InitMultiplayer(); - UpdateRoomNumberDisplay(); - UserInBeginnerMode(); - } - - public async void InitMultiplayer() - { - bool success = await MultiplayerManager.m_Instance.Init(); - MultiplayerManager.m_Instance.Disconnected += OnDisconnected; - } - - private void OnDisconnected() - { - UpdateMode(Mode.Lobby); - } - - private async void JoinRoom() - { - - if (MultiplayerManager.m_Instance != null) - { - - bool success = await MultiplayerManager.m_Instance.Connect(data); - - if (success) - { - Debug.Log("Connected to room successfully."); - - // Additional UI updates or feedback - UpdateMode(Mode.Joined); - UpdateRoomNumberDisplay(); // Update room number display after joining - } - else - { - Debug.LogError("Failed to connect to room."); - - // Provide user feedback with some UI element - } - - } - } - - private async void LeaveRoom() - { - if (MultiplayerManager.m_Instance != null) - { - - bool success = await MultiplayerManager.m_Instance.Disconnect(false); - - if (success) - { - Debug.Log("Left room successfully."); - - // Additional UI updates or feedback - UpdateMode(Mode.Lobby); - UpdateRoomNumberDisplay(); // Update room number display after joining - } - else - { - Debug.LogError("Failed to leave to room."); - // Provide user feedback with some UI element - } - - } - } - - private void UpdateRoomExistenceMessage() - { - if (m_RoomNumberTextLobby) return; - - if (MultiplayerManager.m_Instance != null && m_DoesRoomNumberExist != null) - { - if (MultiplayerManager.m_Instance.DoesRoomNameExist(data.roomName)) - { - m_DoesRoomNumberExist.text = "This room exists. You will be joining an active session. You can change the room number by pressing edit."; - } - else - { - m_DoesRoomNumberExist.text = "This room does not exist yet. By pressing join, the room will be created."; - } - } - } - - private void UpdateMode(Mode newMode) - { - m_CurrentMode = newMode; - m_LobbyElements.SetActive(m_CurrentMode == Mode.Lobby); - m_JoinedElements.SetActive(m_CurrentMode == Mode.Joined); - - // Update room number display if switching to a mode that shows it - if (m_CurrentMode == Mode.Lobby || m_CurrentMode == Mode.Joined) - { - UpdateRoomNumberDisplay(); - } - - if (m_CurrentMode == Mode.Lobby) - { - UserInBeginnerMode(); - } - } - - private void RefreshObjects() - { - - } - - - // This function serves as a callback from ProfilePopUpButtons that want to - // change the mode of the popup on click. - public void OnMultiplayerPanelButtonPressed(MultiplayerPanelButton button) - { - switch (button.m_Command) - { - // Identifier for signaling we understand the info message. - case SketchControlsScript.GlobalCommands.Null: - UpdateMode(Mode.Lobby); - RefreshObjects(); - break; - case SketchControlsScript.GlobalCommands.MultiplayerPanelOptions: - switch ((Mode)button.m_CommandParam) - { - case Mode.Lobby: - UpdateMode(Mode.Lobby); - break; - default: - break; - } - break; - case SketchControlsScript.GlobalCommands.MultiplayerJoinRoom: - JoinRoom(); - break; - case SketchControlsScript.GlobalCommands.MultiplayerLeaveRoom: - LeaveRoom(); - break; - } - } - } -} // namespace TiltBrush +// 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 OpenBrush.Multiplayer; +using TMPro; +using UnityEngine; + +namespace TiltBrush +{ + public class MultiplayerPanel : BasePanel + { + [SerializeField] private TextMeshPro m_RoomNumberTextLobby; + [SerializeField] private TextMeshPro m_RoomNumberTextRoomSettings; + [SerializeField] private TextMeshPro m_DoesRoomNumberExist; + [SerializeField] private TextMeshPro m_AlertUserInBeginnerMode; + + + public string RoomName + { + get { return data.roomName; } + set + { + data.roomName = value; + UpdateRoomNumberDisplay(); + UpdateRoomExistenceMessage(); + } + } + + private RoomCreateData data; + + public void Awake() + { + data = new RoomCreateData + { + roomName = GenerateUniqueRoomName(), + @private = false, + maxPlayers = 4, + voiceDisabled = false + }; + + } + + protected override void OnEnablePanel() + { + base.OnEnablePanel(); + + if (m_CurrentMode == Mode.Lobby) + { + UserInBeginnerMode(); + } + } + + private void UserInBeginnerMode() + { + if (m_AlertUserInBeginnerMode) + { + PanelManager panelManager = PanelManager.m_Instance; + bool IsAdavancedModeActive = panelManager.AdvancedModeActive(); + Debug.Log(IsAdavancedModeActive); + m_AlertUserInBeginnerMode.gameObject.SetActive(IsAdavancedModeActive); + } + } + + private static string GenerateUniqueRoomName() + { + string roomName; + do + { + roomName = GenerateRandomRoomName(); + } while (MultiplayerManager.m_Instance != null && MultiplayerManager.m_Instance.DoesRoomNameExist(roomName)); + + return roomName; + } + + private static string GenerateRandomRoomName() + { + System.Random random = new System.Random(); + return random.Next(100000, 999999).ToString(); + } + + private void UpdateRoomNumberDisplay() + { + if (m_RoomNumberTextLobby) + { + m_RoomNumberTextLobby.text = data.roomName; + } + if (m_RoomNumberTextRoomSettings) + { + m_RoomNumberTextRoomSettings.text = data.roomName; + } + } + + public enum Mode + { + Null, + Lobby, + Joined + } + + [SerializeField] private GameObject m_LobbyElements; + [SerializeField] private GameObject m_JoinedElements; + + private Mode m_CurrentMode = Mode.Lobby; + + public override void InitPanel() + { + base.InitPanel(); + + InitMultiplayer(); + UpdateRoomNumberDisplay(); + UserInBeginnerMode(); + } + + public async void InitMultiplayer() + { + await MultiplayerManager.m_Instance.Connect(); + MultiplayerManager.m_Instance.Disconnected += OnDisconnected; + } + + private void OnDisconnected() + { + UpdateMode(Mode.Lobby); + } + + private async void JoinRoom() + { + + if (MultiplayerManager.m_Instance != null) + { + + bool success = await MultiplayerManager.m_Instance.JoinRoom(data); + + if (success) + { + + // Additional UI updates or feedback + UpdateMode(Mode.Joined); + UpdateRoomNumberDisplay(); // Update room number display after joining + } + else + { + // Provide user feedback with some UI element + } + + } + } + + private async void LeaveRoom() + { + if (MultiplayerManager.m_Instance != null) + { + + bool success = await MultiplayerManager.m_Instance.LeaveRoom(false); + + if (success) + { + + // Additional UI updates or feedback + UpdateMode(Mode.Lobby); + UpdateRoomNumberDisplay(); // Update room number display after joining + } + else + { + // Provide user feedback with some UI element + } + + } + } + + private void UpdateRoomExistenceMessage() + { + if (m_RoomNumberTextLobby) return; + + if (MultiplayerManager.m_Instance != null && m_DoesRoomNumberExist != null) + { + if (MultiplayerManager.m_Instance.DoesRoomNameExist(data.roomName)) + { + m_DoesRoomNumberExist.text = "This room exists. You will be joining an active session. You can change the room number by pressing edit."; + } + else + { + m_DoesRoomNumberExist.text = "This room does not exist yet. By pressing join, the room will be created."; + } + } + } + + private void UpdateMode(Mode newMode) + { + m_CurrentMode = newMode; + m_LobbyElements.SetActive(m_CurrentMode == Mode.Lobby); + m_JoinedElements.SetActive(m_CurrentMode == Mode.Joined); + + // Update room number display if switching to a mode that shows it + if (m_CurrentMode == Mode.Lobby || m_CurrentMode == Mode.Joined) + { + UpdateRoomNumberDisplay(); + } + + if (m_CurrentMode == Mode.Lobby) + { + UserInBeginnerMode(); + } + } + + private void RefreshObjects() + { + + } + + + // This function serves as a callback from ProfilePopUpButtons that want to + // change the mode of the popup on click. + public void OnMultiplayerPanelButtonPressed(MultiplayerPanelButton button) + { + switch (button.m_Command) + { + // Identifier for signaling we understand the info message. + case SketchControlsScript.GlobalCommands.Null: + UpdateMode(Mode.Lobby); + RefreshObjects(); + break; + case SketchControlsScript.GlobalCommands.MultiplayerPanelOptions: + switch ((Mode)button.m_CommandParam) + { + case Mode.Lobby: + UpdateMode(Mode.Lobby); + break; + default: + break; + } + break; + case SketchControlsScript.GlobalCommands.MultiplayerJoinRoom: + JoinRoom(); + break; + case SketchControlsScript.GlobalCommands.MultiplayerLeaveRoom: + LeaveRoom(); + break; + } + } + } +} // namespace TiltBrush diff --git a/Assets/Scripts/Multiplayer/MultiplayerDataStructs.cs b/Assets/Scripts/Multiplayer/MultiplayerDataStructs.cs index ab59b3bda5..7561ec2014 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerDataStructs.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerDataStructs.cs @@ -1,69 +1,78 @@ -// 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; - } - - [System.Serializable] - public struct RoomCreateData - { - public string roomName; - public string roomPassword; - public bool @private; - public int maxPlayers; - public bool voiceDisabled; - } - - [System.Serializable] - public struct RoomData - { - public string roomName; - public bool @private; - public int numPlayers; - public int maxPlayers; - public bool voiceDisabled; - } -} +// 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; + } + + [System.Serializable] + public struct RoomCreateData + { + public string roomName; + public string roomPassword; + public bool @private; + public int maxPlayers; + public bool voiceDisabled; + } + + [System.Serializable] + public struct RoomData + { + public string roomName; + public bool @private; + public int numPlayers; + public int maxPlayers; + public bool voiceDisabled; + } + + + [System.Serializable] + public struct ConnectionUserInfo + { + public string UserId; + public string Nickname; + public string Role; + } +} diff --git a/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs b/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs index 30350e2549..21331fc836 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs @@ -1,64 +1,73 @@ -// 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 Init(); - Task Connect(RoomCreateData data); - - bool IsConnected(); - bool IsInRoom(); - - Task Disconnect(bool force = false); - - void Update(); - - Task PerformCommand(BaseCommand command); - Task UndoCommand(BaseCommand command); - Task RedoCommand(BaseCommand command); - Task RpcSyncToSharedAnchor(string uuid); - - event Action Disconnected; - - string Id { get; } - - - //ITransientData SpawnPlayer(); - } - - public interface IVoiceManager - { - void InitializeVoice(); - Task ConnectToVoiceServer(); - Task JoinRoom(string roomName); - void StartSpeaking(); - void StopSpeaking(); - - } - - public interface ITransientData - { - int PlayerId { get; set; } - void TransmitData(T data); - T RecieveData(); - } -} +// 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.Threading.Tasks; +using TiltBrush; + +namespace OpenBrush.Multiplayer +{ + public interface IConnectionHandler + { + Task Connect(); + Task JoinRoom(RoomCreateData data); + Task LeaveRoom(bool force = false); + ConnectionState State { get; } + ConnectionUserInfo UserInfo { get; set; } + } + + public interface IDataConnectionHandler : IConnectionHandler + { + + void Update(); + + Task PerformCommand(BaseCommand command); + Task UndoCommand(BaseCommand command); + Task RedoCommand(BaseCommand command); + Task RpcSyncToSharedAnchor(string uuid); + + event Action Disconnected; + + } + + public interface IVoiceConnectionHandler : IConnectionHandler + { + + bool StartSpeaking(); + bool StopSpeaking(); + + } + + public enum ConnectionState + { + INITIALISING = 0, + INITIALIZED = 1, + DISCONNECTED = 2, + DISCONNECTING = 3, + CONNECTING = 4, + AUTHENTICATING = 5, + IN_LOBBY = 6, + JOINING_ROOM = 7, + IN_ROOM = 8, + RECONNECTING = 9, + ERROR = 10, + } + + public interface ITransientData + { + int PlayerId { get; set; } + void TransmitData(T data); + T RecieveData(); + } +} diff --git a/Assets/Scripts/Multiplayer/MultiplayerManager.cs b/Assets/Scripts/Multiplayer/MultiplayerManager.cs index c4f1f73fe2..a4a934ba7f 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerManager.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerManager.cs @@ -1,334 +1,348 @@ -// 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 System.Linq; -using System.Threading.Tasks; -using UnityEngine; -using Unity.XR.CoreUtils; -#if OCULUS_SUPPORTED -using OVRPlatform = Oculus.Platform; -#endif -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; - public event Action Disconnected; - - private IConnectionHandler m_Manager; - private IVoiceManager m_VoiceManager; - - private ITransientData m_LocalPlayer; - private List> m_RemotePlayers; - - public Action> localPlayerJoined; - public Action> remotePlayerJoined; - public Action playerLeft; - public Action> roomDataRefreshed; - private List m_RoomData = new List(); - - ulong myOculusUserId; - - List oculusPlayerIds; - internal string UserId; - public string CurrentRoomName; - - public bool IsConnected { get { return m_Manager != null && m_Manager.IsConnected(); } } - public bool IsInRoom { get { return m_Manager != null && m_Manager.IsInRoom(); } } - - public string Id { get { return m_Manager.Id; } } - - 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); +// 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 System.Linq; +using System.Threading.Tasks; +using UnityEngine; +using Unity.XR.CoreUtils; +#if OCULUS_SUPPORTED +using OVRPlatform = Oculus.Platform; +#endif +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; + public event Action Disconnected; + + private IDataConnectionHandler m_Manager; + private IVoiceConnectionHandler m_VoiceManager; + + private ITransientData m_LocalPlayer; + private List> m_RemotePlayers; + + public Action> localPlayerJoined; + public Action> remotePlayerJoined; + public Action playerLeft; + public Action> roomDataRefreshed; + private List m_RoomData = new List(); + + ulong myOculusUserId; + + List oculusPlayerIds; + internal string UserId; + [HideInInspector] public string CurrentRoomName; + + public ConnectionState State => m_Manager?.State ?? ConnectionState.DISCONNECTED; + + public ConnectionUserInfo UserInfo + { + get => m_Manager?.UserInfo ?? default; + set + { + if (m_Manager != null) + { + m_Manager.UserInfo = value; + } + } + } + + 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); m_Manager.Disconnected += OnConnectionHandlerDisconnected; if (m_Manager != null) ControllerConsoleScript.m_Instance.AddNewLine("PhotonManager Loaded"); else ControllerConsoleScript.m_Instance.AddNewLine("PhotonManager Not Loaded"); -#endif -#if PHOTON_UNITY_NETWORKING && PHOTON_VOICE_DEFINED +#endif +#if PHOTON_UNITY_NETWORKING && PHOTON_VOICE_DEFINED m_VoiceManager = new PhotonVoiceManager(this); if (m_Manager != null) ControllerConsoleScript.m_Instance.AddNewLine("PhotonVoiceManager Loaded"); - else ControllerConsoleScript.m_Instance.AddNewLine("PhotonVoiceManager Not Loaded"); -#endif - break; - default: - return; - } - - localPlayerJoined += OnLocalPlayerJoined; - remotePlayerJoined += OnRemotePlayerJoined; - playerLeft += OnPlayerLeft; - SketchMemoryScript.m_Instance.CommandPerformed += OnCommandPerformed; - SketchMemoryScript.m_Instance.CommandUndo += OnCommandUndo; - SketchMemoryScript.m_Instance.CommandRedo += OnCommandRedo; - } - - void OnDestroy() - { - localPlayerJoined -= OnLocalPlayerJoined; - remotePlayerJoined -= OnRemotePlayerJoined; - playerLeft -= OnPlayerLeft; - SketchMemoryScript.m_Instance.CommandPerformed -= OnCommandPerformed; - SketchMemoryScript.m_Instance.CommandUndo -= OnCommandUndo; - SketchMemoryScript.m_Instance.CommandRedo -= OnCommandRedo; - } - - public async Task Init() - { - var success = false; - if (m_Manager != null) - { - success = await m_Manager.Init(); - } - return success; - } - - public async Task Connect(RoomCreateData data) - { - bool success = await m_Manager.Connect(data); - if (success) - { - CurrentRoomName = data.roomName; - StartSpeaking(); - } - - return success; - } - - public bool DoesRoomNameExist(string roomName) - { - return m_RoomData.Any(room => room.roomName == roomName); - } - - void OnRoomDataRefreshed(List rooms) - { - m_RoomData = rooms; - } - - 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(); -#if OCULUS_SUPPORTED - // 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; - } -#endif // OCULUS_SUPPORTED - } - - if (newUser) - { - ShareAnchors(); - } - } - - void OnLocalPlayerJoined(int id, ITransientData playerData) - { - m_LocalPlayer = playerData; - } - - void OnRemotePlayerJoined(int id, ITransientData playerData) - { - Debug.Log("Adding new player to track."); - playerData.PlayerId = id; - m_RemotePlayers.Add(playerData); - } - - void OnPlayerLeft(int id) - { - if (m_LocalPlayer.PlayerId == id) - { - m_LocalPlayer = null; - Debug.Log("Possible to get here!"); - return; - } - var copy = m_RemotePlayers.ToList(); - foreach (var player in copy) - { - if (player.PlayerId == id) - { - m_RemotePlayers.Remove(player); - } - } - } - - 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() - { -#if OCULUS_SUPPORTED - 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); - } - } -#endif // OCULUS_SUPPORTED - } - - public async Task Disconnect(bool force = false) - { - if (m_Manager != null) - { - var success = await m_Manager.Disconnect(force); - if (success) StopSpeaking(); - return success; - } - return true; - } - - private void OnConnectionHandlerDisconnected() - { - // Clean up local player reference - m_LocalPlayer = null; - - // Invoke the Disconnected event - Disconnected?.Invoke(); - } - - public void JoinVoiceRoom(RoomCreateData data) - { - m_VoiceManager?.JoinRoom(data.roomName); - } - - public void StartSpeaking() - { - m_VoiceManager?.StartSpeaking(); - } - - public void StopSpeaking() - { - m_VoiceManager?.StopSpeaking(); - } - - } -} + else ControllerConsoleScript.m_Instance.AddNewLine("PhotonVoiceManager Not Loaded"); +#endif + break; + default: + return; + } + + localPlayerJoined += OnLocalPlayerJoined; + remotePlayerJoined += OnRemotePlayerJoined; + playerLeft += OnPlayerLeft; + SketchMemoryScript.m_Instance.CommandPerformed += OnCommandPerformed; + SketchMemoryScript.m_Instance.CommandUndo += OnCommandUndo; + SketchMemoryScript.m_Instance.CommandRedo += OnCommandRedo; + } + + void OnDestroy() + { + localPlayerJoined -= OnLocalPlayerJoined; + remotePlayerJoined -= OnRemotePlayerJoined; + playerLeft -= OnPlayerLeft; + SketchMemoryScript.m_Instance.CommandPerformed -= OnCommandPerformed; + SketchMemoryScript.m_Instance.CommandUndo -= OnCommandUndo; + SketchMemoryScript.m_Instance.CommandRedo -= OnCommandRedo; + } + + public async Task Connect() + { + var successData = false; + if (m_Manager != null) + { + successData = await m_Manager.Connect(); + } + var successVoice = false; + if (m_VoiceManager != null) + { + successVoice = await m_VoiceManager.Connect(); + } + return successData & successVoice; + } + + public async Task JoinRoom(RoomCreateData RoomData) + { + // attempt to connect multiplayer backend + bool success = await m_Manager.JoinRoom(RoomData); + if (!success) return success; + + // attempt to connect voice backend OPTIONAL + m_VoiceManager?.JoinRoom(RoomData); + m_VoiceManager?.StartSpeaking(); + + return success; + } + + public async Task LeaveRoom(bool force = false) + { + if (m_Manager != null) + { + var success = await m_Manager.LeaveRoom(force); + if (success) + { + m_VoiceManager?.LeaveRoom(); + StopSpeaking(); + } + return success; + } + return true; + } + + public bool DoesRoomNameExist(string roomName) + { + return m_RoomData.Any(room => room.roomName == roomName); + } + + void OnRoomDataRefreshed(List rooms) + { + m_RoomData = rooms; + } + + 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(); +#if OCULUS_SUPPORTED + // 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; + } +#endif // OCULUS_SUPPORTED + } + + if (newUser) + { + ShareAnchors(); + } + } + + void OnLocalPlayerJoined(int id, ITransientData playerData) + { + m_LocalPlayer = playerData; + } + + void OnRemotePlayerJoined(int id, ITransientData playerData) + { + Debug.Log("Adding new player to track."); + playerData.PlayerId = id; + m_RemotePlayers.Add(playerData); + } + + void OnPlayerLeft(int id) + { + if (m_LocalPlayer.PlayerId == id) + { + m_LocalPlayer = null; + Debug.Log("Possible to get here!"); + return; + } + var copy = m_RemotePlayers.ToList(); + foreach (var player in copy) + { + if (player.PlayerId == id) + { + m_RemotePlayers.Remove(player); + } + } + } + + private async void OnCommandPerformed(BaseCommand command) + { + if (State == ConnectionState.IN_ROOM) + { + 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 (State == ConnectionState.IN_ROOM) + { + m_Manager.UndoCommand(command); + } + } + + private void OnCommandRedo(BaseCommand command) + { + if (State == ConnectionState.IN_ROOM) + { + m_Manager.RedoCommand(command); + } + } + + async void ShareAnchors() + { +#if OCULUS_SUPPORTED + 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); + } + } +#endif // OCULUS_SUPPORTED + } + + private void OnConnectionHandlerDisconnected() + { + // Clean up local player reference + m_LocalPlayer = null; + + // Invoke the Disconnected event + Disconnected?.Invoke(); + } + + public void StartSpeaking() + { + m_VoiceManager?.StartSpeaking(); + } + + public void StopSpeaking() + { + m_VoiceManager?.StopSpeaking(); + } + + } +} diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs index a5fd786dd8..65bd747fe4 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs @@ -1,385 +1,399 @@ -// 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.Linq; -using System.Threading.Tasks; -using UnityEngine; -using Fusion; -using Fusion.Photon.Realtime; -using Fusion.Sockets; -using TiltBrush; - -namespace OpenBrush.Multiplayer -{ - public class PhotonManager : IConnectionHandler, INetworkRunnerCallbacks - { - private NetworkRunner m_Runner; - - private MultiplayerManager m_Manager; - - private List m_PlayersSpawning; - - private PhotonPlayerRig m_LocalPlayer; - - private AppSettings m_PhotonAppSettings; - - public event Action Disconnected; - - public string Id { get { return m_Runner.UserId; } } - - public PhotonManager(MultiplayerManager manager) - { - m_Manager = manager; - m_PlayersSpawning = new List(); - - InitializeRunner(); - - m_PhotonAppSettings = new AppSettings - { - AppIdFusion = App.Config.PhotonFusionSecrets.ClientId, - // Need this set for some reason - FixedRegion = "", - }; - } - - private void InitializeRunner() - { - var runnerGO = new GameObject("Photon Network Components"); - m_Runner = runnerGO.AddComponent(); - m_Runner.gameObject.AddComponent(); - m_Runner.ProvideInput = true; - m_Runner.AddCallbacks(this); - ControllerConsoleScript.m_Instance.AddNewLine("Runner Initialized"); - } - - public async Task Init() - { - await Task.Yield(); - //return true; - var result = await m_Runner.JoinSessionLobby(SessionLobby.Shared, customAppSettings: m_PhotonAppSettings); - - if (result.Ok) - { - ControllerConsoleScript.m_Instance.AddNewLine("Connected to lobby"); - } - else - { - ControllerConsoleScript.m_Instance.AddNewLine("Failed to join lobby!"); - } - - return result.Ok; - } - - public async Task Connect(RoomCreateData roomCreateData) - { - if (m_Runner == null) - { - InitializeRunner(); - } - - var args = new StartGameArgs() - { - GameMode = GameMode.Shared, - SessionName = roomCreateData.roomName, - CustomPhotonAppSettings = m_PhotonAppSettings, - PlayerCount = roomCreateData.maxPlayers != 0 ? roomCreateData.maxPlayers : null, - SceneManager = m_Runner.gameObject.GetComponent(), - Scene = UnityEngine.SceneManagement.SceneManager.GetActiveScene().buildIndex, - }; - - var result = await m_Runner.StartGame(args); - - if (result.Ok) - { - ControllerConsoleScript.m_Instance.AddNewLine("Joined Room"); - m_Manager.JoinVoiceRoom(roomCreateData); // i'd like to solve this in a different way - } - else - { - ControllerConsoleScript.m_Instance.AddNewLine("Failed to join Room!"); - } - - return result.Ok; - - } - - public bool IsConnected() - { - if(m_Runner == null) - { - return false; - } - return m_Runner.IsRunning; - } - - public bool IsInRoom() - { - if (m_Runner == null) - { - return false; - } - return m_Runner.IsInSession; - } - - - public async Task Disconnect(bool force) - { - if(m_Runner != null) - { - - if (m_LocalPlayer != null) - { - m_Runner.Despawn(m_LocalPlayer.Object); - m_LocalPlayer = null; - } - - await m_Runner.Shutdown(forceShutdownProcedure: force); - GameObject.Destroy(m_Runner.gameObject); - 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(player.PlayerId, 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 SwitchEnvironmentCommand: - success = CommandSwitchEnvironment(command as SwitchEnvironmentCommand); - 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; - int maxPointsPerChunk = NetworkingConstants.MaxControlPointsPerChunk; - - - if (stroke.m_ControlPoints.Length > maxPointsPerChunk) - { - // Split and Send - int numSplits = stroke.m_ControlPoints.Length / maxPointsPerChunk; - - var firstStroke = new Stroke(stroke) - { - m_ControlPoints = stroke.m_ControlPoints.Take(maxPointsPerChunk).ToArray(), - m_ControlPointsToDrop = stroke.m_ControlPointsToDrop.Take(maxPointsPerChunk).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* maxPointsPerChunk).Take(maxPointsPerChunk).ToArray(); - var dropPoints = stroke.m_ControlPointsToDrop.Skip(rounds* maxPointsPerChunk).Take(maxPointsPerChunk).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 * maxPointsPerChunk, 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; - } - - private bool CommandSwitchEnvironment(SwitchEnvironmentCommand command) - { - Guid environmentGuid = command.m_NextEnvironment.m_Guid; - PhotonRPC.RPC_SwitchEnvironment(m_Runner, environmentGuid, 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) - { - Debug.Log($"OnPlayerJoined called. PlayerRef: {player.PlayerId}"); - - try - { - - 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(player.PlayerId, m_LocalPlayer); - } - else - { - m_PlayersSpawning.Add(player); - } - } - catch (Exception ex) - { - Debug.LogError($"Exception in OnPlayerJoined: {ex.Message}"); - } - } - - public void OnPlayerLeft(NetworkRunner runner, PlayerRef player) - { - m_Manager.playerLeft?.Invoke(player.PlayerId); - } - - public void OnSessionListUpdated(NetworkRunner runner, List sessionList) - { - var roomData = new List(); - foreach (var session in sessionList) - { - RoomData data = new RoomData() - { - roomName = session.Name, - @private = session.IsOpen, - numPlayers = session.PlayerCount, - maxPlayers = session.MaxPlayers - }; - - roomData.Add(data); - } - - m_Manager.roomDataRefreshed?.Invoke(roomData); - } -#endregion - -#region Unused Photon Callbacks - public void OnShutdown(NetworkRunner runner, ShutdownReason shutdownReason) { - Disconnected?.Invoke(); - } - 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 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 +// 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.Linq; +using System.Threading.Tasks; +using UnityEngine; +using Fusion; +using Fusion.Photon.Realtime; +using Fusion.Sockets; +using TiltBrush; + + +namespace OpenBrush.Multiplayer +{ + public class PhotonManager : IDataConnectionHandler, INetworkRunnerCallbacks + { + + private NetworkRunner m_Runner; + + private MultiplayerManager m_Manager; + + private List m_PlayersSpawning; + + private PhotonPlayerRig m_LocalPlayer; + + private AppSettings m_PhotonAppSettings; + + + public event Action Disconnected; + + public ConnectionUserInfo UserInfo { get; set; } + public ConnectionState State { get; private set; } + public string LastError { get; private set; } + + + public PhotonManager(MultiplayerManager manager) + { + m_Manager = manager; + m_PlayersSpawning = new List(); + + Init(); + + m_PhotonAppSettings = new AppSettings + { + AppIdFusion = App.Config.PhotonFusionSecrets.ClientId, + FixedRegion = "", + }; + } + + public async Task Init() + { + try + { + State = ConnectionState.INITIALISING; + var runnerGO = new GameObject("Photon Network Components"); + m_Runner = runnerGO.AddComponent(); + m_Runner.gameObject.AddComponent(); + m_Runner.ProvideInput = true; + m_Runner.AddCallbacks(this); + + } + catch (Exception ex) + { + State = ConnectionState.ERROR; + LastError = $"Failed to Initialize lobby: {ex.Message}"; + ControllerConsoleScript.m_Instance.AddNewLine(LastError); + return false; + } + + ControllerConsoleScript.m_Instance.AddNewLine("Runner Initialized"); + State = ConnectionState.INITIALIZED; + return true; + } + + public async Task Connect() + { + State = ConnectionState.CONNECTING; + + await Task.Yield(); + //return true; + var result = await m_Runner.JoinSessionLobby(SessionLobby.Shared, customAppSettings: m_PhotonAppSettings); + + if (result.Ok) + { + State = ConnectionState.IN_LOBBY; + ControllerConsoleScript.m_Instance.AddNewLine("Connected to lobby"); + } + else + { + State = ConnectionState.ERROR; + LastError = $"Failed to join lobby: {result.ErrorMessage}"; + ControllerConsoleScript.m_Instance.AddNewLine(LastError); + } + + return result.Ok; + } + + public async Task JoinRoom(RoomCreateData roomCreateData) + { + + if (m_Runner == null) Init(); + + State = ConnectionState.JOINING_ROOM; + + var args = new StartGameArgs() + { + GameMode = GameMode.Shared, + SessionName = roomCreateData.roomName, + CustomPhotonAppSettings = m_PhotonAppSettings, + PlayerCount = roomCreateData.maxPlayers != 0 ? roomCreateData.maxPlayers : null, + SceneManager = m_Runner.gameObject.GetComponent(), + Scene = UnityEngine.SceneManagement.SceneManager.GetActiveScene().buildIndex, + }; + + var result = await m_Runner.StartGame(args); + + if (result.Ok) + { + State = ConnectionState.IN_ROOM; + ControllerConsoleScript.m_Instance.AddNewLine("Joined Room"); + UserInfo = new ConnectionUserInfo { UserId = m_Runner.UserId }; + } + else + { + State = ConnectionState.ERROR; + LastError = $"Failed to join Room: {result.ErrorMessage}"; + ControllerConsoleScript.m_Instance.AddNewLine(LastError); + } + + return result.Ok; + + } + + public async Task LeaveRoom(bool force) + { + State = ConnectionState.DISCONNECTING; + + if (m_Runner != null) + { + + if (m_LocalPlayer != null) + { + m_Runner.Despawn(m_LocalPlayer.Object); + m_LocalPlayer = null; + } + + await m_Runner.Shutdown(forceShutdownProcedure: force); + GameObject.Destroy(m_Runner.gameObject); + + if(m_Runner.IsShutdown) State = ConnectionState.DISCONNECTED; + + 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(player.PlayerId, 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 SwitchEnvironmentCommand: + success = CommandSwitchEnvironment(command as SwitchEnvironmentCommand); + 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; + int maxPointsPerChunk = NetworkingConstants.MaxControlPointsPerChunk; + + + if (stroke.m_ControlPoints.Length > maxPointsPerChunk) + { + // Split and Send + int numSplits = stroke.m_ControlPoints.Length / maxPointsPerChunk; + + var firstStroke = new Stroke(stroke) + { + m_ControlPoints = stroke.m_ControlPoints.Take(maxPointsPerChunk).ToArray(), + m_ControlPointsToDrop = stroke.m_ControlPointsToDrop.Take(maxPointsPerChunk).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* maxPointsPerChunk).Take(maxPointsPerChunk).ToArray(); + var dropPoints = stroke.m_ControlPointsToDrop.Skip(rounds* maxPointsPerChunk).Take(maxPointsPerChunk).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 * maxPointsPerChunk, 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; + } + + private bool CommandSwitchEnvironment(SwitchEnvironmentCommand command) + { + Guid environmentGuid = command.m_NextEnvironment.m_Guid; + PhotonRPC.RPC_SwitchEnvironment(m_Runner, environmentGuid, 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) + { + Debug.Log($"OnPlayerJoined called. PlayerRef: {player.PlayerId}"); + + try + { + + 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(player.PlayerId, m_LocalPlayer); + } + else + { + m_PlayersSpawning.Add(player); + } + } + catch (Exception ex) + { + Debug.LogError($"Exception in OnPlayerJoined: {ex.Message}"); + } + } + + public void OnPlayerLeft(NetworkRunner runner, PlayerRef player) + { + m_Manager.playerLeft?.Invoke(player.PlayerId); + } + + public void OnSessionListUpdated(NetworkRunner runner, List sessionList) + { + var roomData = new List(); + foreach (var session in sessionList) + { + RoomData data = new RoomData() + { + roomName = session.Name, + @private = session.IsOpen, + numPlayers = session.PlayerCount, + maxPlayers = session.MaxPlayers + }; + + roomData.Add(data); + } + + m_Manager.roomDataRefreshed?.Invoke(roomData); + } +#endregion + +#region Unused Photon Callbacks + public void OnShutdown(NetworkRunner runner, ShutdownReason shutdownReason) { + Disconnected?.Invoke(); + } + 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 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/PhotonVoiceManager.cs b/Assets/Scripts/Multiplayer/Photon/PhotonVoiceManager.cs index ccf6638b8f..1ae27cb542 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonVoiceManager.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonVoiceManager.cs @@ -1,199 +1,278 @@ -#if PHOTON_UNITY_NETWORKING && PHOTON_VOICE_DEFINED -using OpenBrush.Multiplayer; -using Photon.Realtime; -using Photon.Voice.Unity; -using System.Collections.Generic; -using System.Threading.Tasks; -using TiltBrush; -using UnityEngine; - -public class PhotonVoiceManager : IVoiceManager, IConnectionCallbacks, IMatchmakingCallbacks -{ - private VoiceConnection m_VoiceConnection; - private MultiplayerManager m_Manager; - private AppSettings m_PhotonVoiceAppSettings; - private Recorder m_Recorder; - - public PhotonVoiceManager(MultiplayerManager manager) - { - m_Manager = manager; - InitializeVoice(); - - } - - public void InitializeVoice() - { - m_VoiceConnection = GameObject.FindFirstObjectByType(); - if (m_VoiceConnection == null) - { - ControllerConsoleScript.m_Instance.AddNewLine("VoiceConnection not found! Ensure the component is attached to a GameObject."); - return; - } - - m_VoiceConnection.Settings = new AppSettings - { - AppIdVoice = App.Config.PhotonVoiceSecrets.ClientId, - FixedRegion = "", - }; - - - - m_VoiceConnection.Client.AddCallbackTarget(this); - } - - public async Task ConnectToVoiceServer() - { - m_VoiceConnection.Client.UserId = m_Manager.Id; - - if (!m_VoiceConnection.Client.IsConnected) - { - ControllerConsoleScript.m_Instance.AddNewLine("Attempting to connect Voice Server..."); - m_VoiceConnection.ConnectUsingSettings(); - while (!m_VoiceConnection.Client.IsConnected && !m_VoiceConnection.Client.IsConnectedAndReady) - { - ControllerConsoleScript.m_Instance.AddNewLine("Waiting for Voice Connection to establish..."); - await Task.Delay(100); - } - } - - bool connectedAndReady = m_VoiceConnection.Client.IsConnectedAndReady; - if (connectedAndReady) ControllerConsoleScript.m_Instance.AddNewLine("Voice Connection successfully established."); - else ControllerConsoleScript.m_Instance.AddNewLine("Failed to connect to Voice Server."); - return connectedAndReady; - } - - - public async Task JoinRoom(string roomName) - { - if (!m_VoiceConnection.Client.IsConnected) - { - bool connected = await ConnectToVoiceServer(); - if (!connected) - { - return false; - } - } - - while (m_VoiceConnection.ClientState != ClientState.JoinedLobby) - { - ControllerConsoleScript.m_Instance.AddNewLine("Waiting to join lobby..."); - await Task.Delay(100); - } - ControllerConsoleScript.m_Instance.AddNewLine("Joined lobby..."); - - bool roomJoined = m_VoiceConnection.Client.OpJoinOrCreateRoom(new EnterRoomParams - { - RoomName = roomName - }); - - if (roomJoined) ControllerConsoleScript.m_Instance.AddNewLine($"Successfully joined room: {roomName}"); - else ControllerConsoleScript.m_Instance.AddNewLine($"Failed to join or create room: {roomName}"); - - - return roomJoined; - } - - - public void StartSpeaking() - { - m_Recorder = m_VoiceConnection.PrimaryRecorder; - if (m_Recorder == null) - { - ControllerConsoleScript.m_Instance.AddNewLine("Recorder not found! Ensure it's attached to a GameObject."); - return; - } - - // m_Recorder.DebugEchoMode = true; - m_Recorder.TransmitEnabled = true; - } - - public void StopSpeaking() - { - if (m_Recorder != null) - { - m_Recorder.TransmitEnabled = false; - } - } - - - #region MatchmakingCallbacks - - public void OnCreatedRoom() - { - - } - - public void OnCreateRoomFailed(short returnCode, string message) - { - Debug.LogErrorFormat("OnCreateRoomFailed errorCode={0} errorMessage={1}", returnCode, message); - } - - public void OnFriendListUpdate(List friendList) - { - - } - - public void OnJoinedRoom() - { - } - - public void OnJoinRandomFailed(short returnCode, string message) - { - Debug.LogErrorFormat("OnJoinRandomFailed errorCode={0} errorMessage={1}", returnCode, message); - } - - public void OnJoinRoomFailed(short returnCode, string message) - { - Debug.LogErrorFormat("OnJoinRoomFailed errorCode={1} errorMessage={2}", returnCode, message); - } - - public void OnLeftRoom() - { - - } - - #endregion - - #region ConnectionCallbacks - - public void OnConnected() - { - - } - - public void OnConnectedToMaster() - { - m_VoiceConnection.Client.OpJoinOrCreateRoom(new EnterRoomParams - { - RoomName = m_Manager.CurrentRoomName, - }); - } - - public void OnDisconnected(DisconnectCause cause) - { - if (cause == DisconnectCause.None || cause == DisconnectCause.DisconnectByClientLogic || cause == DisconnectCause.ApplicationQuit) - { - return; - } - Debug.LogErrorFormat("OnDisconnected cause={0}", cause); - } - - public void OnRegionListReceived(RegionHandler regionHandler) - { - - } - - public void OnCustomAuthenticationResponse(Dictionary data) - { - - } - - public void OnCustomAuthenticationFailed(string debugMessage) - { - - } - - #endregion - -} +// 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 PHOTON_UNITY_NETWORKING && PHOTON_VOICE_DEFINED + +using Fusion; +using OpenBrush.Multiplayer; +using Photon.Realtime; +using Photon.Voice.Unity; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using TiltBrush; +using UnityEngine; + +public class PhotonVoiceManager : IVoiceConnectionHandler, IConnectionCallbacks, IMatchmakingCallbacks +{ + private VoiceConnection m_VoiceConnection; + private MultiplayerManager m_Manager; + private AppSettings m_PhotonVoiceAppSettings; + private Recorder m_Recorder; + private bool ConnectedToMaster = false; + + public ConnectionUserInfo UserInfo { get; set; } + public ConnectionState State { get; private set; } + public string LastError { get; private set; } + + public PhotonVoiceManager(MultiplayerManager manager) + { + m_Manager = manager; + Init(); + } + + public async Task Init() + { + + try + { + State = ConnectionState.INITIALISING; + m_VoiceConnection = GameObject.FindFirstObjectByType(); + if (m_VoiceConnection == null) throw new Exception("VoiceConnection component not found in scene"); + + m_VoiceConnection.Settings = new AppSettings + { + AppIdVoice = App.Config.PhotonVoiceSecrets.ClientId, + FixedRegion = "", + }; + + m_VoiceConnection.Client.AddCallbackTarget(this); + + } + catch (Exception ex) + { + State = ConnectionState.ERROR; + LastError = $"Failed to Initialize lobby: {ex.Message}"; + ControllerConsoleScript.m_Instance.AddNewLine(LastError); + return false; + } + + ControllerConsoleScript.m_Instance.AddNewLine("Runner Initialized"); + State = ConnectionState.INITIALIZED; + return true; + + } + + public async Task Connect() + { + State = ConnectionState.CONNECTING; + + m_VoiceConnection.Client.UserId = m_Manager.UserInfo.UserId; + + if (!m_VoiceConnection.Client.IsConnected) + { + ControllerConsoleScript.m_Instance.AddNewLine("Attempting to connect Voice Server..."); + m_VoiceConnection.ConnectUsingSettings(); + while (!ConnectedToMaster) + { + ControllerConsoleScript.m_Instance.AddNewLine("Waiting for Voice Connection to establish..."); + await Task.Delay(100); + } + } + + if (ConnectedToMaster) + { + State = ConnectionState.IN_LOBBY; + ControllerConsoleScript.m_Instance.AddNewLine("Voice Connection successfully established."); + } + else + { + State = ConnectionState.ERROR; + LastError = $"Failed to connect to Voice Server."; + ControllerConsoleScript.m_Instance.AddNewLine(LastError); + } + + return ConnectedToMaster; + } + + public async Task JoinRoom(RoomCreateData RoomData) + { + State = ConnectionState.JOINING_ROOM; + + if (!m_VoiceConnection.Client.IsConnected) + { + bool connected = await Connect(); + if (!connected) + { + return false; + } + } + + var RoomParameters = new EnterRoomParams{RoomName = RoomData.roomName}; + bool roomJoined = m_VoiceConnection.Client.OpJoinOrCreateRoom(RoomParameters); + + if (roomJoined) + { + State = ConnectionState.IN_ROOM; + ControllerConsoleScript.m_Instance.AddNewLine($"Successfully joined room: {RoomData.roomName}"); + } + else + { + State = ConnectionState.ERROR; + LastError = $"Failed to join or create room: {RoomData}"; + ControllerConsoleScript.m_Instance.AddNewLine(LastError); + } + + return roomJoined; + } + + public async Task LeaveRoom(bool force) + { + State = ConnectionState.DISCONNECTING; + + if (!m_VoiceConnection.Client.InRoom) return false; + + bool leftRoom = m_VoiceConnection.Client.OpLeaveRoom(false); + + if (!leftRoom) + { + ControllerConsoleScript.m_Instance.AddNewLine("Failed to initiate leaving the room."); + return false; + } + + ControllerConsoleScript.m_Instance.AddNewLine("Initiated leaving the room..."); + + while (m_VoiceConnection.ClientState != ClientState.JoinedLobby && m_VoiceConnection.ClientState != ClientState.Disconnected) + { + await Task.Delay(100); + } + + if (m_VoiceConnection.ClientState == ClientState.JoinedLobby) + { + State = ConnectionState.DISCONNECTED; + ControllerConsoleScript.m_Instance.AddNewLine("Successfully left the room and returned to the lobby."); + return true; + } + else + { + State = ConnectionState.ERROR; + ControllerConsoleScript.m_Instance.AddNewLine("Failed to leave the room properly."); + return false; + } + } + + public bool StartSpeaking() + { + m_Recorder = m_VoiceConnection.PrimaryRecorder; + if (m_Recorder == null) + { + ControllerConsoleScript.m_Instance.AddNewLine("Recorder not found! Ensure it's attached to a GameObject."); + return false; + } + + // m_Recorder.DebugEchoMode = true; + m_Recorder.TransmitEnabled = true; + return true; + } + + public bool StopSpeaking() + { + if (m_Recorder != null) + { + m_Recorder.TransmitEnabled = false; + return true; + } + return false; + } + + + #region MatchmakingCallbacks + + public void OnCreatedRoom() + { + + } + + public void OnCreateRoomFailed(short returnCode, string message) + { + Debug.LogErrorFormat("OnCreateRoomFailed errorCode={0} errorMessage={1}", returnCode, message); + } + + public void OnFriendListUpdate(List friendList) + { + + } + + public void OnJoinedRoom() + { + } + + public void OnJoinRandomFailed(short returnCode, string message) + { + Debug.LogErrorFormat("OnJoinRandomFailed errorCode={0} errorMessage={1}", returnCode, message); + } + + public void OnJoinRoomFailed(short returnCode, string message) + { + Debug.LogErrorFormat("OnJoinRoomFailed errorCode={1} errorMessage={2}", returnCode, message); + } + + public void OnLeftRoom() + { + + } + + #endregion + + #region ConnectionCallbacks + + public void OnConnected() + { + + } + + public void OnConnectedToMaster() + { + ConnectedToMaster = true; + } + + public void OnDisconnected(DisconnectCause cause) + { + if (cause == DisconnectCause.None || cause == DisconnectCause.DisconnectByClientLogic || cause == DisconnectCause.ApplicationQuit) + { + return; + } + Debug.LogErrorFormat("OnDisconnected cause={0}", cause); + } + + public void OnRegionListReceived(RegionHandler regionHandler) + { + + } + + public void OnCustomAuthenticationResponse(Dictionary data) + { + + } + + public void OnCustomAuthenticationFailed(string debugMessage) + { + + } + + + #endregion + +} #endif \ No newline at end of file diff --git a/Assets/Scripts/SketchControlsScript.cs b/Assets/Scripts/SketchControlsScript.cs index 0d37c9f1c3..43d9481a62 100644 --- a/Assets/Scripts/SketchControlsScript.cs +++ b/Assets/Scripts/SketchControlsScript.cs @@ -1280,8 +1280,8 @@ void UpdateBaseInput() && !InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Activate) && m_GrabBrush.grabbingWorld == false && m_CurrentGazeObject == -1 // free up swipe for use by gaze object - && (m_ControlsType != ControlsType.SixDofControllers || InputManager.Brush.IsTrackedObjectValid) - // TODO:Mikesky - very hacky + && (m_ControlsType != ControlsType.SixDofControllers || InputManager.Brush.IsTrackedObjectValid) + // TODO:Mikesky - very hacky && SketchSurfacePanel.m_Instance.ActiveTool.m_Type != BaseTool.ToolType.MultiCamTool; if (m_EatToolScaleInput) @@ -1497,8 +1497,8 @@ void UpdateStandardInput() if (!m_PanelManager.AdvancedModeActive() && InputManager.m_Instance.GetCommandDown(InputManager.SketchCommands.ToggleDefaultTool) && !m_SketchSurfacePanel.IsDefaultToolEnabled() && - m_SketchSurfacePanel.ActiveTool.AllowDefaultToolToggle() && - // don't allow tool to change while pointing at panel because there is no visual indication + m_SketchSurfacePanel.ActiveTool.AllowDefaultToolToggle() && + // don't allow tool to change while pointing at panel because there is no visual indication m_CurrentGazeObject == -1) { m_SketchSurfacePanel.EnableDefaultTool(); @@ -5064,7 +5064,7 @@ public bool IsCommandAvailable(GlobalCommands rEnum, int iParam = -1) case GlobalCommands.GoogleDriveSync: return App.GoogleIdentity.LoggedIn; case GlobalCommands.RecordCameraPath: return m_WidgetManager.CameraPathsVisible; - case GlobalCommands.AdvancedPanelsToggle: return !MultiplayerManager.m_Instance.IsInRoom; + case GlobalCommands.AdvancedPanelsToggle: return !(MultiplayerManager.m_Instance.State == ConnectionState.IN_ROOM); case GlobalCommands.MultiplayerJoinRoom: return !PanelManager.m_Instance.AdvancedModeActive(); } return true; From ecf162deb585a9557512d03df3e1db9eb5ec4c67 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Tue, 29 Oct 2024 16:10:27 +0000 Subject: [PATCH 061/174] New Icons --- Assets/Resources/Icons/connect.png | Bin 0 -> 2841 bytes Assets/Resources/Icons/connect.png.meta | 127 ++++++++++++++++++ Assets/Resources/Icons/disconnect.png | Bin 0 -> 3213 bytes Assets/Resources/Icons/disconnect.png.meta | 127 ++++++++++++++++++ Assets/Resources/Icons/edit.png | Bin 0 -> 2647 bytes Assets/Resources/Icons/edit.png.meta | 127 ++++++++++++++++++ Assets/Resources/Icons/enter.png | Bin 0 -> 1537 bytes Assets/Resources/Icons/enter.png.meta | 127 ++++++++++++++++++ Assets/Resources/Icons/exitmultiplayer.png | Bin 0 -> 2428 bytes .../Resources/Icons/exitmultiplayer.png.meta | 127 ++++++++++++++++++ 10 files changed, 635 insertions(+) create mode 100644 Assets/Resources/Icons/connect.png create mode 100644 Assets/Resources/Icons/connect.png.meta create mode 100644 Assets/Resources/Icons/disconnect.png create mode 100644 Assets/Resources/Icons/disconnect.png.meta create mode 100644 Assets/Resources/Icons/edit.png create mode 100644 Assets/Resources/Icons/edit.png.meta create mode 100644 Assets/Resources/Icons/enter.png create mode 100644 Assets/Resources/Icons/enter.png.meta create mode 100644 Assets/Resources/Icons/exitmultiplayer.png create mode 100644 Assets/Resources/Icons/exitmultiplayer.png.meta diff --git a/Assets/Resources/Icons/connect.png b/Assets/Resources/Icons/connect.png new file mode 100644 index 0000000000000000000000000000000000000000..e1958af94d0ead51c6b5a98cc5d5773fe0ef3260 GIT binary patch literal 2841 zcmdT``#%%<8{ct4bnrzhtkL;aq`8!c+=>%rJH}ToGZJaU+K4feOG%Vmis-V~A#2#& zZF5&Ct6YaEx5=%!G+{=V?d<&i{RhtH^?E+<*Ym^kd7jtndA~o;bKezyUP)0)5dZ)v zIbAsCCPnH$wp&hmYtCKQl)^8OZs+X*6+H*$rOvKkJ7+rppgL=hC_q-~D}-P0jsyUd z-~400)IUP={|Ur7AaEDMf^hhN2owO1#~X!U(UE}x;V7f9h+yK9wH5%Nc;D%qoqLQQ ze>~)Z`y~ze<$+g*p(#Mj)zUBavcUDi-TgI3|18QlSTqas?|1BL-LG0_=X&{Ak?pxX zb*|41ud1PR*-xNF{f|A1O)ieEwQe-n*oWmmYsl&DBD|2OXmf97R`TF@aXQ{+z+N9U zEbHH#C%O!n{+)JE?ycgLga0n5tLofp(C|}JQ*(E7%U&37VlE7o6VuYtrbtg01-#y} z=4M|HclVO)Y&GGQ5G{6!*-fm{)zvNQ>iRRcprFDrU88VnYU-z6L`&SJ8*tygvavDW zsFf)P;X2<(>?Cjqz~lkd)dLNE>-&xw88MzWZEeLaE^w$rUC&OI-@CV)zkW@Q%-?&7 z&1MgM%7v7x=-4j%X!>!R3DctOQ97D3Zkq%f)vrO)Ls5pvx(~4uu}vFhvYbZidTxSB zi}q14#8B$Bj{Dz=`oXnHAb$1Y^^%y4(fqP;>|5xle%ZHfV({?!Pokk)JyfqsT6G{R zlu7!lKRT^spCJ-KJcXHJ{u;NrGM!pWEFR6XhmMBC(L)A0EaSIK+mya$@kg0xX6gKq zRcmVeK{EV>MPX3a(>FHGf~9dQUh?)tO;)4F#?p8L*~`a=`u!yiyOVEpsj*~k?i$vt zd#K!plcEHwKf=;cIMt#IwtPoY6;r9yNM(zfni^U_dx|$^l)5O1JCLAqZ)TmOj!T^Owa_jiXO}y zeXJOSNUUs)-$pj!e=y3*%3_?C*tXx371ZmdwU8t5L+HVh{|#;Ym>^ti`eVP`(c-!+ zDoAj7FBrA0R#J5;ujPS1*dqLOL-wqSrQSHz%b5REE{`!#A3D+6BAofC&V9GkH_P&G zR@F_WS372DV{-HIR*~Z$J3AwLXmcGWX?6J>z@1V7i`#NDAA)SG7DqkX>si*5Z{=ei z#+@;1s`Y;(!$+j#G6qVZP-snS!pa|w?l!Nnjy0@Gs`3K zH+Qz^Kp>C|?v!>*%R~IaWt@Q_MyH?6yB|O zGOm|`^&X-^5v6klw^mmp5`bI}PfsM+64#d*Xs;nUw*tF*OLtFfkFZS7d)-IwO?#!!EL27MKcYmg8I@HC5=QZhF zV(SfBT%K$fL7STrC9UflO8OXzs#*LNj+@M7=Ku<=g(eB2n1mw*cP(i)v1?VKp`kxH zG3Mc4&+;`2jb7q^edSE-fN}X>s@plFhVb^ zsY>^-yyuuqn6kF@2N;)3Ca+|B`%XkT;4R<3dlwE_o;xvLRP`@hZ?2-uZcLrS-tC!YWqC-%I}PNG+0OkOii?hrKWe_DKwDS{6;88Mu!N1a z9T^uh+*}{Y$~5J~q~mX~O0CJUBzko;f8>4ThXc6Y)FryuuPFFxwIshMQt*|VZ-tL> z(q;!38X9^?&zOw4wsp*F*y=rHi1W)pnH*z9Q6@uk$07Ry4l&V|*bDo*4{RF0wRYd9 z=AQCmTda0z>EZaAes8csPT#yVs9P*Ao*$u8)e-m4rJ9{S?d{;`7_nze!#d`;@yA1! zG*wmA&<_uPm*z=P(dmJ$*0&}})w0-y(K5j0qB`>o$5zt|;wiyj|F~cAr<6zGu-?ZI zgsiM==-M26tbbd))AamgFuV`TY`!s_QbwcA}vZA#p-U=a+Fnqu+J;M?M8gU`uy$adG&b=0f7Y_yONp9#4?%oz4wz z37?cPCNQtOSM-x{xt8{lMl-+?c+pMrM0S7;VfC;v;e4c@66jRA&l|l-YjkupV_~>f zk7*5oKrZ5caA)(p)0GzoC@#4zl6Qu^M-6b5uLcFFw;h?=<(JzEe7J0-k##JD*>y!! zN{!!MU&+R=DH))LXp{R)L&qEGmJu`esMPq&Z3$8FccM!~HwIB|!=TYvv1qfm-Cbwy z5kEcsH@?Mf10=<`&=OP?+^d}++d5o0lo{X}VwF)gTpKL-ww9rt26J}iWU7~#n9(|) z81v}cju&rtuvlo;iITc}$J`c|P^mCif`sLbjU%TwN8XxnvNt6!@vCpA;AC&z{gX0+stP;Z_xeWWFgwI-`6^;Gf{`HWG;xh6)b0 zQg5B_kPtt0bs6H|jnI@|nv#l*m3H-x`!vmqw|s9XsSBInHiSLVg}4Ez`Zbms!#pix0T&_>I6-b00qJD?W2)a&D|4 zvS;Ju9D{O8I;QlWpOK8v(IU@VdUb8^+Uh#R{tbg&(moWW^^hwrH4Xi!1S{^ZYuelO z;P5X77|Mr*&y_m1s6;!`D^?~rvh@&**|IB-aq&94RO7kq_GEjiDDN%>_Qc3rUtfO& zbGW`Pf9gTB(zS2bx28^QB`e6&>GUD?%k#v*3;Ak<0?uUx1%)0$pOiRe5i=Kf+P&YB z3Ot%3X47iUIB}$e;jprFHubt)5XnL>TvEQaNimP7uCDG87uPHhF0MVcoBoI&7#tk5 zh;(NkZZoac0$%DmYGmY@rETLT6beO&r@{jEdp_*^$wC6O{+&zI|J(WOj|z3DxdSo) TMO7Q=<_d6ffS;?d_fPs8Sqp!j literal 0 HcmV?d00001 diff --git a/Assets/Resources/Icons/connect.png.meta b/Assets/Resources/Icons/connect.png.meta new file mode 100644 index 0000000000..8dd4bd72be --- /dev/null +++ b/Assets/Resources/Icons/connect.png.meta @@ -0,0 +1,127 @@ +fileFormatVersion: 2 +guid: 366faf1780512c44b84568938fb37aaa +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 13 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + flipGreenChannel: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMipmapLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 0 + wrapV: 0 + wrapW: 0 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 0 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + swizzle: 50462976 + cookieLightType: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 128 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Android + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + mipmapLimitGroupName: + pSDRemoveMatte: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Resources/Icons/disconnect.png b/Assets/Resources/Icons/disconnect.png new file mode 100644 index 0000000000000000000000000000000000000000..70a31a1427773087a76fe4d70a5dc8ed9ba1c31e GIT binary patch literal 3213 zcmbtX`9DrWXh7XC~r&1G6ta`8K%ZCBl|8gvJ@iwQjKMpk!36) z%hxqbI#-3^Sbw(*S*iz^ZC3HMh4noRsmKJ2n5#oN6Q!( z3r-sgGtl$Tzu*T328^-xJy22C-wVLxtmEBBcR`@y7`8(=6EHuI{>K6X0$r#-Z44Ff zkSnJ*VjsY;CN9sh9&k4YkcWqdyb}tEv4^7_&&wyDP{dgJ6U+y%Zs2g8%EqD3bL%F(oBqQv&RhuS#p{jpSjOYN>@fapD$1MC)kW2`6+{uUd$3 zH-tIjV0SGNf*g3m$}5dlNV=0kf)+io_*>Ke$&1l)uqY({B79}I1YhC3lRrOJd3`r9 zBK`tWODhysfzBGL_8>~yd{fyC&Wi^lSH~(TVPL_g><5v{J!d5Op{#S7Cx=%bM*rQW z0m6m3?=1eb+n(KT=kZYg1F@N5}aye6`v>&*2;^+_zcK{Fc8_ zTmF9B`}cM~k|7<^)>Wn0>Hw3Cjg3RukBzQSL*0=DJ&4Q1*#7a6dfidiH~*uN&y3U?STzr&rBO*6faZBDf`RQGz$!*z{>WumaOOJKI!RQrNw7ijyzCKT)yfy7&Qu`o@zBCwf znMqsyDF|4%Z;3vX#NZ7xE|O8>z42c-QTT|?L?X7_zE)jyy(Zw zb-Bu5jt+MD#B}4QfMf=*Ed?>2+k5xM)aJHK6wZT*wl@PrRGbzWQIJ5 zq{F?+U?Rx{Rq{Xd+?eg)LosPP)*-G;_hj9#Jg1+j4~vV76XH0>VtqX}Hdf@-%7}yE z?a+0+KJ0v9iNK;esFeP_#%H(bt3wCph$7w;=H!SRBEe~0Y3gG$>}j*fs+cdHd+S*m z$Dcokq3y}*Ilrun7q~Y&KVFX>;AIJW-b`H&{^HlCX0bL|HaY|p{9H7%Z=B|TE2z| z{ZK><5I(Xj2aV2HNbsRUj!W5Pva;&mUDehi+SDx16k|I?t7b%+%ck|(;?4wIRZ^nf z4m8a8*4x`lKhK=}mk9xO?U;KtNy!1H;zO4u|C)!G7C@t{RM*C0GOK!%!w5@ zeM7BbW;f7NcZ&TEUZE9=H;$#NHAx8p;vYGV_VKZf%QF(qduccnR@YQwWB z0B&JGS6+o(u$yg9(AyW_RdAAMf4!+rsPh3{E#&2hvP{77A$2g{&Z#i*RG_Z}s;l4IU3n6PfH-aedFfy}~YJLASbSIZ-#|T>lZPZ|`Y~p*h z8NXI@bIbq5&ApN)QB~S=z6#~pm8v?n8Lmtb=UMUW8BB5hw*TxA@uHk1?DB2jDMyag zt+{UKYgXP*qZ^r@(wh6fJWAPnX%s#P|GWr*a>jP7qC#ci#w|%F{Iv&(tuIH*qb8Xl zdtA``*^b058J5+VmKbQU9p%#~x&&<;*vp@ilyvFfxgaVlWJ}pmR;IKk^)jYy-V2Zw zdFTGzdX0}MxBaRVZi=F5lGs7KQUMUH&Pi>`Yb4_ohYx+(wX_zt z_4~)l`HwdDVAC8y9FK}o<7z0T!Agvy|1Ut@l`{D&44OMj5$TDfkXcc@N%7H0{l1Xg zTYWtU)BPpxCl%-*t6{RwE~?o7$Q#9!2p5TT!()f)b&10zj#~w;llf)^pNP{ZY>l8; zV^=Q6P~J}n%EO-`1d#_GvgY6hR%7KpGYE^!KfM}mW+9~D{z-i$EE{k+N&rIBUi_}Z zz_EZe4jf=d3>FCiMI`^*s{F+?l-|2?;4~1-&3~abdrZpOfEDl$?YfU1&6>wm0%8H= zX!RNndZy^Rht}46O8x8p|K*2m#X<3|4NE@nMG5QSk8>WXRvGL})cB-lyr*1%g8XK8 z)+Q%ymhu6=F_yA5-*v6fvWy1(eK=At{Wv?+uaRA%1?J?`36u+_L!F=btGL+)jZz5k z`G$Om01)M-=Bl(F`7A-s3R(qD1hAV2Vwuc+sT()Jy5C^OOGhs`t=fpQJ`p2A8jR&tT^x^FQr5M}P4vVoe$p_rc$pOmb?Pz1*{5(QN zN-84b2fwAIBw(bfG&oKVw=!OxIC;Unf)_dxf)@)$jPd6u$e8Q?f3@TRMkee~YNkAu z9SQ7GI5Y&45fv4+5)}i~qx6iu*ZpOL)^DU$ZAwMg(LYG{dZiuqDlLSI3Vsq0*qPyu#+OK!&YHzLu=ND{ zUc(}oB(QpXbYRySD@xx}Cf%phINyVT-Dlf*efD+J)ZCi}3a!dicHWW%{-vsFL4XxE z)CQa>e3Ay}*D1Z?7aIYfsixtM$Os%(R~P5G`75HrXSc}j5^mtJnQL*GkqALJyC8-2 zqHJqn>hAH8&$|yFnhVzf#NJSR{y%!*|G-5jbJA?>+rfxPJrCe#0R+-{V4ziW?`iPA E0io_ja{vGU literal 0 HcmV?d00001 diff --git a/Assets/Resources/Icons/disconnect.png.meta b/Assets/Resources/Icons/disconnect.png.meta new file mode 100644 index 0000000000..680e2aa673 --- /dev/null +++ b/Assets/Resources/Icons/disconnect.png.meta @@ -0,0 +1,127 @@ +fileFormatVersion: 2 +guid: 56e5ebeba70465c44bf75491d40b6f3d +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 13 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + flipGreenChannel: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMipmapLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 0 + wrapV: 0 + wrapW: 0 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 0 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + swizzle: 50462976 + cookieLightType: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 128 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Android + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + mipmapLimitGroupName: + pSDRemoveMatte: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Resources/Icons/edit.png b/Assets/Resources/Icons/edit.png new file mode 100644 index 0000000000000000000000000000000000000000..4ac63b6a45ac88c3ccb5f44de0b88a58c78c2c65 GIT binary patch literal 2647 zcmb_ec|6qZ79UE)psuWC%U+>Tw!v6NG1Fj-C6P4D#A`Pyd(sS9ZiAW0UW^oF8d0_( zdadt_EZK`o4Ui4}N)RY^tyk~g*oY!@?h-=qlumn6PCML$R(aqHJsN@BlrabMVkhMhK6UF;5|baY<~?DU9$M;b$`!TP<75|;_hsi-^MFBd`|e$6<;u^Jl^?HXYL-)15=PA@el+k)z9u?b_bBY@ zf$}Qbx0FGB#5!TT&7~BhGKP&J+OrI-dlG$sCab%TBTf^-i%8ue5)1S5^IPi-xQeH$ zYJsn{x;iT`FfckIB0{j@Wp6|(QczVLY-wrvx1m5F*!cdblieO7#6A@Kac6rIe{MJb z&K+S`{92G;HrC6CWMT?|xO!5bR8){QH#e!f<3x)3t6B&0vE2Jxrn0iKmTGFZw-i(; zV7lb;M2)KT4L*(Gzf#3yejcfG(XoCgstFG|v-xvnkV>WEMIJnAXviBZHqG8@3eN-6 z?;SgR736|Iw(>*+b)hGYLjFxTP=phgXq*;;C7z&)7}iJR;9?xv+tN_(9E=oF%; z^yk9D!n)v|p${K88X6kx!G#0tLnpos(cy8<&SXYG?e&{C1^vaQ)Y1tYu(9+4+Zh`k z&Sd17rw6{ixiaH)>z0fbys8QYgIQWz`+0H_rBo~(XQZf_@T#&$=kF0LJ^0~d6)WV7 ziVUz+FxQ*C(B0i#2m7`#-5gEsB|M$FR)!OkB@9;}wtjq_s55z}VP*9oAucYr@tqog z$em|Xx3;0GMmVuJAITgrU3j1XLTpd?={(*AO?Nnsg34S-8{!TW8D}ML ze77C%jJLeCHMzP^^~ThunxdVAMegXsI=a--k1l4qFJAUTfJN3r*u=F#vA>$17m1t>-`r3>3Cl!;jtEG`6Pn_nHcJJcE ztiB99un6-V^-G6ObFH=N436rLXh&L(OGT|rHD+Iyurng5dI&62B86j62=h2W#k9$GQ^HT?8J$;u9AeqJj=l^W1-v4);T=J&CNH?LrBq1M%gS_e=o*g$Gy#4P9e zNLELO9qig$1%fXacwrLWNyyB?^u~9`ApZNC4GtjFh7jE}+F+@5AE3%$oH}#2cH2PF zP6s#*pU^^xb4;I}9@hqm!)Z2ai#w(@cuoLvS*+^J>3(qvJxn%2?~1#- z=ANO&Z-(Bl(l5)TYQlY>*pB@>+-`~Hq+e(^x6XyPg>YtuKZU*w0C6~+`Sr!lV$+CN z8ygVd6`9sR;Ez6SEibnVFNIkq@09En-H`GsT+q?Lfs&^@8V#tW7AmV{vq9Q5^4XEpS=s( zGs_K)ev@bDHxL{gj4L6hMZ&7k`ay>=G#du+~rv#;_ zh*Mr(?&afCjJgfN*DG*sIC=U$UV(v?s5gdyM>VMmO>J$cR7JWqaaR;y4+0d{m#5yZ z&h_GjbROSJyLx1qcmIS;$BcWYk{EtIUPEMgb&i$qF z=RQV4Rw;GyxBWyhDzHRdnW3X-Pg6G!k8S|8Co?k>Cmb)q;h#rdxS*_43FH|kTq1qW+i-K) zd;G(P6EZ{cXiusm66p_tK+JOd)pV;5rc>QoZZ3DE93d|Lbn{_WDRAWE<#lSN@5XxZ z`D4`rafOVqKJg{dHc1fY_TM%Q|EKLWv#-G%nqKIqj#Pm-D0*XKRU_g1$CTFc#af1c zPY@EB*(&_zSolmUyQ8y{CoB0s1ZMS+CHV8t0HE~eT?7@teSMV^)|U!f%YRR36vn@4 l6#8#1(chfzNJ{N4Xj#-mo6~@r1NuG?;+(TR)9&h>zW_Ww-7o+E literal 0 HcmV?d00001 diff --git a/Assets/Resources/Icons/edit.png.meta b/Assets/Resources/Icons/edit.png.meta new file mode 100644 index 0000000000..a6e5151319 --- /dev/null +++ b/Assets/Resources/Icons/edit.png.meta @@ -0,0 +1,127 @@ +fileFormatVersion: 2 +guid: 0141ac6bf9f578f4e95706cdd2117914 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 13 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + flipGreenChannel: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMipmapLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 0 + wrapV: 0 + wrapW: 0 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 0 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + swizzle: 50462976 + cookieLightType: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Android + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + mipmapLimitGroupName: + pSDRemoveMatte: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Resources/Icons/enter.png b/Assets/Resources/Icons/enter.png new file mode 100644 index 0000000000000000000000000000000000000000..a0f0b8a4b143a11aa8b9eb1ae4b5cfe35cbe50ed GIT binary patch literal 1537 zcmeAS@N?(olHy`uVBq!ia0vp^t3jB94M*K8cSRwN`mv#O3D+9QW?t2%k?tzvWt@w3sUv+ zi_&Mmvyoz8U@i1?aSW-5dwbW`TiKuC_(w}#tuVVre+4tG^oQ9W{9K!UaJ^Z#X^Y^N zjXa0;PCNW4N`9jD6Or8+4m+N?=r+l5HMtbsG01jW86=Nz5k|UuU%&eK^X8vFKQEsXX#M!H(Adl@E&U+N-O{HY zlX71Lc5RL5kI@VN`c2}Uo7&`!{bdE4H*XHj|Gf8LDnp8ZHW4VBdCS(18?HCI>!!Ud zxix40{Mip5Chlh1-(l(&x5C=mIxQ{jn2d~!K#s!^smWd|=FXk_^yif;A_v)A))-bu zXR#h+b6z7@t$&59>1%(4a)>sF(H^1v$Q;O6+Z&-QU;2*)+qq+Z5@yByaTD&F`23=bA*zKzFv5?;Hn;dR*XRq+~| z;*AF#TN^pdB$9FsK;ECtaPXJ&8sDabY~^ceP8%H$R+toTn(^2GE3pq!YZb>yP{%aO<%u$UDmeZ%a<=R_U*H?E`I#v$_cjw#J zzv+^eIWz5np`l^mpTEDi-<|&C*vprh8@F%Y{^~oo^^MZ00ZRi;zt`yP>jQ@9mH1!( zfqrFKc+G8%Z$rReU_dB`{09YuDKH=eoOt^pq#FWWJFhWRzVb^jyGKo3ozs*hsh4wM zoBJBW6)jdqMoHm^yrY0l?3Gx!3z&SALbj{?D}R0JRM*Q_uQnO~my?q_vpxU*8=W{$ z&Z)Xh`EGXex6eM+bnXol`u<%X`0LCT9PaqBSXUA18Agz2&Vvo4aLSVW# ztds`QKu>*Yij9ggs;;iyV;st!)O+*e$Bi@R&woC5r4>C=5n91Y-g`g2{wC?hrDWjPUaJ334L@x+4PakTRF`@C#kaac4OJ+*KkG&wn$&EsQj zjv@rF{6dAlz2ac_6YzV^4IzxJt>@CGnr2PaOP4ao{B>6TDvaQAY*G=wP$14lploO0G_H}Wj~LuxmB8rOfZ8Y(a>^m8QsOZRgwsqY0d$53!$M5GmV zDtLJ;y*C?qeXchv_fw5e5KBoCd-`LopHpMt6lK5YQxiErFNAZ1RcI6QU29zu+fO7C zzfQM5Vpe-$N+reQv>rTG&^5BB*-fEusnD-jW@rr<`i)ec!tKi$L})FIH>IlEC;Vn7 ziH*ldsCsyK*fxbO)ld6ym_^loquLWF@m7xOz`(%sq$I=f#*i7btC^WuwKq$B0(G#J zW6h%3=WcEWb0%AmxUQ6p477PFg6+JmLKlDaI9@MoRas14`*)zD#HsG_Y)@uq)2fk! zqDhu}tUJ~Tfj<}M7CZIt?E zYWr0#7Wi6OiHjXO7ySYm)tkI-L$r1B62%AsAJvsGYveBdy3r-SiROmMsP2T= zZiooLkm<>~lnS!xfCt^E^Dk+tWd_8qN0;m24Gj-irk_Xg%5NAtH@P$hKjaLR^}!dE z=~s}EyE#;4T|$-(c0lY%Jdxe+}vQHP43tIJw9IJ+PDI3B8I~% z4rw4hp%Wsb85c z+20l~;q~QR(&o1fQfXRM1lJprou)#!Ru7RJ8?JDh2k!{4L7M4ERH-|Cf^MzKRc-OE z-RCaI;EYK5-{c9xSFK*?n^k%Ayqs)_3OW^xdcrEKdr!GvP9bpSAtAZ9gt>oBug!GE zM7Rz}ouzO}q`A}47Y<8HbFa~`)_#eIRP#xO?9^Za+|$fYiqYLY^Ig6?cWewFz~5Th zi(J`vuKy+a0cPfrozEXFYpCx=4sVX`ipBAfRJiZVWK!n7GTjyMPFowPBJGKvH*t6q zy0bZdRn6+(tMjoahc_t)C6p}UC5|IB-W?+dkM4YjxPgW9>+Ei=+tY6k??Iu!a4#rd zPez)?=F+H6=`Q%@CHfJYro8^hoy~_plnUKd-oMUC;v#lW;72pjk&%&|hO(iun?da| ztuQIxY>&JmQ|6y8f$hj@s8RncBi(Jp0(+tON)1#_vGhO82notqBJo5Nsi(WIkJr)O z{%w!Edlr8%n&2Hhy0tVKPU`K;MskB3aAhuR^oS((oU!7gB9msP+rQ>#;4k`p(i$&6 z3_4oZE%yDk*p@x6n+9h=Sd2YWDnKLD5~_GI)$L z9|LQJ?ex2b9L3`C3OGhZM5&Jyl{LjQZV?m_{~G9MXvx4D6k#nH`!c=$HD^N9lzgL^ zvJhEW*=8U!Be^kX&T1UxE*x_7zFrH9)~30;Ht&C^x<3T|nti8X8?J=_fbNwcSoO!U zn|)D(`cFFDJnJ*k!2ylG@%lI2vyPQi@@(M?J8tnLZKF!S&_U7j4MjK~X{k=+zNhAa zxk=4z5g!T^YToQjlqZd(m$`$R`8Qi4Q#N1Gbaq&hFs_PGy)s?=+4JYGNT%sFVMx@* zT;I^~yf|slrZ}2AAl-$NAFtMgtr5}Y=KQKdC#kIa%#G{Psuno_t~9W{p2@T`?sE`z(|N@=#p75XmMMS*J;k? z!iW4nUKJD!E{;@j6Yc*2PNP0aT}>?>h$=E!eE&>sp|jwm7(uwilQKD47tml7JkueD zHZ?7;R%3x8Nic}qtq;@Y#E3|ozI^$`>)*b$M#+VP7aGXl^1FAVrt#Nz;FQq}l%_~OVfvuKzRb-%qpBK5?kGI)x}u_@xi}4N;SKo3 zo<~Wxstuo^eE1)}$WYpXx~{HcwuQ)6@LaE=L2czrm^vMjMD2!13KE1hB(eW5P`)Dh X^}h7Pg*#K=&k@87Wo2Ay Date: Tue, 29 Oct 2024 16:22:44 +0000 Subject: [PATCH 062/174] Updates To The Multiplayer UI --- .github/FUNDING.yml | 6 +- .github/dependabot.yml | 30 +- .github/workflows/build.yml | 2626 +++++++-------- .github/workflows/delete_branch_cache.yml | 80 +- .github/workflows/export_secrets.yml | 66 +- .github/workflows/generate_certs.yml | 94 +- .github/workflows/get_license.yml | 44 +- .github/workflows/ios_setup.yml | 72 +- .github/workflows/pre-commit.yml | 78 +- .github/workflows/test_unity_credentials.yml | 44 +- .github/workflows/third_party_notices.yml | 38 +- .pre-commit-config.yaml | 96 +- Assets/OculusMR/OculusMRController.cs | 178 +- Assets/Prefabs/Panels/MultiplayerPanel.prefab | 2865 +++++++++++++---- Assets/Scripts/GUI/MultiplayerPanel.cs | 186 +- .../Multiplayer/MultiplayerInterfaces.cs | 1 + .../Scripts/Multiplayer/MultiplayerManager.cs | 118 +- .../Multiplayer/Photon/PhotonManager.cs | 2 - .../Multiplayer/Photon/PhotonVoiceManager.cs | 2 +- Assets/Scripts/SketchControlsScript.cs | 28 +- .../Strings/Strings Shared Data.asset | 26 +- .../Localization/Strings/Strings_en.asset | 26 +- 22 files changed, 4218 insertions(+), 2488 deletions(-) diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 681f2ee1d8..971753fd20 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,3 +1,3 @@ ---- -github: icosa-foundation -open_collective: icosa +--- +github: icosa-foundation +open_collective: icosa diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 6dd25a79a2..e3edcc6248 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,15 +1,15 @@ ---- -version: 2 -updates: - - package-ecosystem: "github-actions" - directory: "/" - schedule: - interval: "daily" - reviewers: - - "mikeage" - assignees: - - "mikeage" - groups: - all-actions-updates: - patterns: - - "**" +--- +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" + reviewers: + - "mikeage" + assignees: + - "mikeage" + groups: + all-actions-updates: + patterns: + - "**" diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 617e8cc0aa..43c82828ee 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,1313 +1,1313 @@ ---- -# yamllint disable rule:line-length -# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json -name: Builds - -on: # yamllint disable-line rule:truthy - pull_request: {} - push: - branches: - - "**" - tags: - - "v*" - schedule: - - cron: "34 19 * * sat" # Weekly, Saturday at 19:34 UTC - -env: - UNITY_VERSION: "2022.3.34f1" - PHOTON_PAT: ${{ secrets.PHOTON_PAT }} # This needs to be here, since you can't use a ${{ secrets }} within an if: condition -jobs: - configuration: - if: | - (github.event_name == 'schedule') || - (github.event_name == 'pull_request') || - ( - github.event_name == 'push' && - ( - github.ref == 'refs/heads/main' || - contains(github.ref, 'refs/tags/v') || - contains(github.event.head_commit.message, '[CI BUILD]') - ) - ) - name: Configure Build Parameters - runs-on: ubuntu-latest - outputs: - version: ${{ steps.version.outputs.version}} - androidVersionCode: ${{ steps.version.outputs.androidVersionCode }} - stamp: ${{ steps.version.outputs.stamp }} - prerelease: ${{ steps.version.outputs.prerelease }} - previousrelease: ${{ steps.rawchangelogdata.outputs.previousrelease }} - previousfullrelease: ${{ steps.rawchangelogdata.outputs.previousfullrelease }} - currentrelease: ${{ steps.rawchangelogdata.outputs.currentrelease }} - rawchangelog: ${{ steps.rawchangelogdata.outputs.rawchangelog}} - basename: ${{ steps.github.outputs.basename }} - description: ${{ steps.github.outputs.description}} - itchchannelname: ${{ steps.version.outputs.itchchannelname }} - fastlanelane: ${{ steps.version.outputs.fastlanelane}} - uid: ${{ steps.github.outputs.uid }} - gid: ${{ steps.github.outputs.gid }} - flavors: ${{ steps.flavors.outputs.flavors }} - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - fetch-depth: 0 - lfs: true # We don't use LFS, but it adds no time, and leave it here in case we do at some point later - - - name: Calculate version and stamp - id: version - run: | - # General note: for pull requests, we query github.event.pull_request.head.sha rather than the default sha, which is a merge commit of the target branch into the head. For pushes or tag commits, there's no additional commits made by the CI, so we can use the default, current, reference - - # Get the first two numbers from the last tag, including a tag on the current commit (to handle the case of a formal build) - MAJOR_MINOR=$(git describe --tags --abbrev=0 --match "v[0-9]*.[0-9]*" ${{ github.event.pull_request.head.sha }}) - - # How many commits have been made since the last tag of the form vX.Y. - # - # We used to use this version, however, it couldn't handle these two cases at the same time: - # (v2.1) - # | - # /-c2..c4..c5-\ - # / \ - # c...c0..c1....c3.........m6..c7....c10.c11.....m13...c14 <- [main] - # ^ \ / - # (v2.0) \-c8..c9..c12-/ - # If we use --first-parent, it wouldn't find a tag that was not a first parent, and so it'll think we're now in 2.0.8, though it skips the commits on the branches. If we did not use --first-parent, it gets the proper tag (v2.1), but counts each commit in the feature branch, and gives 2.1.10. While we almost always squash, if we ever do have an explicit merge commit, we don't want to count the commits on the feature branch. In this case, we actually want to get 2.1.7 (commits c3, m6, c7, c10, c11, m13, and c14). - ######## OLD CODE ######## - # # If the value is not equal to zero, git describe will give us a version in the form vX.Y-Z-gAAAAAAA, where Z is the count. If the current commit has a vX.Y tag, it'll just return that, so the 'cut' does nothing. We test for this below - # PATCH_VERSION=$(git describe --tags --match "v[0-9]*.[0-9]*" --first-parent ${{ github.event.pull_request.head.sha }} | cut -d'-' -f2) - ######## END OLD CODE ######## - - # Instead, we'll find the last tag, wherever it is, and then count the --first-parent commits "since" then (i.e., not included; they might be historically behind it) - CLOSEST_TAG=$(git describe --tags --match "v[0-9]*.[0-9]*" --abbrev=0 HEAD) - PATCH_VERSION=$(git log ${CLOSEST_TAG}.. --oneline --first-parent | wc -l) - - if [ $PATCH_VERSION == "0" ] - then - STAMP="" - echo "prerelease=false" >> $GITHUB_OUTPUT - echo "itchchannelname=release" >> $GITHUB_OUTPUT - echo "fastlanelane=beta" >> $GITHUB_OUTPUT - - else - # This is the first 7 characters of the commit; we do it this way rather than via rev-parse to avoid an 'if' conditional depending on whether it's a PR or push. (unlike git describe, git rev-parse doesn't default to the current HEAD) - STAMP=$(git describe --tags --match "v[0-9]*.[0-9]*" ${{ github.event.pull_request.head.sha }} | cut -d'-' -f3) - echo "prerelease=true" >> $GITHUB_OUTPUT - echo "itchchannelname=beta" >> $GITHUB_OUTPUT - echo "fastlanelane=beta" >> $GITHUB_OUTPUT - fi - VERSION=$(echo "$MAJOR_MINOR.$PATCH_VERSION" | sed -e 's/^v//') - echo "version=$VERSION" >> $GITHUB_OUTPUT - echo "stamp=$STAMP" >> $GITHUB_OUTPUT - MAJOR=$(echo $VERSION | cut -d '.' -f 1) - MINOR=$(echo $VERSION | cut -d '.' -f 2) - ANDROID_VERSION_CODE=$((MAJOR * 1000000 + MINOR * 1000 + PATCH_VERSION)) - echo "androidVersionCode=$ANDROID_VERSION_CODE" >> $GITHUB_OUTPUT - echo "Version $VERSION stamp=$STAMP androidVersionCode=$ANDROID_VERSION_CODE" - - name: Calculate Release tags for Changelog and raw changelog - id: rawchangelogdata - env: - PRERELEASE: ${{ steps.version.outputs.prerelease }} - VERSION: ${{ steps.version.outputs.version }} - run: | - if [ "$PRERELEASE" == "true" ] - then - PREV=$(git describe --tags --abbrev=0 HEAD^) - else - PREV=$(git describe --tags --match "v[0-9]*.[0-9]*" --first-parent --abbrev=0 HEAD^) - fi - PREVFULL=$(git describe --tags --match "v[0-9]*.[0-9]*" --first-parent --abbrev=0 HEAD^) - CUR="$(git rev-parse HEAD)" - echo "previousrelease=$PREV" >> $GITHUB_OUTPUT - echo "previousfullrelease=$PREVFULL" >> $GITHUB_OUTPUT - echo "currentrelease=$CUR" >> $GITHUB_OUTPUT - LAST_TAG=$(git describe --tags --match 'v[0-9]*.[0-9]*' --abbrev=0 HEAD^) - RAW_CHANGELOG=$(echo "$(git log --first-parent ${LAST_TAG}.. --pretty=format:'%D-g%h: %s' | sed -e 's/tag: //' -e 's/HEAD -> main, //')" | sed -e "s/origin\/main/$VERSION/" | tac) - echo "rawchangelog=${RAW_CHANGELOG//$'\n'/'\n'}" >> $GITHUB_OUTPUT - - - name: Echo Changelog (for debugging purposes) - env: - CHANGELOG: ${{ steps.rawchangelogdata.outputs.rawchangelog}} - run: | - echo "CHANGELOG=$CHANGELOG" - - - name: Set custom app name and package name, if relevant - id: github - env: - PRERELEASE: ${{ steps.version.outputs.prerelease }} - run: | - # For a PR action (i.e., synchronize / open), the value of github.ref will be refs/pull/1234/merge - # For a push action, it will be either refs/heads/foo_branch_name OR refs/tags/v1234. - # We want to use the base name for pushes of tags or to main, the PR number for PRs, and the branch name for named branches. - if [[ "$PRERELEASE" == "false" || ${{ github.ref }} == refs/heads/main ]] - then - echo "basename=OpenBrush" >> $GITHUB_OUTPUT - echo "description=" >> $GITHUB_OUTPUT - else - if [[ ${{ github.ref }} == refs/pull/* ]] - then - DESCRIPTION="PR#$(echo ${{ github.ref }} | sed -e 's#refs/pull/##' -e 's#/merge##')" - elif [[ ${{ github.ref }} == refs/heads/* ]] - then - DESCRIPTION="$(echo ${{ github.ref }} | sed -e 's#refs/heads/##')" - else - DESCRIPTION="Unknown" - fi - echo "description=-btb-description ${DESCRIPTION}" >> $GITHUB_OUTPUT - IDENTIFIER=$(echo ${DESCRIPTION} | sed -e 's/[\/#_-]//g') - echo "basename=OpenBrush-${IDENTIFIER}" >> $GITHUB_OUTPUT - fi - echo "uid=$(id -u)" >> $GITHUB_OUTPUT - echo "gid=$(id -g)" >> $GITHUB_OUTPUT - - - name: Determine whether to build Development builds or not - id: flavors - run: | - set -x - if [[ $(git log --format=%B ${{ github.event.pull_request.head.sha }} -1) == *'[CI BUILD DEV]'* ]] - then - echo 'flavors=[{"development": true, "title": "Development"}, {"development": false}]' >> $GITHUB_OUTPUT - - else - echo 'flavors=[{"development": false}]' >> $GITHUB_OUTPUT - fi - - build: - name: ${{ matrix.name }} ${{ matrix.flavors.title }} - needs: configuration - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - flavors: ${{ fromJson(needs.configuration.outputs.flavors) }} - name: [Windows OpenXR, Windows Pimax, Windows Rift, Linux, MacOS, Android OpenXR, Oculus Quest (1), Oculus Quest (2+), Android Pico, Android Pico (CN), iOS Zapbox] # These will all be overwritten, but because we have the flavors matrix as well, we can't just add configurations via include; they'll overwrite each other. This way ensures that we get each one - include: - - name: Windows OpenXR - targetPlatform: StandaloneWindows64 - vrsdk: OpenXR - cache: Windows - - - name: Windows Pimax - targetPlatform: StandaloneWindows64 - vrsdk: OpenXR - cache: Windows - extra_defines: PIMAX_SUPPORTED - - - name: Windows Rift - targetPlatform: StandaloneWindows64 - vrsdk: Oculus - cache: Windows - extra_defines: OCULUS_SUPPORTED - - - name: Linux - targetPlatform: StandaloneLinux64 - vrsdk: Monoscopic # All builds include monoscopic, but this one has no additional XrSdk, so we'll keep the name monoscopic - cache: Linux - - - name: MacOS - targetPlatform: StandaloneOSX - vrsdk: Monoscopic - cache: MacOS - packages_to_remove: com.meta.xr.sdk.core - - - name: Android OpenXR - targetPlatform: Android - vrsdk: OpenXR - cache: Android_Vulkan - extraoptions: -btb-il2cpp - versionSuffix: 0 - - - name: Oculus Quest (1) - targetPlatform: Android - vrsdk: OpenXR - cache: Android_Vulkan - extraoptions: -btb-il2cpp - versionSuffix: 0 - extra_defines: USE_QUEST_PACKAGE_NAME FORCE_QUEST_SUPPORT_DEVICE FORCE_FOCUSAWARE FORCE_HEADTRACKING - packages_to_remove: com.meta.xr.sdk.platform com.meta.xr.sdk.utilities - - - name: Oculus Quest (2+) - targetPlatform: Android - vrsdk: Oculus - cache: Android_Vulkan - extraoptions: -btb-il2cpp - versionSuffix: 1 - extra_defines: OCULUS_SUPPORTED USE_QUEST_PACKAGE_NAME - - - name: Android Pico - targetPlatform: Android - vrsdk: Pico - cache: Android_GLES - extraoptions: -btb-il2cpp - versionSuffix: 0 - extra_defines: PICO_SUPPORTED - - - name: Android Pico (CN) - targetPlatform: Android - vrsdk: Pico - cache: Android_GLES - # Pico requested Chinese build that doesn't have google/sketchfab login. - extraoptions: -btb-il2cpp -btb-disableAccountLogins - versionSuffix: 1 - extra_defines: PICO_SUPPORTED - - - name: iOS Zapbox - targetPlatform: iOS - vrsdk: Zapbox - cache: iOS - extraoptions: -btb-il2cpp - extra_defines: ZAPBOX_SUPPORTED - packages_to_remove: com.unity.formats.usd - - steps: - - name: Set masking - run: echo "::add-mask::DoNotStealThis1" - - name: Free extra space - # As of 02/08/2024, this increases free space from 21GB to 47GB - run: | - echo "Initial free space" - df -h / - echo "Removing all pre-loaded docker images" - docker rmi $(docker image ls -aq) # Removes ~3GB - df -h / - echo "Listing 100 largest packages" - dpkg-query -Wf '${Installed-Size}\t${Package}\n' | sort -rn | head -n 100 - echo "Removing unneeded large packages" - sudo apt update - sudo apt remove -y '^ghc-.*' '^dotnet-.*' azure-cli powershell google-chrome-stable firefox microsoft-edge-stable 'mongodb-*' 'mysql-*' 'mariadb-*' 'temurin-*' 'openjdk-*' default-jre-headless mono-devel libgl1-mesa-dri # Removes ~6GB - sudo apt autoremove -y - sudo apt clean - df -h / - echo "Removing Android" - sudo rm -rf /usr/local/lib/android # Removes ~9GB - df -h / - echo "Removing remaining large directories" - rm -rf /usr/share/dotnet/ # Removes ~1GB - rm -rf "$AGENT_TOOLSDIRECTORY" # Removes ~7GB - echo "Disk space after cleanup" - df -h / - - - name: Checkout repository - uses: actions/checkout@v4 - with: - lfs: true # We don't use LFS, but it adds no time, and leave it here in case we do at some point later - - - name: Install Pimax unity package - if: startsWith(matrix.name, 'Windows Pimax') - run: | - # version 0.6.3 - # Same as above, but adapted to work for Pimax instead. - wget -q https://dl.appstore.pimax.com/sdk/Pimax_Platform_Unity_SDK_v0.6.3.zip -O package.zip - unzip package.zip - mkdir tmp - tar -C tmp -xzf PimaxPlatform_v0.6.3.unitypackage - find tmp -type f | xargs chmod a-x - for pn in tmp/*/pathname; do - id=${pn%/*} - id=${id#*/} - p=$(head -1 $pn) - d=${p%/*} - mkdir -p "tmp/$d" - [ -f "tmp/$id/asset" ] && cp -v "tmp/$id/asset" "tmp/$p" - cp "tmp/$id/asset.meta" "tmp/${p}.meta" - done - cp -R tmp/Assets/Pimax Assets/ - rm -rf tmp package.zip PimaxPlatform_v0.6.3.unitypackage Tools - - - name: Install Pico unity package - if: matrix.vrsdk == 'Pico' - run: | - # version 2.1.1 - 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: | - # version 3.0.6; must be updated if the version changes - # This replaces the GUI's "Window -> TextMesh Pro -> Import TMP Essential Resources". I don't know why Unity makes this sort of thing so hard! - mkdir tmp.plugin - wget -q https://download.packages.unity.com/com.unity.textmeshpro/-/com.unity.textmeshpro-3.0.6.tgz -O tmp.plugin/plugin.tgz - tar -C tmp.plugin -xzf tmp.plugin/plugin.tgz - mkdir tmp.package - tar -C tmp.package -xzf 'tmp.plugin/package/Package Resources/TMP Essential Resources.unitypackage' - for pn in tmp.package/*/pathname; do - id=${pn%/*} - id=${id#*/} - p=$(head -1 $pn) - d=${p%/*} - mkdir -p "tmp.package/$d" - [ -f "tmp.package/$id/asset" ] && cp -v "tmp.package/$id/asset" "tmp.package/$p" - cp "tmp.package/$id/asset.meta" "tmp.package/${p}.meta" - done - mkdir -p 'Assets/TextMesh Pro' - 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 - ref: Fusion_v1.1.10_Voice_2 - 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@v4 - env: - SEGMENT_DOWNLOAD_TIMEOUT_MINS: 10 - with: - path: Library - # Some platforms share a cache; it's not a 1:1 mapping of either targetPlatform or vrsdk, so we have a distinct variable for which cache to use - key: Library_${{ matrix.cache }}_${{ env.UNITY_VERSION }} - - - name: Restore Library/PackageCache - id: cache_packagecache - uses: actions/cache/restore@v4 - env: - SEGMENT_DOWNLOAD_TIMEOUT_MINS: 10 - with: - path: Library/PackageCache - key: Library_PackageCache_${{ env.UNITY_VERSION }}_${{ hashFiles('Packages/packages-lock.json') }} - restore-keys: | - Library_PackageCache_${{ env.UNITY_VERSION }} - Library_PackageCache - - - name: Remove problematic packages - if: ${{ matrix.packages_to_remove }} - run: | - cp Packages/manifest.json{,.bak} - cp Packages/packages-lock.json{,.bak} - for PACKAGE in ${{ matrix.packages_to_remove }}; do - cat Packages/manifest.json | jq 'del( .dependencies ["'${PACKAGE}'"] )' > Packages/manifest.json.new - mv Packages/manifest.json.new Packages/manifest.json - cat Packages/packages-lock.json | jq 'del( .dependencies ["'${PACKAGE}'"] )' > Packages/packages-lock.json.new - mv Packages/packages-lock.json.new Packages/packages-lock.json - done - diff -u Packages/manifest.json.bak Packages/manifest.json || true - diff -u Packages/packages-lock.json.bak Packages/packages-lock.json || true - - - name: Set output filename - env: - BASENAME: ${{ needs.configuration.outputs.basename }} - run: | - if [[ "${{ matrix.targetPlatform}}" == "StandaloneWindows64" ]]; then - echo "filename=$BASENAME.exe" >> $GITHUB_ENV - elif [[ "${{ matrix.targetPlatform}}" == "StandaloneLinux64" ]]; then - echo "filename=$BASENAME" >> $GITHUB_ENV - elif [[ "${{ matrix.targetPlatform}}" == "iOS" ]]; then - echo "filename=$BASENAME" >> $GITHUB_ENV - elif [[ "${{ matrix.targetPlatform}}" == "StandaloneOSX" ]]; then - echo "filename=$BASENAME.app" >> $GITHUB_ENV - elif [[ "${{ matrix.targetPlatform}}" == "Android" ]]; then - echo "filename=com.Icosa.$BASENAME.apk" >> $GITHUB_ENV - fi - - - name: Set build stamp - if: ${{ needs.configuration.outputs.stamp }} - # We checkout the merge commit, but for the purpose of the tag, use the version from the PR, not the merge commit, which is rather hard to find later. We skip the version tag, since this comes from the code and can't be easily overwritten - run: | - echo "stamp=-btb-stamp ${{needs.configuration.outputs.stamp}}" >> $GITHUB_ENV - - - name: Enable Development Mode - if: ${{ matrix.flavors.development == true }} - run: | - echo "btbbopts=-btb-bopt Development" >> $GITHUB_ENV - - - name: Update version - env: - VERSION: ${{ needs.configuration.outputs.version}} - run: | - sed -e "s/m_VersionNumber:.*$/m_VersionNumber: $VERSION/" -i Assets/Scenes/Main.unity - sed -e "s/bundleVersion:.*$/bundleVersion: $VERSION/" -i ProjectSettings/ProjectSettings.asset - - - name: Add secure secrets file - env: - SECRETS_ASSET: ${{ secrets.SECRETS_ASSET }} - SECRETS_ASSET_META: ${{ secrets.SECRETS_ASSET_META }} - if: | - env.SECRETS_ASSET != null && - env.SECRETS_ASSET_META != null - run: | - echo "$SECRETS_ASSET" > Assets/Secrets.asset - echo "$SECRETS_ASSET_META" > Assets/Secrets.asset.meta - SECRETS_ASSET_META_GUID=$(grep "guid:" Assets/Secrets.asset.meta | cut -d" " -f2) - sed -e "s/Secrets:.*$/Secrets: {fileID: 11400000, guid: $SECRETS_ASSET_META_GUID, type: 2}/" -i Assets/Scenes/Main.unity - - - name: Enable keystore - run: | - sed -e 's/androidUseCustomKeystore.*$/androidUseCustomKeystore: 1/' -i ProjectSettings/ProjectSettings.asset - - - name: Add PHOTON_PAT specific define - if: ${{ env.PHOTON_PAT }} - run: | - echo -e "\n -define:PHOTON_UNITY_NETWORKING \n-define:PUN_2_0_OR_NEWER \n-define:PUN_2_OR_NEWER \n-define:PUN_2_19_OR_NEWER \n-define:FUSION_WEAVER \n-define:CROSS_PLATFORM_INPUT \n-define:MOBILE_INPUT \n-define:PHOTON_VOICE_DEFINED" | tee -a Assets/csc.rsp - - - name: Update build matrix specific defines in csc.rsp - if: ${{ matrix.extra_defines }} - run: | - for DEFINE in ${{ matrix.extra_defines }}; do - echo -e "\n-define:$DEFINE" | tee -a Assets/csc.rsp - done - - - name: Build project - uses: Wandalen/wretry.action@v3 - env: - VERSION: ${{ needs.configuration.outputs.version}} - UNITY_EMAIL: ${{ fromJSON(format('["unitytest@mikeage.net", "{0}"]', vars.UNITY_EMAIL))[secrets.UNITY_SERIAL != null] }} - UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }} - 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] }} - with: - retry_condition: steps._this.outputs.engineExitCode == 1 - action: game-ci/unity-builder@v4 - with: | - allowDirtyBuild: true # Because of the OVR Update, the build tree might be dirty - unityVersion: ${{ env.UNITY_VERSION }} - targetPlatform: ${{ matrix.targetPlatform }} - customParameters: -btb-target ${{ matrix.targetPlatform }} -btb-display ${{ matrix.vrsdk }} -btb-out /github/workspace/build/${{ matrix.vrsdk }}/${{ matrix.targetPlatform }}/${{ env.filename }} ${{ needs.configuration.outputs.description}} ${{ env.stamp }} ${{ env.btbbopts }} ${{ matrix.extraoptions }} - versioning: Custom - androidVersionCode: "${{ needs.configuration.outputs.androidVersionCode }}${{ matrix.versionSuffix }}" - version: ${{ needs.configuration.outputs.version }} - buildName: ${{ needs.configuration.outputs.basename }} - buildsPath: build/${{ matrix.vrsdk }} - chownFilesTo: ${{ needs.configuration.outputs.uid }}:${{ needs.configuration.outputs.gid }} - buildMethod: BuildTiltBrush.CommandLine - androidKeystoreName: openbrush.keystore - androidKeystoreBase64: ${{ secrets.ANDROID_KEYSTORE_BASE64 || '/u3+7QAAAAIAAAABAAAAAQAWb3BlbmJydXNoLW5vbi1vZmZpY2lhbAAAAX66M2FtAAAFATCCBP0wDgYKKwYBBAEqAhEBAQUABIIE6Wufa9OVstw7Bu/gdATKqoPafXGefygChsN1d4LGY0SMLPORjHXiryEVMKi2rt61kNeXzeLkiM4yIQAam4HZtNTxgjoFQ6KB7uzkqMJYKViBUgg1HCAl2e+QpYjqG+YNJT67CiPgjpsJHNE628CwKAvjJ85FhqFz+MKzNF8BOpS5g5waqFda67oxaE4qO8eAL+F9P7us+ziY5B4O3EJC9s7xpT2GV2ro0m0fZI2dr3OO9UdUO72CYTg5qs250JiSij26Haf4t8Vq28F2S8rTcMUVtN4FRtzeR/wjeeZ3laER+WoxYni4MrZEXhYYCGhfor8Zcfi3p5ka8TJCQxywTKpghpSwgykgMJLn1HksxB0vhIMGTb87c2CTqS4t5Js/OPdcYS4Jnr7mHdQtOGfJCvl3TJC7NJwzLLOzUTmVIogaZCA9GlRballbD7XYbR8mcPxs+jLq5HJJk8/3B8ojAz/YA9vp6ml3RSYDA+yv9fBIefxNniAredJeqAnmH4o9er3+n0rKmpoqiXdzFkp1ywYbDDxrsFTiPrTc0gEiLRbfCERBx8GZ/7zGv6exKW1mc1L7QcFRmT1PRuJo6vRfCOtjdAdp0Mj1bllGGe9oBSKOxqtxs/NFygaVZjMDqryRvObKaJaj5CDhNdwsa21EsQ3+YvQWBzlcs5FTi5S2zG3W4+tMb+HoyV36SEV4yBLtqqrczhVCuPMlZu2p1iFLyODJJOxrWnmZy49BlQiudmiR7wILJoYKIFFvGv1jCJnTl9cI6UGX8IwSHYjGJIdLxaQM6c/7tw15+h+3jPajzZqkIQ7r0fyBp2TxE+QXMCP/knYu/dVzzQoBe5CgnAr5Fj60eEF78mJZbU3m9EjuVglURCTs2hDiyl3eRENgJjTc8p9iho4aK5eT5BVF7v2TAsTkfm+AwOq78chbWfh7J5OYnycG+v6S76LE6T8Yy0Arkk4lOF5SC05SmrDQpFcbRC9B7pR8XwJx3rabt4jvFsdqQtqv7TRasNQs95oROSC8335tzsaQfPwL/sGH4wi4zsH3YZ6As2V9myMEytqVEX5DdGBtzRr1opkx0aisyG48Evtk1UHMR9ROoZmkbNOIFNDUxCBvw7CU20aJSri4GX7kahg8Lj670Lfpx1C9OMwH0xRGUHE4e2ZWaw6Smkjc0Rru7j4YFKel0KtJgQaei2fz2i+6wOv1uz+H4j6f98pVMsf3HODmnh4x+qlUXaJWbNILQEGwv3zVReY123TPHIzkwImNLej62BLaqnEgiPkKr/gp/2MdrgepUEGC8FN0MTPbazDR4aE5XqLtnehhq8/9EfIk3b5WzNh00IAELwFrWnabkob5xmSLORBH8SpS3J6NwWa4jJMADRAGPYOUH7tYUM1/GRUK1HuboNP9v3KAny/k30CrxLvNHwe/zkXgoU9+M+gXVXL8pJJLMawVe/Dg13XyqTTa00UX7TsQFJZGm6lHrgeFIejKBEMLsMXNAIccphZe6sDnycDm/GY8vqmfjg9R05GwJOhBd46vhDi7Ph8YbLjohEoT4KfE5o8+Norzc/VHbRv5Y+G6JCL6hRV72meb3LswLYGUzGYP4nh2Y/yixg+rAtre80xjbXFfdvXVF6CuibKn5gmjCmiRN31rvEfdwVPIQDCaqv19Do2cQYDN+yGCo7yDHAAAAAEABVguNTA5AAADhzCCA4MwggJroAMCAQICBGNtJJswDQYJKoZIhvcNAQELBQAwcTELMAkGA1UEBhMCR0IxFzAVBgNVBAgTDldlc3QgWW9ya3NoaXJlMQ4wDAYDVQQHEwVMZWVkczEOMAwGA1UEChMFSWNvc2ExEzARBgNVBAsTCk9wZW4gQnJ1c2gxFDASBgNVBAMTC01pa2UgTWlsbGVyMCAXDTIyMDIwMjExMzAyNVoYDzIwNzIwMTIxMTEzMDI1WjBxMQswCQYDVQQGEwJHQjEXMBUGA1UECBMOV2VzdCBZb3Jrc2hpcmUxDjAMBgNVBAcTBUxlZWRzMQ4wDAYDVQQKEwVJY29zYTETMBEGA1UECxMKT3BlbiBCcnVzaDEUMBIGA1UEAxMLTWlrZSBNaWxsZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCZOlUSd2Z9VSuVE1NK2AKiKCYR3ADh3f3PN6ipTtqUdxP44l5jJnPVXc5YXJ4DyBsXHGTqCSiL9wiqdRCNTMcRf6vrpcuRWxqwMMu4bid0eDiFBU+wModQl70N0VblMolYZzD/y0NpXWh7VKPSXyA22ZwygeOPQFzxR4j2jRvM/g+9HeJeVN1p5f+6pvceg/9FBSCEOQg5fbDtO+ytZfMiawcyhSSwwlOzEOGT0Dq6d9xIs1/zTA8LxAlGYHLSpQCT/n3X27LNgUMNrCpWgLTtxH/qQ61NU3juqTqBBWT4nzTXl1J9JyPaHH1yzC908YiI5PQSFehX80KTvsf0B65DAgMBAAGjITAfMB0GA1UdDgQWBBTThSJ0yfVNgUC4h3Sa9o8aUmLY3jANBgkqhkiG9w0BAQsFAAOCAQEAUqE9NJA+PaMBrCcVHkxmk32DsVNIVCM/eaTPCyjBM3V5COgxscven160OKGHRn6Xhplr/UDy+StphE9Hwk8MAwSJ4reBdPiNMQvIsDEQ/aXSAyTiKQeIU5Zc+cYuJvHcyxIOVektDe8Er2AITvpXQDK1JRvYU6lFKym3j/CZ4comUwjdolB1C6fzlTkhP3ZuuFMfv543WyuVtb3A1mioLzQ5kfFlbTO0uXqEm+gltkK8AMqU6B5RJDYtQXIJkjR//UzNgpaILVvQ4pyyS6VvBNbUbrHaUKabtP3daDtQ0AQw3gSkCJ+QPpY9joIq38LMcVY5/x5/nbcxTuYvUlHozn/+qtNvA7MtikSNPcblNpmifg4o' }} - androidKeystorePass: ${{ secrets.ANDROID_KEYSTORE_PASS || 'FakeKey' }} - androidKeyaliasName: ${{ secrets.ANDROID_KEYALIAS_NAME || 'openbrush-non-official' }} - androidKeyaliasPass: ${{ secrets.ANDROID_KEYALIAS_PASS || 'FakeKey' }} - - - name: Prepare for packaging (permissions and compression, OSX only) - if: matrix.targetPlatform == 'StandaloneOSX' - run: | - mv build/${{ matrix.vrsdk }}/${{ matrix.targetPlatform }}/Support "build/${{ matrix.vrsdk }}/${{ matrix.targetPlatform }}/${{ env.filename }}/Contents/" - find build -name 'UnityFbxSdkNative.bundle' -delete - # Compress, but skip the top directories - tar -c -v -z -f OpenBrush.tgz -C build/${{ matrix.vrsdk }} ${{ matrix.targetPlatform}} - rm -rf build/${{ matrix.vrsdk }}/* - mv OpenBrush.tgz build/${{ matrix.vrsdk }}/ - - - name: Upload build/ - uses: actions/upload-artifact@v4 - with: - name: ${{ matrix.name }} ${{ matrix.flavors.title }} - path: | - build/${{ matrix.vrsdk }} - !build/Pico/*.symbols.zip - !build/**/*_BackUpThisFolder_ButDontShipItWithYourGame - - - name: Check if packages-lock.json has changed or if it's cacheable - id: check_packagecache - run: | - # Check if there are any changes to the packages-lock.json file - set +e - git diff --exit-code -- Packages/packages-lock.json - CHANGES="$?" - set -e - echo "changes=$CHANGES" >> $GITHUB_OUTPUT - echo "diff returned: $CHANGES" - - - name: Save Library/PackageCache cache - uses: actions/cache/save@v4 - if: github.ref == 'refs/heads/main' && steps.check_packagecache.outputs.changes == 0 && steps.cache_packagecache.outputs.cache-hit != 'true' && ! matrix.packages_to_remove # Ideally, we'd save caches on branches, but they're too big, and branch caches can evict those from main, which is unacceptable. - env: - SEGMENT_DOWNLOAD_TIMEOUT_MINS: 10 - with: - path: Library/PackageCache - key: Library_PackageCache_${{ env.UNITY_VERSION }}_${{ hashFiles('Packages/packages-lock.json') }} - - - name: Clean Library before caching - if: github.ref == 'refs/heads/main' && steps.cache_library.outputs.cache-hit != 'true' # Ideally, we'd save caches on branches, but they're too big, and branch caches can evict those from main, which is unacceptable. - run: | - # Remove the large files from the Library directory that we know we'll rebuild. As our il2cpp caches are huge and barely fit in the Github quota, it's better not to save an unneeded 1GB of space (or so). If a new Unity version is taken, this may need to be updated - # Debugging - echo "Library/ directories" - du -mcsh Library/* - find Library -size +50M -exec ls -altrh {} \; - # chown all files, since some are owned by root after the docker run - docker run -v $(pwd)/Library:/mnt alpine chown $(id -u).$(id -g) -R /mnt/ - # Print the files to be deleted - find Library/Bee/ -name 'symbols.zip' -or -name 'libil2cpp*.so' -or -name 'launcher-release.apk' | tee todelete.txt - cat todelete.txt | xargs -r rm - # The package cache is stored in a separate, shared, cache - rm -rf Library/PackageCache - echo "Final space used" - du -mcsh Library - - - name: Save Library/ cache - uses: actions/cache/save@v4 - if: github.ref == 'refs/heads/main' && steps.cache_library.outputs.cache-hit != 'true' # Ideally, we'd save caches on branches, but they're too big, and branch caches can evict those from main, which is unacceptable. - env: - SEGMENT_DOWNLOAD_TIMEOUT_MINS: 10 - with: - path: Library - # Some platforms share a cache; it's not a 1:1 mapping of either targetPlatform or vrsdk, so we have a distinct variable for which cache to use - key: Library_${{ matrix.cache }}_${{ env.UNITY_VERSION }} - - signmacos: - name: Sign the MacOS .app - needs: [configuration, build] - if: | - github.event_name == 'push' && - github.repository == 'icosa-foundation/open-brush' && - (github.ref == 'refs/heads/main' || contains(github.ref, 'refs/tags/v')) - runs-on: macos-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - fetch-depth: 0 - lfs: true # We don't use LFS, but it adds no time, and leave it here in case we do at some point later - sparse-checkout: | - Support/macos - - - name: Download Build Artifacts - uses: actions/download-artifact@v4 - with: - name: MacOS - path: build_macos - - # See https://docs.github.com/en/actions/deployment/deploying-xcode-applications/installing-an-apple-certificate-on-macos-runners-for-xcode-development - - name: Install the Apple certificate and provisioning profile - env: - BUILD_CERTIFICATE_BASE64: ${{ secrets.APPLE_BUILD_CERTIFICATE_BASE64 }} - INSTALL_CERTIFICATE_BASE64: ${{ secrets.APPLE_INSTALL_CERTIFICATE_BASE64 }} - P12_PASSWORD: ${{ secrets.APPLE_P12_PASSWORD }} - BUILD_PROVISION_PROFILE_BASE64: ${{ secrets.APPLE_BUILD_PROVISION_PROFILE_BASE64 }} - KEYCHAIN_PASSWORD: ${{ secrets.APPLE_KEYCHAIN_PASSWORD }} - run: | - BUILD_CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12 - INSTALL_CERTIFICATE_PATH=$RUNNER_TEMP/install_certificate.p12 - PP_PATH=$RUNNER_TEMP/openbrushmac.provisionprofile - KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db - - echo -n "$BUILD_CERTIFICATE_BASE64" | base64 --decode -o $BUILD_CERTIFICATE_PATH - echo -n "$INSTALL_CERTIFICATE_BASE64" | base64 --decode -o $INSTALL_CERTIFICATE_PATH - echo -n "$BUILD_PROVISION_PROFILE_BASE64" | base64 --decode -o $PP_PATH - - # create temporary keychain - security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH - security set-keychain-settings -lut 21600 $KEYCHAIN_PATH - security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH - - # import certificate to keychain - security import $BUILD_CERTIFICATE_PATH -P "$P12_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH - security import $INSTALL_CERTIFICATE_PATH -P "$P12_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH - security list-keychain -d user -s $KEYCHAIN_PATH - - mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles - cp $PP_PATH ~/Library/MobileDevice/Provisioning\ Profiles - - - name: Sign the release - env: - VERSION: ${{ needs.configuration.outputs.version }} - run: | - tar xvfz build_macos/*tgz - - export FILENAME=$(basename $(readlink -f StandaloneOSX/OpenBrush*.app)) - - cd StandaloneOSX/ - codesign --deep --force --verify --verbose --timestamp --options runtime --entitlements ../Support/macos/OpenBrush.entitlements --sign "Developer ID Application: Icosa Gallery Ltd (${{ vars.APPLE_TEAM_ID }})" $FILENAME/Contents/Support/ThirdParty/ffmpeg/bin/ffmpeg - for bundle in $FILENAME/Contents/Plugins/*.bundle; do - codesign --deep --force --verify --verbose --timestamp --options runtime --entitlements ../Support/macos/OpenBrush.entitlements --sign "Developer ID Application: Icosa Gallery Ltd (${{ vars.APPLE_TEAM_ID }})" $bundle - done - codesign --deep --force --verify --verbose --timestamp --options runtime --entitlements ../Support/macos/OpenBrush.entitlements --sign "Developer ID Application: Icosa Gallery Ltd (${{ vars.APPLE_TEAM_ID }})" $FILENAME - cd - - - tar -c -v -z -f OpenBrush.tgz StandaloneOSX - - name: Upload signed app - uses: actions/upload-artifact@v4 - with: - name: MacOS (signed) - path: | - OpenBrush.tgz - - createdmg: - name: Create and Notarize DMG - needs: [configuration, signmacos] - if: | - github.event_name == 'push' && - github.repository == 'icosa-foundation/open-brush' && - (github.ref == 'refs/heads/main' || contains(github.ref, 'refs/tags/v')) - - runs-on: macos-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - fetch-depth: 0 - lfs: true # We don't use LFS, but it adds no time, and leave it here in case we do at some point later - sparse-checkout: | - Support/macos - - - name: Download Build Artifacts - uses: actions/download-artifact@v4 - with: - name: MacOS (signed) - path: build_macos - - - name: Create a notarized DMG - env: - VERSION: ${{ needs.configuration.outputs.version }} - run: | - tar xvfz build_macos/*tgz - - export FILENAME=$(basename $(readlink -f StandaloneOSX/OpenBrush*.app)) - mkdir dist - - cp Support/macos/background.png StandaloneOSX/ - cp Support/macos/background@2x.png StandaloneOSX/ - pip install jinjanator - jinjanate Support/macos/openbrush-dmg.json.j2 > StandaloneOSX/openbrush-dmg.json - pushd StandaloneOSX/ - npx appdmg openbrush-dmg.json ../dist/OpenBrush.dmg - popd - - xcrun notarytool submit \ - --team-id ${{ vars.APPLE_TEAM_ID }} \ - --apple-id ${{ vars.APPLE_ID }} \ - --password ${{ secrets.APPLE_APPLICATION_SPECIFIC_PASSWORD }} \ - --wait \ - dist/OpenBrush.dmg - - xcrun stapler staple dist/OpenBrush.dmg - - - name: Upload notarized dmg - uses: actions/upload-artifact@v4 - with: - name: MacOS (DMG) - path: | - dist/OpenBrush.dmg - - release: - name: Create Github Release - needs: [configuration, build, createdmg] - runs-on: ubuntu-latest - if: | - github.event_name == 'push' && - github.repository == 'icosa-foundation/open-brush' && - (github.ref == 'refs/heads/main' || contains(github.ref, 'refs/tags/v')) - - steps: - - name: "Build Changelog" - id: changelog - uses: mikepenz/release-changelog-builder-action@v5 - with: - fromTag: "${{ needs.configuration.outputs.previousrelease }}" - toTag: "${{ needs.configuration.outputs.currentrelease }}" - configurationJson: | - { - "categories": [ - { - "title": "## 🚀 Features", - "labels": ["feature", "enhancement"] - }, - { - "title": "## 🎨 UI / UX", - "labels": ["ux"] - }, - { - "title": "## 🐛 Fixes", - "labels": ["fix", "bugfix"] - }, - { - "title": "## 🛠️ Infrastructure", - "labels": ["infrastructure"] - }, - { - "title": "## 📦 Dependencies / Maintenance", - "labels": ["dependencies", "maintenance"] - }, - { - "title": "## 💬 Uncategorized", - "labels": [] - } - ], - "pr_template": "- #{{TITLE}} (PR ##{{NUMBER}} by @#{{AUTHOR}})" - } - - - name: Echo Changelog (for debugging purposes) - env: - CHANGELOG: ${{ steps.changelog.outputs.changelog }} - run: echo "$CHANGELOG" - - - name: Download Build Artifacts (Windows OpenXR) - uses: actions/download-artifact@v4 - with: - name: Windows OpenXR - path: build_windows_openxr - - - name: Download Build Artifacts (Windows Rift) - uses: actions/download-artifact@v4 - with: - name: Windows Rift - path: build_windows_rift - - - name: Download Build Artifacts (Android OpenXR) - uses: actions/download-artifact@v4 - with: - name: Android OpenXR - path: build_android_openxr - - - name: Download Build Artifacts (Oculus Quest 2+) - uses: actions/download-artifact@v4 - with: - name: Oculus Quest (2+) - path: build_oculus_quest - - - name: Download Build Artifacts (Pico) - uses: actions/download-artifact@v4 - with: - name: Android Pico - path: build_android_pico - - - name: Download Build Artifacts (Linux) - uses: actions/download-artifact@v4 - with: - name: Linux - path: build_linux - - - name: Download Build Artifacts (Mac) - uses: actions/download-artifact@v4 - with: - name: MacOS (DMG) - path: build_macos - - - name: Package Artifacts for release - env: - VERSION: ${{ needs.configuration.outputs.version }} - run: | - mkdir releases - mv build_oculus_quest/*/com.Icosa.OpenBrush*apk releases/OpenBrush_Quest_$VERSION.apk - mv build_android_openxr/*/com.Icosa.OpenBrush*apk releases/OpenBrush_OpenXR_$VERSION.apk - mv build_android_pico/*/com.Icosa.OpenBrush*apk releases/OpenBrush_Pico_$VERSION.apk - mv build_windows_openxr/StandaloneWindows64/ releases/OpenBrush_Desktop_$VERSION/ - mv build_windows_rift/StandaloneWindows64/ releases/OpenBrush_Rift_$VERSION/ - mv build_macos/*.dmg releases/OpenBrush_Mac_$VERSION.dmg - mv build_linux/StandaloneLinux64/ releases/OpenBrush_Linux_$VERSION/ - cd releases - zip -r OpenBrush_Desktop_$VERSION.zip OpenBrush_Desktop_$VERSION/ - rm -rf OpenBrush_Desktop_$VERSION - zip -r OpenBrush_Rift_$VERSION.zip OpenBrush_Rift_$VERSION/ - rm -rf OpenBrush_Rift_$VERSION - zip -r OpenBrush_Linux_$VERSION.zip OpenBrush_Linux_$VERSION/ - - - name: Publish - uses: softprops/action-gh-release@v2 - with: - body: ${{ steps.changelog.outputs.changelog }} - prerelease: ${{ needs.configuration.outputs.prerelease }} - target_commitish: ${{ needs.configuration.outputs.currentrelease }} - tag_name: ${{ needs.configuration.outputs.version }} - files: releases/* - token: ${{ secrets.RELEASE_TOKEN }} - - publish_gitbook: - name: Publish changelog from last major build to open-brush-docs - needs: [configuration, build] - runs-on: ubuntu-latest - if: | - github.event_name == 'push' && - github.repository == 'icosa-foundation/open-brush' && - (github.ref == 'refs/heads/main' || contains(github.ref, 'refs/tags/v')) - steps: - - name: "Build Changelog" - id: changelog - uses: mikepenz/release-changelog-builder-action@v5 - with: - fromTag: "${{ needs.configuration.outputs.previousfullrelease }}" - toTag: "${{ needs.configuration.outputs.currentrelease }}" - configurationJson: | - { - "categories": [ - { - "title": "## 🚀 Features", - "labels": ["feature", "enhancement"] - }, - { - "title": "## 🎨 UI / UX", - "labels": ["ux"] - }, - { - "title": "## 🐛 Fixes", - "labels": ["fix", "bugfix"] - }, - { - "title": "## 🛠️ Infrastructure", - "labels": ["infrastructure"] - }, - { - "title": "## 📦 Dependencies / Maintenance", - "labels": ["dependencies", "maintenance"] - }, - { - "title": "## 💬 Uncategorized", - "labels": [] - } - ], - "template": "# Changelog since #{{FROM_TAG}}\n\n[Full release details](#{{RELEASE_DIFF}})\n\n#{{CHANGELOG}}\n\n", - "pr_template": "- #{{TITLE}} ([PR ##{{NUMBER}}](#{{URL}}) by @#{{AUTHOR}})\n" - } - - - name: Get the current contents of the docs repository - uses: actions/checkout@v4 - with: - repository: icosa-foundation/open-brush-docs - path: open-brush-docs - ref: master - fetch-depth: 0 - sparse-checkout: | - release-history/ - - - name: Create Changelog file - env: - CHANGELOG: ${{ steps.changelog.outputs.changelog }} - run: | - echo "$CHANGELOG" | tee open-brush-docs/release-history/automatic-changelog.md - - - name: Publish release notes - uses: cpina/github-action-push-to-another-repository@composite-1.5.1 - env: - SSH_DEPLOY_KEY: ${{ secrets.OPENBRUSH_DOCS_SSH_DEPLOY_KEY }} - with: - source-directory: 'open-brush-docs/release-history/' - target-directory: 'release-history/' - destination-github-username: 'icosa-foundation' - destination-repository-name: 'open-brush-docs' - user-name: 'release-note-bot' - user-email: automatic-release@icosa - target-branch: master - - publish_steam: - name: Publish Steam Release - needs: [configuration, build, signmacos] - if: | - github.event_name == 'push' && - github.repository == 'icosa-foundation/open-brush' && - (github.ref == 'refs/heads/main' || contains(github.ref, 'refs/tags/v')) - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - fetch-depth: 0 - sparse-checkout: | - Support/steam - lfs: true # We don't use LFS, but it adds no time, and leave it here in case we do at some point later - - name: Setup steamcmd - uses: CyberAndrii/setup-steamcmd@v1.2.0 - - name: Restore steam login config - run: | - mkdir -p /home/runner/Steam/config - echo "${{ secrets.STEAM_CONFIG_VDF}}" | base64 -d - | gunzip - > /home/runner/Steam/config/config.vdf - md5sum /home/runner/Steam/config/config.vdf - - name: Download Build Artifacts (Windows OpenXR) - uses: actions/download-artifact@v4 - with: - name: Windows OpenXR - path: build_windows_openxr - - name: Download Build Artifacts (MacOS Signed) - uses: actions/download-artifact@v4 - with: - name: MacOS (signed) - path: build_macos - - name: Download Build Artifacts (Linux) - uses: actions/download-artifact@v4 - with: - name: Linux - path: build_linux - - name: Upload Build - run: | - cd build_macos - tar xvfz OpenBrush.tgz - cd - - pip install -U jinjanator - jinjanate Support/steam/app.vdf.j2 > app.vdf - jinjanate Support/steam/main_depot.win.vdf.j2 > build_windows_openxr/main_depot.vdf - jinjanate Support/steam/main_depot.mac.vdf.j2 > build_macos/main_depot.vdf - jinjanate Support/steam/main_depot.linux.vdf.j2 > build_linux/main_depot.vdf - jinjanate Support/steam/installscript_win.vdf.j2 > build_windows_openxr/installscript_win.vdf - steamcmd +login $STEAM_USERNAME +run_app_build $(pwd)/app.vdf +quit - env: - STEAM_USERNAME: ${{ vars.STEAM_USERNAME }} - STEAM_PASSWORD: ${{ secrets.STEAM_PASSWORD }} - VERSION: ${{ needs.configuration.outputs.version }} - OPEN_BRUSH_APP_ID: ${{ vars.STEAM_APP_ID }} - OPEN_BRUSH_WINDOWS_DEPOT_ID: ${{ vars.STEAM_WINDOWS_DEPOT_ID }} - OPEN_BRUSH_MAC_DEPOT_ID: ${{ vars.STEAM_MAC_DEPOT_ID }} - OPEN_BRUSH_LINUX_DEPOT_ID: ${{ vars.STEAM_LINUX_DEPOT_ID }} - OPEN_BRUSH_WINDOWS_EXECUTABLE: ${{ needs.configuration.outputs.basename}}.exe - CHANNEL: beta - - name: Update steam login secret - run: | - gzip /home/runner/Steam/config/config.vdf -c | base64 | gh secret set --visibility all --org icosa-foundation STEAM_CONFIG_VDF - md5sum /home/runner/Steam/config/config.vdf - env: - GITHUB_TOKEN: ${{ secrets.SECRET_UPDATER_PAT }} - - name: Save logs - uses: actions/upload-artifact@v4 - if: ${{ failure() }} - with: - name: steamcmd logs - path: build_output/ - - publish_pimax: - name: Publish Pimax Release - needs: [configuration, build] - runs-on: windows-latest - env: - PIMAX_APP_ID: ${{ vars.PIMAX_APP_ID }} - PIMAX_USERNAME: ${{ vars.PIMAX_USERNAME }} - PIMAX_PASSWORD: ${{ secrets.PIMAX_PASSWORD }} - VERSION: ${{ needs.configuration.outputs.version}} - if: | - github.event_name == 'push' && - github.repository == 'icosa-foundation/open-brush' && - contains(github.ref, 'refs/tags/v') - - steps: - - name: Download Build Artifacts (Windows Pimax) - uses: actions/download-artifact@v4 - with: - name: Windows Pimax - path: build_windows_pimax - - - name: Publish Pimax Builds - run: | - New-Item "releases" -Type Directory - Move-Item -Path build_windows_pimax/StandaloneWindows64/* releases/ - Set-Location -Path "releases" - Compress-Archive -Path ./* -DestinationPath OpenBrush_Pimax_${Env:VERSION}.zip - Invoke-WebRequest -Uri https://dl.appstore.pimax.com/tools/pimax-dev-util.exe -OutFile pimax-dev-util.exe - ./pimax-dev-util.exe login -u $Env:PIMAX_USERNAME -p $Env:PIMAX_PASSWORD - ./pimax-dev-util.exe upload-pc-build -a $Env:PIMAX_APP_ID -d OpenBrush_Pimax_${Env:VERSION}.zip - - publish_viveport: - name: Publish Viveport Release - needs: [configuration, build] - runs-on: windows-latest - env: - VIVEPORT_EMAIL: ${{ vars.VIVEPORT_EMAIL }} - VIVEPORT_PASSWORD: ${{ secrets.VIVEPORT_PASSWORD }} - VIVEPORT_APP_ID: ${{ vars.VIVEPORT_APP_ID }} - VIVEPORT_ORG_ID: ${{ vars.VIVEPORT_ORG_ID }} - VERSION: ${{ needs.configuration.outputs.version}} - if: | - github.event_name == 'push' && - github.repository == 'icosa-foundation/open-brush' && - contains(github.ref, 'refs/tags/v') - - steps: - - name: Download Build Artifacts (Windows OpenXR) - uses: actions/download-artifact@v4 - with: - name: Windows OpenXR - path: build_windows_openxr - - - name: Publish Viveport Builds - run: | - New-Item "releases" -Type Directory - Move-Item -Path build_windows_openxr/StandaloneWindows64/* releases/ - Set-Location -Path "releases" - Compress-Archive -Path ./* -DestinationPath OpenBrush_Viveport_${Env:VERSION}.zip - Invoke-WebRequest -Uri https://assets-global.viveport.com/static-misc/developer-tool/ViveportUploader.exe -OutFile ViveportUploader.exe - ./ViveportUploader.exe -email ${Env:VIVEPORT_EMAIL} -password ${Env:VIVEPORT_PASSWORD} -filepath OpenBrush_Viveport_${Env:VERSION}.zip -appid ${Env:VIVEPORT_APP_ID} -orgid ${Env:VIVEPORT_ORG_ID} -version ${VERSION} - - publish_itch: - name: Publish Itch.io Release - needs: [configuration, build] - runs-on: ubuntu-latest - env: - ITCH_SUBCHANNEL_NAME: ${{ needs.configuration.outputs.itchchannelname }} - BUTLER_CREDENTIALS: ${{ secrets.BUTLER_CREDENTIALS }} - ITCH_GAME: openbrush - ITCH_USER: openbrush - VERSION: ${{ needs.configuration.outputs.version }} - if: | - github.event_name == 'push' && - github.repository == 'icosa-foundation/open-brush' && - (github.ref == 'refs/heads/main' || contains(github.ref, 'refs/tags/v')) - - steps: - - name: Download Build Artifacts (Windows OpenXR) - uses: actions/download-artifact@v4 - with: - name: Windows OpenXR - path: build_windows_openxr - - - name: Download Build Artifacts (Oculus Quest 2+) - uses: actions/download-artifact@v4 - with: - name: Oculus Quest (2+) - path: build_oculus_quest - - - name: Package Artifacts for release - run: | - mkdir releases - mv build_oculus_quest/*/com.Icosa.OpenBrush*apk releases/OpenBrush_Quest_$VERSION.apk - mv build_windows_openxr/StandaloneWindows64/ releases/OpenBrush_Desktop_$VERSION/ - - name: Publish Windows - uses: josephbmanley/butler-publish-itchio-action@master - env: - CHANNEL: windows-${{ env.ITCH_SUBCHANNEL_NAME }} - PACKAGE: releases/OpenBrush_Desktop_${{ needs.configuration.outputs.version }} - - name: Publish Quest - uses: josephbmanley/butler-publish-itchio-action@master - env: - CHANNEL: android-quest-${{ env.ITCH_SUBCHANNEL_NAME }} - PACKAGE: releases/OpenBrush_Quest_${{ needs.configuration.outputs.version }}.apk - - publish_oculus_quest: - name: Publish Oculus Quest 2+ Release - needs: [configuration, build] - runs-on: macos-latest # the ovr-platform-util tool is only available for Mac and Windows - if: | - github.event_name == 'push' && - github.repository == 'icosa-foundation/open-brush' && - (github.ref == 'refs/heads/main' || contains(github.ref, 'refs/tags/v')) - - steps: - - name: Download Build Artifacts (Oculus Quest 2+) - uses: actions/download-artifact@v4 - with: - name: Oculus Quest (2+) - path: build_oculus_quest - - name: Publish Oculus Builds - env: - VERSION: ${{ needs.configuration.outputs.version }} - PRERELEASE: ${{ needs.configuration.outputs.prerelease }} - RAW_CHANGELOG: ${{ needs.configuration.outputs.rawchangelog }} - OCULUS_QUEST_APP_ID: ${{ vars.OCULUS_QUEST_APP_ID }} - OCULUS_QUEST_APP_SECRET: ${{ secrets.OCULUS_QUEST_APP_SECRET }} - run: | - mkdir releases - mv build_oculus_quest/*/com.Icosa.OpenBrush*apk releases/OpenBrush_Quest_$VERSION.apk - mv build_oculus_quest/*/com.Icosa.OpenBrush*.symbols.zip releases/symbols.zip - - cd releases - unzip symbols.zip - curl -L 'https://www.oculus.com/download_app/?id=1462426033810370' -o ovr-platform-util - chmod 755 ovr-platform-util - - if [ "$PRERELEASE" == "false" ] - then - ./ovr-platform-util upload-quest-build --app-id ${OCULUS_QUEST_APP_ID} --app-secret ${OCULUS_QUEST_APP_SECRET} --apk OpenBrush_Quest_$VERSION.apk --channel LIVE:quest2+ --debug_symbols_dir ./arm64-v8a/ --debug-symbols-pattern '*.so' --age-group MIXED_AGES - else - CHANGELOG="${RAW_CHANGELOG}" - ./ovr-platform-util upload-quest-build --app-id ${OCULUS_QUEST_APP_ID} --app-secret ${OCULUS_QUEST_APP_SECRET} --apk OpenBrush_Quest_$VERSION.apk --channel Beta:quest2+ --debug_symbols_dir ./arm64-v8a/ --debug-symbols-pattern '*.so' --notes "${CHANGELOG}" --age-group MIXED_AGES - fi - - publish_oculus_quest1: - name: Publish Oculus Quest 1 Release - needs: [configuration, build] - runs-on: macos-latest # the ovr-platform-util tool is only available for Mac and Windows - if: | - github.event_name == 'push' && - github.repository == 'icosa-foundation/open-brush' && - (github.ref == 'refs/heads/main' || contains(github.ref, 'refs/tags/v')) - - steps: - - name: Download Build Artifacts (Oculus Quest 1) - uses: actions/download-artifact@v4 - with: - name: Oculus Quest (1) - path: build_oculus_quest1 - - name: Publish Oculus Builds - env: - VERSION: ${{ needs.configuration.outputs.version }} - PRERELEASE: ${{ needs.configuration.outputs.prerelease }} - RAW_CHANGELOG: ${{ needs.configuration.outputs.rawchangelog }} - OCULUS_QUEST_APP_ID: ${{ vars.OCULUS_QUEST_APP_ID }} - OCULUS_QUEST_APP_SECRET: ${{ secrets.OCULUS_QUEST_APP_SECRET }} - run: | - mkdir releases1 - mv build_oculus_quest1/*/com.Icosa.OpenBrush*apk releases1/OpenBrush_Quest1_$VERSION.apk - mv build_oculus_quest1/*/com.Icosa.OpenBrush*.symbols.zip releases1/symbols.zip - - cd releases1 - unzip symbols.zip - curl -L 'https://www.oculus.com/download_app/?id=1462426033810370' -o ovr-platform-util - chmod 755 ovr-platform-util - - if [ "$PRERELEASE" == "false" ] - then - ./ovr-platform-util upload-quest-build --app-id ${OCULUS_QUEST_APP_ID} --app-secret ${OCULUS_QUEST_APP_SECRET} --apk OpenBrush_Quest1_$VERSION.apk --channel LIVE:quest1only --debug_symbols_dir ./arm64-v8a/ --debug-symbols-pattern '*.so' --age-group MIXED_AGES - else - CHANGELOG="${RAW_CHANGELOG}" - ./ovr-platform-util upload-quest-build --app-id ${OCULUS_QUEST_APP_ID} --app-secret ${OCULUS_QUEST_APP_SECRET} --apk OpenBrush_Quest1_$VERSION.apk --channel Beta:quest1only --debug_symbols_dir ./arm64-v8a/ --debug-symbols-pattern '*.so' --notes "${CHANGELOG}" --age-group MIXED_AGES - fi - - publish_oculus_rift: - name: Publish Oculus Rift Release - needs: [configuration, build] - runs-on: macos-latest # the ovr-platform-util tool is only available for Mac and Windows - if: | - github.event_name == 'push' && - github.repository == 'icosa-foundation/open-brush' && - (github.ref == 'refs/heads/main' || contains(github.ref, 'refs/tags/v')) - - steps: - - name: Download Build Artifacts (Windows Rift) - uses: actions/download-artifact@v4 - with: - name: Windows Rift - path: build_windows_rift - - name: Publish Oculus Builds - env: - VERSION: ${{ needs.configuration.outputs.version }} - PRERELEASE: ${{ needs.configuration.outputs.prerelease }} - RAW_CHANGELOG: ${{ needs.configuration.outputs.rawchangelog }} - OCULUS_RIFT_APP_ID: ${{ vars.OCULUS_RIFT_APP_ID }} - OCULUS_RIFT_APP_SECRET: ${{ secrets.OCULUS_RIFT_APP_SECRET }} - run: | - mkdir releases - mv build_windows_rift/StandaloneWindows64/ releases/OpenBrush_Rift_$VERSION/ - cd releases - zip -r OpenBrush_Rift_$VERSION.zip OpenBrush_Rift_$VERSION/ - curl -L 'https://www.oculus.com/download_app/?id=1462426033810370' -o ovr-platform-util - chmod 755 ovr-platform-util - - if [ "$PRERELEASE" == "false" ] - then - ./ovr-platform-util upload-rift-build --app-id ${OCULUS_RIFT_APP_ID} --app-secret ${OCULUS_RIFT_APP_SECRET} --build-dir OpenBrush_Rift_$VERSION --launch-file OpenBrush.exe --channel LIVE --version $VERSION --firewall_exceptions true --redistributables 822786567843179,1675031999409058,2657209094360789 - else - CHANGELOG="${RAW_CHANGELOG}" - ./ovr-platform-util upload-rift-build --app-id ${OCULUS_RIFT_APP_ID} --app-secret ${OCULUS_RIFT_APP_SECRET} --build-dir OpenBrush_Rift_$VERSION --launch-file OpenBrush.exe --channel BETA --version $VERSION --firewall_exceptions true --redistributables 822786567843179,1675031999409058,2657209094360789 --notes "${CHANGELOG}" - fi - - publish_pico: - name: Publish Pico Releases - needs: [configuration, build] - runs-on: ubuntu-latest # the ovr-platform-util tool is only available for Mac and Windows - if: | - github.event_name == 'push' && - github.repository == 'icosa-foundation/open-brush' && - (github.ref == 'refs/heads/main' || contains(github.ref, 'refs/tags/v')) - - steps: - - name: Download Build Artifacts (Android Pico) - uses: actions/download-artifact@v4 - with: - name: Android Pico - path: build_android_pico - - name: Download Build Artifacts (Android Pico CN) - uses: actions/download-artifact@v4 - with: - name: Android Pico (CN) - path: build_android_pico_cn - - name: Publish Pico Builds - env: - VERSION: ${{ needs.configuration.outputs.version }} - PRERELEASE: ${{ needs.configuration.outputs.prerelease }} - RAW_CHANGELOG: ${{ needs.configuration.outputs.rawchangelog }} - PICO_APP_ID: ${{ vars.PICO_APP_ID }} - PICO_APP_SECRET: ${{ secrets.PICO_APP_SECRET }} - run: | - mkdir releases - mv build_android_pico/*/com.Icosa.OpenBrush*apk releases/OpenBrush_Pico_$VERSION.apk - mv build_android_pico_cn/*/com.Icosa.OpenBrush*apk releases/OpenBrush_Pico_CN_$VERSION.apk - - cd releases - # pico-cli v1.0.3 - curl -L 'https://p16-platform-static-va.ibyteimg.com/tos-maliva-i-jo6vmmv194-us/linux-noncn/202304111056/pico-cli?r=1681181814847245000' -o pico-cli - chmod 755 pico-cli - - if [ "$PRERELEASE" == "false" ] - then - # The order here matters, because the Chinese build has a slightly higher version code due to the suffix of 1 vs 0 - ./pico-cli upload-build --app-id $PICO_APP_ID --app-secret $PICO_APP_SECRET --region noncn --apk OpenBrush_Pico_$VERSION.apk --channel 2 --notes-en "Version $VERSION" --device 'PICO Neo3,PICO Neo3 Pro,PICO Neo3 Eye,PICO 4' - ./pico-cli upload-build --app-id $PICO_APP_ID --app-secret $PICO_APP_SECRET --region noncn --apk OpenBrush_Pico_CN_$VERSION.apk --channel 1 --notes-en "Version $VERSION" --device 'PICO Neo3,PICO Neo3 Pro,PICO Neo3 Eye,PICO 4' - else - # For Pico, Beta channels can only get one build, not a separate China / non-China build - ./pico-cli upload-build --app-id $PICO_APP_ID --app-secret $PICO_APP_SECRET --region noncn --apk OpenBrush_Pico_$VERSION.apk --channel 3 --notes-en "Version $VERSION" --device 'PICO Neo3,PICO Neo3 Pro,PICO Neo3 Eye,PICO 4' - fi - - publish_ios_zapbox: - name: Publish Zapbox iOS - needs: [configuration, build] - runs-on: macos-latest - if: | - github.event_name == 'push' && - github.repository == 'icosa-foundation/open-brush' && - (github.ref == 'refs/heads/main' || contains(github.ref, 'refs/tags/v')) - steps: - - name: Checkout Repository - uses: actions/checkout@v4 - with: - fetch-depth: 0 - sparse-checkout: | - Gemfile - Gemfile.lock - fastlane - - - name: Free extra space - # As of 02/08/2024, this increases free space from 21GB to 47GB - run: | - echo "Initial free space" - df -h - rm -rf "$AGENT_TOOLSDIRECTORY" - echo "Disk space after cleanup of \$AGENT_TOOLSDIRECTORY" - df -h - echo "Deleting all Xcode versions except 15.4" - find /Applications/Xcode_* -maxdepth 0 -type d ! -name 'Xcode_15.4.app' -exec rm -rf {} \; - df -h - find /Applications/Xcode* -name "*.app" -exec du -mcsh {} \; # Shows Xcode app sizes - - - name: Download iOS Artifact - uses: actions/download-artifact@v4 - with: - name: iOS Zapbox - path: build - - - name: Fix File Permissions - run: | - export FILENAME=$(basename $(readlink -f build/iOS/OpenBrush*)) - export IOS_BUILD_PATH=$(pwd)/build/iOS/${FILENAME} - - find $IOS_BUILD_PATH -type f -name "*.sh" -exec chmod +x {} \; - - - name: Run fastlane - env: - APPLE_CONNECT_EMAIL: ${{ secrets.APPLE_CONNECT_EMAIL }} - APPLE_DEVELOPER_EMAIL: ${{ secrets.APPLE_DEVELOPER_EMAIL }} - APPLE_TEAM_ID: ${{ vars.APPLE_TEAM_ID }} - - MATCH_REPOSITORY: ${{ secrets.MATCH_REPOSITORY }} - MATCH_DEPLOY_KEY: ${{ secrets.MATCH_DEPLOY_KEY }} - MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }} - - APPSTORE_ISSUER_ID: ${{ secrets.APPSTORE_ISSUER_ID }} - APPSTORE_KEY_ID: ${{ secrets.APPSTORE_KEY_ID }} - APPSTORE_P8: ${{ secrets.APPSTORE_P8 }} - - IOS_BUNDLE_ID: ${{ vars.IOS_ZAPBOX_BUNDLE_ID }} - PROJECT_NAME: Open Brush for Zapbox - FASTLANE_LANE: ${{ needs.configuration.outputs.fastlanelane }} - run: | - eval "$(ssh-agent -s)" - ssh-add - <<< "${MATCH_DEPLOY_KEY}" - export FILENAME=$(basename $(readlink -f build/iOS/OpenBrush*)) - export IOS_BUILD_PATH=$(pwd)/build/iOS/${FILENAME} - - bundle install - bundle exec fastlane ios ${FASTLANE_LANE} - - - name: Save logs - uses: actions/upload-artifact@v4 - if: ${{ failure() }} - with: - name: fastlane logs - path: /Users/runner/Library/Logs/gym/ +--- +# yamllint disable rule:line-length +# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json +name: Builds + +on: # yamllint disable-line rule:truthy + pull_request: {} + push: + branches: + - "**" + tags: + - "v*" + schedule: + - cron: "34 19 * * sat" # Weekly, Saturday at 19:34 UTC + +env: + UNITY_VERSION: "2022.3.34f1" + PHOTON_PAT: ${{ secrets.PHOTON_PAT }} # This needs to be here, since you can't use a ${{ secrets }} within an if: condition +jobs: + configuration: + if: | + (github.event_name == 'schedule') || + (github.event_name == 'pull_request') || + ( + github.event_name == 'push' && + ( + github.ref == 'refs/heads/main' || + contains(github.ref, 'refs/tags/v') || + contains(github.event.head_commit.message, '[CI BUILD]') + ) + ) + name: Configure Build Parameters + runs-on: ubuntu-latest + outputs: + version: ${{ steps.version.outputs.version}} + androidVersionCode: ${{ steps.version.outputs.androidVersionCode }} + stamp: ${{ steps.version.outputs.stamp }} + prerelease: ${{ steps.version.outputs.prerelease }} + previousrelease: ${{ steps.rawchangelogdata.outputs.previousrelease }} + previousfullrelease: ${{ steps.rawchangelogdata.outputs.previousfullrelease }} + currentrelease: ${{ steps.rawchangelogdata.outputs.currentrelease }} + rawchangelog: ${{ steps.rawchangelogdata.outputs.rawchangelog}} + basename: ${{ steps.github.outputs.basename }} + description: ${{ steps.github.outputs.description}} + itchchannelname: ${{ steps.version.outputs.itchchannelname }} + fastlanelane: ${{ steps.version.outputs.fastlanelane}} + uid: ${{ steps.github.outputs.uid }} + gid: ${{ steps.github.outputs.gid }} + flavors: ${{ steps.flavors.outputs.flavors }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + lfs: true # We don't use LFS, but it adds no time, and leave it here in case we do at some point later + + - name: Calculate version and stamp + id: version + run: | + # General note: for pull requests, we query github.event.pull_request.head.sha rather than the default sha, which is a merge commit of the target branch into the head. For pushes or tag commits, there's no additional commits made by the CI, so we can use the default, current, reference + + # Get the first two numbers from the last tag, including a tag on the current commit (to handle the case of a formal build) + MAJOR_MINOR=$(git describe --tags --abbrev=0 --match "v[0-9]*.[0-9]*" ${{ github.event.pull_request.head.sha }}) + + # How many commits have been made since the last tag of the form vX.Y. + # + # We used to use this version, however, it couldn't handle these two cases at the same time: + # (v2.1) + # | + # /-c2..c4..c5-\ + # / \ + # c...c0..c1....c3.........m6..c7....c10.c11.....m13...c14 <- [main] + # ^ \ / + # (v2.0) \-c8..c9..c12-/ + # If we use --first-parent, it wouldn't find a tag that was not a first parent, and so it'll think we're now in 2.0.8, though it skips the commits on the branches. If we did not use --first-parent, it gets the proper tag (v2.1), but counts each commit in the feature branch, and gives 2.1.10. While we almost always squash, if we ever do have an explicit merge commit, we don't want to count the commits on the feature branch. In this case, we actually want to get 2.1.7 (commits c3, m6, c7, c10, c11, m13, and c14). + ######## OLD CODE ######## + # # If the value is not equal to zero, git describe will give us a version in the form vX.Y-Z-gAAAAAAA, where Z is the count. If the current commit has a vX.Y tag, it'll just return that, so the 'cut' does nothing. We test for this below + # PATCH_VERSION=$(git describe --tags --match "v[0-9]*.[0-9]*" --first-parent ${{ github.event.pull_request.head.sha }} | cut -d'-' -f2) + ######## END OLD CODE ######## + + # Instead, we'll find the last tag, wherever it is, and then count the --first-parent commits "since" then (i.e., not included; they might be historically behind it) + CLOSEST_TAG=$(git describe --tags --match "v[0-9]*.[0-9]*" --abbrev=0 HEAD) + PATCH_VERSION=$(git log ${CLOSEST_TAG}.. --oneline --first-parent | wc -l) + + if [ $PATCH_VERSION == "0" ] + then + STAMP="" + echo "prerelease=false" >> $GITHUB_OUTPUT + echo "itchchannelname=release" >> $GITHUB_OUTPUT + echo "fastlanelane=beta" >> $GITHUB_OUTPUT + + else + # This is the first 7 characters of the commit; we do it this way rather than via rev-parse to avoid an 'if' conditional depending on whether it's a PR or push. (unlike git describe, git rev-parse doesn't default to the current HEAD) + STAMP=$(git describe --tags --match "v[0-9]*.[0-9]*" ${{ github.event.pull_request.head.sha }} | cut -d'-' -f3) + echo "prerelease=true" >> $GITHUB_OUTPUT + echo "itchchannelname=beta" >> $GITHUB_OUTPUT + echo "fastlanelane=beta" >> $GITHUB_OUTPUT + fi + VERSION=$(echo "$MAJOR_MINOR.$PATCH_VERSION" | sed -e 's/^v//') + echo "version=$VERSION" >> $GITHUB_OUTPUT + echo "stamp=$STAMP" >> $GITHUB_OUTPUT + MAJOR=$(echo $VERSION | cut -d '.' -f 1) + MINOR=$(echo $VERSION | cut -d '.' -f 2) + ANDROID_VERSION_CODE=$((MAJOR * 1000000 + MINOR * 1000 + PATCH_VERSION)) + echo "androidVersionCode=$ANDROID_VERSION_CODE" >> $GITHUB_OUTPUT + echo "Version $VERSION stamp=$STAMP androidVersionCode=$ANDROID_VERSION_CODE" + - name: Calculate Release tags for Changelog and raw changelog + id: rawchangelogdata + env: + PRERELEASE: ${{ steps.version.outputs.prerelease }} + VERSION: ${{ steps.version.outputs.version }} + run: | + if [ "$PRERELEASE" == "true" ] + then + PREV=$(git describe --tags --abbrev=0 HEAD^) + else + PREV=$(git describe --tags --match "v[0-9]*.[0-9]*" --first-parent --abbrev=0 HEAD^) + fi + PREVFULL=$(git describe --tags --match "v[0-9]*.[0-9]*" --first-parent --abbrev=0 HEAD^) + CUR="$(git rev-parse HEAD)" + echo "previousrelease=$PREV" >> $GITHUB_OUTPUT + echo "previousfullrelease=$PREVFULL" >> $GITHUB_OUTPUT + echo "currentrelease=$CUR" >> $GITHUB_OUTPUT + LAST_TAG=$(git describe --tags --match 'v[0-9]*.[0-9]*' --abbrev=0 HEAD^) + RAW_CHANGELOG=$(echo "$(git log --first-parent ${LAST_TAG}.. --pretty=format:'%D-g%h: %s' | sed -e 's/tag: //' -e 's/HEAD -> main, //')" | sed -e "s/origin\/main/$VERSION/" | tac) + echo "rawchangelog=${RAW_CHANGELOG//$'\n'/'\n'}" >> $GITHUB_OUTPUT + + - name: Echo Changelog (for debugging purposes) + env: + CHANGELOG: ${{ steps.rawchangelogdata.outputs.rawchangelog}} + run: | + echo "CHANGELOG=$CHANGELOG" + + - name: Set custom app name and package name, if relevant + id: github + env: + PRERELEASE: ${{ steps.version.outputs.prerelease }} + run: | + # For a PR action (i.e., synchronize / open), the value of github.ref will be refs/pull/1234/merge + # For a push action, it will be either refs/heads/foo_branch_name OR refs/tags/v1234. + # We want to use the base name for pushes of tags or to main, the PR number for PRs, and the branch name for named branches. + if [[ "$PRERELEASE" == "false" || ${{ github.ref }} == refs/heads/main ]] + then + echo "basename=OpenBrush" >> $GITHUB_OUTPUT + echo "description=" >> $GITHUB_OUTPUT + else + if [[ ${{ github.ref }} == refs/pull/* ]] + then + DESCRIPTION="PR#$(echo ${{ github.ref }} | sed -e 's#refs/pull/##' -e 's#/merge##')" + elif [[ ${{ github.ref }} == refs/heads/* ]] + then + DESCRIPTION="$(echo ${{ github.ref }} | sed -e 's#refs/heads/##')" + else + DESCRIPTION="Unknown" + fi + echo "description=-btb-description ${DESCRIPTION}" >> $GITHUB_OUTPUT + IDENTIFIER=$(echo ${DESCRIPTION} | sed -e 's/[\/#_-]//g') + echo "basename=OpenBrush-${IDENTIFIER}" >> $GITHUB_OUTPUT + fi + echo "uid=$(id -u)" >> $GITHUB_OUTPUT + echo "gid=$(id -g)" >> $GITHUB_OUTPUT + + - name: Determine whether to build Development builds or not + id: flavors + run: | + set -x + if [[ $(git log --format=%B ${{ github.event.pull_request.head.sha }} -1) == *'[CI BUILD DEV]'* ]] + then + echo 'flavors=[{"development": true, "title": "Development"}, {"development": false}]' >> $GITHUB_OUTPUT + + else + echo 'flavors=[{"development": false}]' >> $GITHUB_OUTPUT + fi + + build: + name: ${{ matrix.name }} ${{ matrix.flavors.title }} + needs: configuration + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + flavors: ${{ fromJson(needs.configuration.outputs.flavors) }} + name: [Windows OpenXR, Windows Pimax, Windows Rift, Linux, MacOS, Android OpenXR, Oculus Quest (1), Oculus Quest (2+), Android Pico, Android Pico (CN), iOS Zapbox] # These will all be overwritten, but because we have the flavors matrix as well, we can't just add configurations via include; they'll overwrite each other. This way ensures that we get each one + include: + - name: Windows OpenXR + targetPlatform: StandaloneWindows64 + vrsdk: OpenXR + cache: Windows + + - name: Windows Pimax + targetPlatform: StandaloneWindows64 + vrsdk: OpenXR + cache: Windows + extra_defines: PIMAX_SUPPORTED + + - name: Windows Rift + targetPlatform: StandaloneWindows64 + vrsdk: Oculus + cache: Windows + extra_defines: OCULUS_SUPPORTED + + - name: Linux + targetPlatform: StandaloneLinux64 + vrsdk: Monoscopic # All builds include monoscopic, but this one has no additional XrSdk, so we'll keep the name monoscopic + cache: Linux + + - name: MacOS + targetPlatform: StandaloneOSX + vrsdk: Monoscopic + cache: MacOS + packages_to_remove: com.meta.xr.sdk.core + + - name: Android OpenXR + targetPlatform: Android + vrsdk: OpenXR + cache: Android_Vulkan + extraoptions: -btb-il2cpp + versionSuffix: 0 + + - name: Oculus Quest (1) + targetPlatform: Android + vrsdk: OpenXR + cache: Android_Vulkan + extraoptions: -btb-il2cpp + versionSuffix: 0 + extra_defines: USE_QUEST_PACKAGE_NAME FORCE_QUEST_SUPPORT_DEVICE FORCE_FOCUSAWARE FORCE_HEADTRACKING + packages_to_remove: com.meta.xr.sdk.platform com.meta.xr.sdk.utilities + + - name: Oculus Quest (2+) + targetPlatform: Android + vrsdk: Oculus + cache: Android_Vulkan + extraoptions: -btb-il2cpp + versionSuffix: 1 + extra_defines: OCULUS_SUPPORTED USE_QUEST_PACKAGE_NAME + + - name: Android Pico + targetPlatform: Android + vrsdk: Pico + cache: Android_GLES + extraoptions: -btb-il2cpp + versionSuffix: 0 + extra_defines: PICO_SUPPORTED + + - name: Android Pico (CN) + targetPlatform: Android + vrsdk: Pico + cache: Android_GLES + # Pico requested Chinese build that doesn't have google/sketchfab login. + extraoptions: -btb-il2cpp -btb-disableAccountLogins + versionSuffix: 1 + extra_defines: PICO_SUPPORTED + + - name: iOS Zapbox + targetPlatform: iOS + vrsdk: Zapbox + cache: iOS + extraoptions: -btb-il2cpp + extra_defines: ZAPBOX_SUPPORTED + packages_to_remove: com.unity.formats.usd + + steps: + - name: Set masking + run: echo "::add-mask::DoNotStealThis1" + - name: Free extra space + # As of 02/08/2024, this increases free space from 21GB to 47GB + run: | + echo "Initial free space" + df -h / + echo "Removing all pre-loaded docker images" + docker rmi $(docker image ls -aq) # Removes ~3GB + df -h / + echo "Listing 100 largest packages" + dpkg-query -Wf '${Installed-Size}\t${Package}\n' | sort -rn | head -n 100 + echo "Removing unneeded large packages" + sudo apt update + sudo apt remove -y '^ghc-.*' '^dotnet-.*' azure-cli powershell google-chrome-stable firefox microsoft-edge-stable 'mongodb-*' 'mysql-*' 'mariadb-*' 'temurin-*' 'openjdk-*' default-jre-headless mono-devel libgl1-mesa-dri # Removes ~6GB + sudo apt autoremove -y + sudo apt clean + df -h / + echo "Removing Android" + sudo rm -rf /usr/local/lib/android # Removes ~9GB + df -h / + echo "Removing remaining large directories" + rm -rf /usr/share/dotnet/ # Removes ~1GB + rm -rf "$AGENT_TOOLSDIRECTORY" # Removes ~7GB + echo "Disk space after cleanup" + df -h / + + - name: Checkout repository + uses: actions/checkout@v4 + with: + lfs: true # We don't use LFS, but it adds no time, and leave it here in case we do at some point later + + - name: Install Pimax unity package + if: startsWith(matrix.name, 'Windows Pimax') + run: | + # version 0.6.3 + # Same as above, but adapted to work for Pimax instead. + wget -q https://dl.appstore.pimax.com/sdk/Pimax_Platform_Unity_SDK_v0.6.3.zip -O package.zip + unzip package.zip + mkdir tmp + tar -C tmp -xzf PimaxPlatform_v0.6.3.unitypackage + find tmp -type f | xargs chmod a-x + for pn in tmp/*/pathname; do + id=${pn%/*} + id=${id#*/} + p=$(head -1 $pn) + d=${p%/*} + mkdir -p "tmp/$d" + [ -f "tmp/$id/asset" ] && cp -v "tmp/$id/asset" "tmp/$p" + cp "tmp/$id/asset.meta" "tmp/${p}.meta" + done + cp -R tmp/Assets/Pimax Assets/ + rm -rf tmp package.zip PimaxPlatform_v0.6.3.unitypackage Tools + + - name: Install Pico unity package + if: matrix.vrsdk == 'Pico' + run: | + # version 2.1.1 + 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: | + # version 3.0.6; must be updated if the version changes + # This replaces the GUI's "Window -> TextMesh Pro -> Import TMP Essential Resources". I don't know why Unity makes this sort of thing so hard! + mkdir tmp.plugin + wget -q https://download.packages.unity.com/com.unity.textmeshpro/-/com.unity.textmeshpro-3.0.6.tgz -O tmp.plugin/plugin.tgz + tar -C tmp.plugin -xzf tmp.plugin/plugin.tgz + mkdir tmp.package + tar -C tmp.package -xzf 'tmp.plugin/package/Package Resources/TMP Essential Resources.unitypackage' + for pn in tmp.package/*/pathname; do + id=${pn%/*} + id=${id#*/} + p=$(head -1 $pn) + d=${p%/*} + mkdir -p "tmp.package/$d" + [ -f "tmp.package/$id/asset" ] && cp -v "tmp.package/$id/asset" "tmp.package/$p" + cp "tmp.package/$id/asset.meta" "tmp.package/${p}.meta" + done + mkdir -p 'Assets/TextMesh Pro' + 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 + ref: Fusion_v1.1.10_Voice_2 + 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@v4 + env: + SEGMENT_DOWNLOAD_TIMEOUT_MINS: 10 + with: + path: Library + # Some platforms share a cache; it's not a 1:1 mapping of either targetPlatform or vrsdk, so we have a distinct variable for which cache to use + key: Library_${{ matrix.cache }}_${{ env.UNITY_VERSION }} + + - name: Restore Library/PackageCache + id: cache_packagecache + uses: actions/cache/restore@v4 + env: + SEGMENT_DOWNLOAD_TIMEOUT_MINS: 10 + with: + path: Library/PackageCache + key: Library_PackageCache_${{ env.UNITY_VERSION }}_${{ hashFiles('Packages/packages-lock.json') }} + restore-keys: | + Library_PackageCache_${{ env.UNITY_VERSION }} + Library_PackageCache + + - name: Remove problematic packages + if: ${{ matrix.packages_to_remove }} + run: | + cp Packages/manifest.json{,.bak} + cp Packages/packages-lock.json{,.bak} + for PACKAGE in ${{ matrix.packages_to_remove }}; do + cat Packages/manifest.json | jq 'del( .dependencies ["'${PACKAGE}'"] )' > Packages/manifest.json.new + mv Packages/manifest.json.new Packages/manifest.json + cat Packages/packages-lock.json | jq 'del( .dependencies ["'${PACKAGE}'"] )' > Packages/packages-lock.json.new + mv Packages/packages-lock.json.new Packages/packages-lock.json + done + diff -u Packages/manifest.json.bak Packages/manifest.json || true + diff -u Packages/packages-lock.json.bak Packages/packages-lock.json || true + + - name: Set output filename + env: + BASENAME: ${{ needs.configuration.outputs.basename }} + run: | + if [[ "${{ matrix.targetPlatform}}" == "StandaloneWindows64" ]]; then + echo "filename=$BASENAME.exe" >> $GITHUB_ENV + elif [[ "${{ matrix.targetPlatform}}" == "StandaloneLinux64" ]]; then + echo "filename=$BASENAME" >> $GITHUB_ENV + elif [[ "${{ matrix.targetPlatform}}" == "iOS" ]]; then + echo "filename=$BASENAME" >> $GITHUB_ENV + elif [[ "${{ matrix.targetPlatform}}" == "StandaloneOSX" ]]; then + echo "filename=$BASENAME.app" >> $GITHUB_ENV + elif [[ "${{ matrix.targetPlatform}}" == "Android" ]]; then + echo "filename=com.Icosa.$BASENAME.apk" >> $GITHUB_ENV + fi + + - name: Set build stamp + if: ${{ needs.configuration.outputs.stamp }} + # We checkout the merge commit, but for the purpose of the tag, use the version from the PR, not the merge commit, which is rather hard to find later. We skip the version tag, since this comes from the code and can't be easily overwritten + run: | + echo "stamp=-btb-stamp ${{needs.configuration.outputs.stamp}}" >> $GITHUB_ENV + + - name: Enable Development Mode + if: ${{ matrix.flavors.development == true }} + run: | + echo "btbbopts=-btb-bopt Development" >> $GITHUB_ENV + + - name: Update version + env: + VERSION: ${{ needs.configuration.outputs.version}} + run: | + sed -e "s/m_VersionNumber:.*$/m_VersionNumber: $VERSION/" -i Assets/Scenes/Main.unity + sed -e "s/bundleVersion:.*$/bundleVersion: $VERSION/" -i ProjectSettings/ProjectSettings.asset + + - name: Add secure secrets file + env: + SECRETS_ASSET: ${{ secrets.SECRETS_ASSET }} + SECRETS_ASSET_META: ${{ secrets.SECRETS_ASSET_META }} + if: | + env.SECRETS_ASSET != null && + env.SECRETS_ASSET_META != null + run: | + echo "$SECRETS_ASSET" > Assets/Secrets.asset + echo "$SECRETS_ASSET_META" > Assets/Secrets.asset.meta + SECRETS_ASSET_META_GUID=$(grep "guid:" Assets/Secrets.asset.meta | cut -d" " -f2) + sed -e "s/Secrets:.*$/Secrets: {fileID: 11400000, guid: $SECRETS_ASSET_META_GUID, type: 2}/" -i Assets/Scenes/Main.unity + + - name: Enable keystore + run: | + sed -e 's/androidUseCustomKeystore.*$/androidUseCustomKeystore: 1/' -i ProjectSettings/ProjectSettings.asset + + - name: Add PHOTON_PAT specific define + if: ${{ env.PHOTON_PAT }} + run: | + echo -e "\n -define:PHOTON_UNITY_NETWORKING \n-define:PUN_2_0_OR_NEWER \n-define:PUN_2_OR_NEWER \n-define:PUN_2_19_OR_NEWER \n-define:FUSION_WEAVER \n-define:CROSS_PLATFORM_INPUT \n-define:MOBILE_INPUT \n-define:PHOTON_VOICE_DEFINED" | tee -a Assets/csc.rsp + + - name: Update build matrix specific defines in csc.rsp + if: ${{ matrix.extra_defines }} + run: | + for DEFINE in ${{ matrix.extra_defines }}; do + echo -e "\n-define:$DEFINE" | tee -a Assets/csc.rsp + done + + - name: Build project + uses: Wandalen/wretry.action@v3 + env: + VERSION: ${{ needs.configuration.outputs.version}} + UNITY_EMAIL: ${{ fromJSON(format('["unitytest@mikeage.net", "{0}"]', vars.UNITY_EMAIL))[secrets.UNITY_SERIAL != null] }} + UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }} + 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] }} + with: + retry_condition: steps._this.outputs.engineExitCode == 1 + action: game-ci/unity-builder@v4 + with: | + allowDirtyBuild: true # Because of the OVR Update, the build tree might be dirty + unityVersion: ${{ env.UNITY_VERSION }} + targetPlatform: ${{ matrix.targetPlatform }} + customParameters: -btb-target ${{ matrix.targetPlatform }} -btb-display ${{ matrix.vrsdk }} -btb-out /github/workspace/build/${{ matrix.vrsdk }}/${{ matrix.targetPlatform }}/${{ env.filename }} ${{ needs.configuration.outputs.description}} ${{ env.stamp }} ${{ env.btbbopts }} ${{ matrix.extraoptions }} + versioning: Custom + androidVersionCode: "${{ needs.configuration.outputs.androidVersionCode }}${{ matrix.versionSuffix }}" + version: ${{ needs.configuration.outputs.version }} + buildName: ${{ needs.configuration.outputs.basename }} + buildsPath: build/${{ matrix.vrsdk }} + chownFilesTo: ${{ needs.configuration.outputs.uid }}:${{ needs.configuration.outputs.gid }} + buildMethod: BuildTiltBrush.CommandLine + androidKeystoreName: openbrush.keystore + androidKeystoreBase64: ${{ secrets.ANDROID_KEYSTORE_BASE64 || '/u3+7QAAAAIAAAABAAAAAQAWb3BlbmJydXNoLW5vbi1vZmZpY2lhbAAAAX66M2FtAAAFATCCBP0wDgYKKwYBBAEqAhEBAQUABIIE6Wufa9OVstw7Bu/gdATKqoPafXGefygChsN1d4LGY0SMLPORjHXiryEVMKi2rt61kNeXzeLkiM4yIQAam4HZtNTxgjoFQ6KB7uzkqMJYKViBUgg1HCAl2e+QpYjqG+YNJT67CiPgjpsJHNE628CwKAvjJ85FhqFz+MKzNF8BOpS5g5waqFda67oxaE4qO8eAL+F9P7us+ziY5B4O3EJC9s7xpT2GV2ro0m0fZI2dr3OO9UdUO72CYTg5qs250JiSij26Haf4t8Vq28F2S8rTcMUVtN4FRtzeR/wjeeZ3laER+WoxYni4MrZEXhYYCGhfor8Zcfi3p5ka8TJCQxywTKpghpSwgykgMJLn1HksxB0vhIMGTb87c2CTqS4t5Js/OPdcYS4Jnr7mHdQtOGfJCvl3TJC7NJwzLLOzUTmVIogaZCA9GlRballbD7XYbR8mcPxs+jLq5HJJk8/3B8ojAz/YA9vp6ml3RSYDA+yv9fBIefxNniAredJeqAnmH4o9er3+n0rKmpoqiXdzFkp1ywYbDDxrsFTiPrTc0gEiLRbfCERBx8GZ/7zGv6exKW1mc1L7QcFRmT1PRuJo6vRfCOtjdAdp0Mj1bllGGe9oBSKOxqtxs/NFygaVZjMDqryRvObKaJaj5CDhNdwsa21EsQ3+YvQWBzlcs5FTi5S2zG3W4+tMb+HoyV36SEV4yBLtqqrczhVCuPMlZu2p1iFLyODJJOxrWnmZy49BlQiudmiR7wILJoYKIFFvGv1jCJnTl9cI6UGX8IwSHYjGJIdLxaQM6c/7tw15+h+3jPajzZqkIQ7r0fyBp2TxE+QXMCP/knYu/dVzzQoBe5CgnAr5Fj60eEF78mJZbU3m9EjuVglURCTs2hDiyl3eRENgJjTc8p9iho4aK5eT5BVF7v2TAsTkfm+AwOq78chbWfh7J5OYnycG+v6S76LE6T8Yy0Arkk4lOF5SC05SmrDQpFcbRC9B7pR8XwJx3rabt4jvFsdqQtqv7TRasNQs95oROSC8335tzsaQfPwL/sGH4wi4zsH3YZ6As2V9myMEytqVEX5DdGBtzRr1opkx0aisyG48Evtk1UHMR9ROoZmkbNOIFNDUxCBvw7CU20aJSri4GX7kahg8Lj670Lfpx1C9OMwH0xRGUHE4e2ZWaw6Smkjc0Rru7j4YFKel0KtJgQaei2fz2i+6wOv1uz+H4j6f98pVMsf3HODmnh4x+qlUXaJWbNILQEGwv3zVReY123TPHIzkwImNLej62BLaqnEgiPkKr/gp/2MdrgepUEGC8FN0MTPbazDR4aE5XqLtnehhq8/9EfIk3b5WzNh00IAELwFrWnabkob5xmSLORBH8SpS3J6NwWa4jJMADRAGPYOUH7tYUM1/GRUK1HuboNP9v3KAny/k30CrxLvNHwe/zkXgoU9+M+gXVXL8pJJLMawVe/Dg13XyqTTa00UX7TsQFJZGm6lHrgeFIejKBEMLsMXNAIccphZe6sDnycDm/GY8vqmfjg9R05GwJOhBd46vhDi7Ph8YbLjohEoT4KfE5o8+Norzc/VHbRv5Y+G6JCL6hRV72meb3LswLYGUzGYP4nh2Y/yixg+rAtre80xjbXFfdvXVF6CuibKn5gmjCmiRN31rvEfdwVPIQDCaqv19Do2cQYDN+yGCo7yDHAAAAAEABVguNTA5AAADhzCCA4MwggJroAMCAQICBGNtJJswDQYJKoZIhvcNAQELBQAwcTELMAkGA1UEBhMCR0IxFzAVBgNVBAgTDldlc3QgWW9ya3NoaXJlMQ4wDAYDVQQHEwVMZWVkczEOMAwGA1UEChMFSWNvc2ExEzARBgNVBAsTCk9wZW4gQnJ1c2gxFDASBgNVBAMTC01pa2UgTWlsbGVyMCAXDTIyMDIwMjExMzAyNVoYDzIwNzIwMTIxMTEzMDI1WjBxMQswCQYDVQQGEwJHQjEXMBUGA1UECBMOV2VzdCBZb3Jrc2hpcmUxDjAMBgNVBAcTBUxlZWRzMQ4wDAYDVQQKEwVJY29zYTETMBEGA1UECxMKT3BlbiBCcnVzaDEUMBIGA1UEAxMLTWlrZSBNaWxsZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCZOlUSd2Z9VSuVE1NK2AKiKCYR3ADh3f3PN6ipTtqUdxP44l5jJnPVXc5YXJ4DyBsXHGTqCSiL9wiqdRCNTMcRf6vrpcuRWxqwMMu4bid0eDiFBU+wModQl70N0VblMolYZzD/y0NpXWh7VKPSXyA22ZwygeOPQFzxR4j2jRvM/g+9HeJeVN1p5f+6pvceg/9FBSCEOQg5fbDtO+ytZfMiawcyhSSwwlOzEOGT0Dq6d9xIs1/zTA8LxAlGYHLSpQCT/n3X27LNgUMNrCpWgLTtxH/qQ61NU3juqTqBBWT4nzTXl1J9JyPaHH1yzC908YiI5PQSFehX80KTvsf0B65DAgMBAAGjITAfMB0GA1UdDgQWBBTThSJ0yfVNgUC4h3Sa9o8aUmLY3jANBgkqhkiG9w0BAQsFAAOCAQEAUqE9NJA+PaMBrCcVHkxmk32DsVNIVCM/eaTPCyjBM3V5COgxscven160OKGHRn6Xhplr/UDy+StphE9Hwk8MAwSJ4reBdPiNMQvIsDEQ/aXSAyTiKQeIU5Zc+cYuJvHcyxIOVektDe8Er2AITvpXQDK1JRvYU6lFKym3j/CZ4comUwjdolB1C6fzlTkhP3ZuuFMfv543WyuVtb3A1mioLzQ5kfFlbTO0uXqEm+gltkK8AMqU6B5RJDYtQXIJkjR//UzNgpaILVvQ4pyyS6VvBNbUbrHaUKabtP3daDtQ0AQw3gSkCJ+QPpY9joIq38LMcVY5/x5/nbcxTuYvUlHozn/+qtNvA7MtikSNPcblNpmifg4o' }} + androidKeystorePass: ${{ secrets.ANDROID_KEYSTORE_PASS || 'FakeKey' }} + androidKeyaliasName: ${{ secrets.ANDROID_KEYALIAS_NAME || 'openbrush-non-official' }} + androidKeyaliasPass: ${{ secrets.ANDROID_KEYALIAS_PASS || 'FakeKey' }} + + - name: Prepare for packaging (permissions and compression, OSX only) + if: matrix.targetPlatform == 'StandaloneOSX' + run: | + mv build/${{ matrix.vrsdk }}/${{ matrix.targetPlatform }}/Support "build/${{ matrix.vrsdk }}/${{ matrix.targetPlatform }}/${{ env.filename }}/Contents/" + find build -name 'UnityFbxSdkNative.bundle' -delete + # Compress, but skip the top directories + tar -c -v -z -f OpenBrush.tgz -C build/${{ matrix.vrsdk }} ${{ matrix.targetPlatform}} + rm -rf build/${{ matrix.vrsdk }}/* + mv OpenBrush.tgz build/${{ matrix.vrsdk }}/ + + - name: Upload build/ + uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.name }} ${{ matrix.flavors.title }} + path: | + build/${{ matrix.vrsdk }} + !build/Pico/*.symbols.zip + !build/**/*_BackUpThisFolder_ButDontShipItWithYourGame + + - name: Check if packages-lock.json has changed or if it's cacheable + id: check_packagecache + run: | + # Check if there are any changes to the packages-lock.json file + set +e + git diff --exit-code -- Packages/packages-lock.json + CHANGES="$?" + set -e + echo "changes=$CHANGES" >> $GITHUB_OUTPUT + echo "diff returned: $CHANGES" + + - name: Save Library/PackageCache cache + uses: actions/cache/save@v4 + if: github.ref == 'refs/heads/main' && steps.check_packagecache.outputs.changes == 0 && steps.cache_packagecache.outputs.cache-hit != 'true' && ! matrix.packages_to_remove # Ideally, we'd save caches on branches, but they're too big, and branch caches can evict those from main, which is unacceptable. + env: + SEGMENT_DOWNLOAD_TIMEOUT_MINS: 10 + with: + path: Library/PackageCache + key: Library_PackageCache_${{ env.UNITY_VERSION }}_${{ hashFiles('Packages/packages-lock.json') }} + + - name: Clean Library before caching + if: github.ref == 'refs/heads/main' && steps.cache_library.outputs.cache-hit != 'true' # Ideally, we'd save caches on branches, but they're too big, and branch caches can evict those from main, which is unacceptable. + run: | + # Remove the large files from the Library directory that we know we'll rebuild. As our il2cpp caches are huge and barely fit in the Github quota, it's better not to save an unneeded 1GB of space (or so). If a new Unity version is taken, this may need to be updated + # Debugging + echo "Library/ directories" + du -mcsh Library/* + find Library -size +50M -exec ls -altrh {} \; + # chown all files, since some are owned by root after the docker run + docker run -v $(pwd)/Library:/mnt alpine chown $(id -u).$(id -g) -R /mnt/ + # Print the files to be deleted + find Library/Bee/ -name 'symbols.zip' -or -name 'libil2cpp*.so' -or -name 'launcher-release.apk' | tee todelete.txt + cat todelete.txt | xargs -r rm + # The package cache is stored in a separate, shared, cache + rm -rf Library/PackageCache + echo "Final space used" + du -mcsh Library + + - name: Save Library/ cache + uses: actions/cache/save@v4 + if: github.ref == 'refs/heads/main' && steps.cache_library.outputs.cache-hit != 'true' # Ideally, we'd save caches on branches, but they're too big, and branch caches can evict those from main, which is unacceptable. + env: + SEGMENT_DOWNLOAD_TIMEOUT_MINS: 10 + with: + path: Library + # Some platforms share a cache; it's not a 1:1 mapping of either targetPlatform or vrsdk, so we have a distinct variable for which cache to use + key: Library_${{ matrix.cache }}_${{ env.UNITY_VERSION }} + + signmacos: + name: Sign the MacOS .app + needs: [configuration, build] + if: | + github.event_name == 'push' && + github.repository == 'icosa-foundation/open-brush' && + (github.ref == 'refs/heads/main' || contains(github.ref, 'refs/tags/v')) + runs-on: macos-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + lfs: true # We don't use LFS, but it adds no time, and leave it here in case we do at some point later + sparse-checkout: | + Support/macos + + - name: Download Build Artifacts + uses: actions/download-artifact@v4 + with: + name: MacOS + path: build_macos + + # See https://docs.github.com/en/actions/deployment/deploying-xcode-applications/installing-an-apple-certificate-on-macos-runners-for-xcode-development + - name: Install the Apple certificate and provisioning profile + env: + BUILD_CERTIFICATE_BASE64: ${{ secrets.APPLE_BUILD_CERTIFICATE_BASE64 }} + INSTALL_CERTIFICATE_BASE64: ${{ secrets.APPLE_INSTALL_CERTIFICATE_BASE64 }} + P12_PASSWORD: ${{ secrets.APPLE_P12_PASSWORD }} + BUILD_PROVISION_PROFILE_BASE64: ${{ secrets.APPLE_BUILD_PROVISION_PROFILE_BASE64 }} + KEYCHAIN_PASSWORD: ${{ secrets.APPLE_KEYCHAIN_PASSWORD }} + run: | + BUILD_CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12 + INSTALL_CERTIFICATE_PATH=$RUNNER_TEMP/install_certificate.p12 + PP_PATH=$RUNNER_TEMP/openbrushmac.provisionprofile + KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db + + echo -n "$BUILD_CERTIFICATE_BASE64" | base64 --decode -o $BUILD_CERTIFICATE_PATH + echo -n "$INSTALL_CERTIFICATE_BASE64" | base64 --decode -o $INSTALL_CERTIFICATE_PATH + echo -n "$BUILD_PROVISION_PROFILE_BASE64" | base64 --decode -o $PP_PATH + + # create temporary keychain + security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH + security set-keychain-settings -lut 21600 $KEYCHAIN_PATH + security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH + + # import certificate to keychain + security import $BUILD_CERTIFICATE_PATH -P "$P12_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH + security import $INSTALL_CERTIFICATE_PATH -P "$P12_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH + security list-keychain -d user -s $KEYCHAIN_PATH + + mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles + cp $PP_PATH ~/Library/MobileDevice/Provisioning\ Profiles + + - name: Sign the release + env: + VERSION: ${{ needs.configuration.outputs.version }} + run: | + tar xvfz build_macos/*tgz + + export FILENAME=$(basename $(readlink -f StandaloneOSX/OpenBrush*.app)) + + cd StandaloneOSX/ + codesign --deep --force --verify --verbose --timestamp --options runtime --entitlements ../Support/macos/OpenBrush.entitlements --sign "Developer ID Application: Icosa Gallery Ltd (${{ vars.APPLE_TEAM_ID }})" $FILENAME/Contents/Support/ThirdParty/ffmpeg/bin/ffmpeg + for bundle in $FILENAME/Contents/Plugins/*.bundle; do + codesign --deep --force --verify --verbose --timestamp --options runtime --entitlements ../Support/macos/OpenBrush.entitlements --sign "Developer ID Application: Icosa Gallery Ltd (${{ vars.APPLE_TEAM_ID }})" $bundle + done + codesign --deep --force --verify --verbose --timestamp --options runtime --entitlements ../Support/macos/OpenBrush.entitlements --sign "Developer ID Application: Icosa Gallery Ltd (${{ vars.APPLE_TEAM_ID }})" $FILENAME + cd - + + tar -c -v -z -f OpenBrush.tgz StandaloneOSX + - name: Upload signed app + uses: actions/upload-artifact@v4 + with: + name: MacOS (signed) + path: | + OpenBrush.tgz + + createdmg: + name: Create and Notarize DMG + needs: [configuration, signmacos] + if: | + github.event_name == 'push' && + github.repository == 'icosa-foundation/open-brush' && + (github.ref == 'refs/heads/main' || contains(github.ref, 'refs/tags/v')) + + runs-on: macos-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + lfs: true # We don't use LFS, but it adds no time, and leave it here in case we do at some point later + sparse-checkout: | + Support/macos + + - name: Download Build Artifacts + uses: actions/download-artifact@v4 + with: + name: MacOS (signed) + path: build_macos + + - name: Create a notarized DMG + env: + VERSION: ${{ needs.configuration.outputs.version }} + run: | + tar xvfz build_macos/*tgz + + export FILENAME=$(basename $(readlink -f StandaloneOSX/OpenBrush*.app)) + mkdir dist + + cp Support/macos/background.png StandaloneOSX/ + cp Support/macos/background@2x.png StandaloneOSX/ + pip install jinjanator + jinjanate Support/macos/openbrush-dmg.json.j2 > StandaloneOSX/openbrush-dmg.json + pushd StandaloneOSX/ + npx appdmg openbrush-dmg.json ../dist/OpenBrush.dmg + popd + + xcrun notarytool submit \ + --team-id ${{ vars.APPLE_TEAM_ID }} \ + --apple-id ${{ vars.APPLE_ID }} \ + --password ${{ secrets.APPLE_APPLICATION_SPECIFIC_PASSWORD }} \ + --wait \ + dist/OpenBrush.dmg + + xcrun stapler staple dist/OpenBrush.dmg + + - name: Upload notarized dmg + uses: actions/upload-artifact@v4 + with: + name: MacOS (DMG) + path: | + dist/OpenBrush.dmg + + release: + name: Create Github Release + needs: [configuration, build, createdmg] + runs-on: ubuntu-latest + if: | + github.event_name == 'push' && + github.repository == 'icosa-foundation/open-brush' && + (github.ref == 'refs/heads/main' || contains(github.ref, 'refs/tags/v')) + + steps: + - name: "Build Changelog" + id: changelog + uses: mikepenz/release-changelog-builder-action@v5 + with: + fromTag: "${{ needs.configuration.outputs.previousrelease }}" + toTag: "${{ needs.configuration.outputs.currentrelease }}" + configurationJson: | + { + "categories": [ + { + "title": "## 🚀 Features", + "labels": ["feature", "enhancement"] + }, + { + "title": "## 🎨 UI / UX", + "labels": ["ux"] + }, + { + "title": "## 🐛 Fixes", + "labels": ["fix", "bugfix"] + }, + { + "title": "## 🛠️ Infrastructure", + "labels": ["infrastructure"] + }, + { + "title": "## 📦 Dependencies / Maintenance", + "labels": ["dependencies", "maintenance"] + }, + { + "title": "## 💬 Uncategorized", + "labels": [] + } + ], + "pr_template": "- #{{TITLE}} (PR ##{{NUMBER}} by @#{{AUTHOR}})" + } + + - name: Echo Changelog (for debugging purposes) + env: + CHANGELOG: ${{ steps.changelog.outputs.changelog }} + run: echo "$CHANGELOG" + + - name: Download Build Artifacts (Windows OpenXR) + uses: actions/download-artifact@v4 + with: + name: Windows OpenXR + path: build_windows_openxr + + - name: Download Build Artifacts (Windows Rift) + uses: actions/download-artifact@v4 + with: + name: Windows Rift + path: build_windows_rift + + - name: Download Build Artifacts (Android OpenXR) + uses: actions/download-artifact@v4 + with: + name: Android OpenXR + path: build_android_openxr + + - name: Download Build Artifacts (Oculus Quest 2+) + uses: actions/download-artifact@v4 + with: + name: Oculus Quest (2+) + path: build_oculus_quest + + - name: Download Build Artifacts (Pico) + uses: actions/download-artifact@v4 + with: + name: Android Pico + path: build_android_pico + + - name: Download Build Artifacts (Linux) + uses: actions/download-artifact@v4 + with: + name: Linux + path: build_linux + + - name: Download Build Artifacts (Mac) + uses: actions/download-artifact@v4 + with: + name: MacOS (DMG) + path: build_macos + + - name: Package Artifacts for release + env: + VERSION: ${{ needs.configuration.outputs.version }} + run: | + mkdir releases + mv build_oculus_quest/*/com.Icosa.OpenBrush*apk releases/OpenBrush_Quest_$VERSION.apk + mv build_android_openxr/*/com.Icosa.OpenBrush*apk releases/OpenBrush_OpenXR_$VERSION.apk + mv build_android_pico/*/com.Icosa.OpenBrush*apk releases/OpenBrush_Pico_$VERSION.apk + mv build_windows_openxr/StandaloneWindows64/ releases/OpenBrush_Desktop_$VERSION/ + mv build_windows_rift/StandaloneWindows64/ releases/OpenBrush_Rift_$VERSION/ + mv build_macos/*.dmg releases/OpenBrush_Mac_$VERSION.dmg + mv build_linux/StandaloneLinux64/ releases/OpenBrush_Linux_$VERSION/ + cd releases + zip -r OpenBrush_Desktop_$VERSION.zip OpenBrush_Desktop_$VERSION/ + rm -rf OpenBrush_Desktop_$VERSION + zip -r OpenBrush_Rift_$VERSION.zip OpenBrush_Rift_$VERSION/ + rm -rf OpenBrush_Rift_$VERSION + zip -r OpenBrush_Linux_$VERSION.zip OpenBrush_Linux_$VERSION/ + + - name: Publish + uses: softprops/action-gh-release@v2 + with: + body: ${{ steps.changelog.outputs.changelog }} + prerelease: ${{ needs.configuration.outputs.prerelease }} + target_commitish: ${{ needs.configuration.outputs.currentrelease }} + tag_name: ${{ needs.configuration.outputs.version }} + files: releases/* + token: ${{ secrets.RELEASE_TOKEN }} + + publish_gitbook: + name: Publish changelog from last major build to open-brush-docs + needs: [configuration, build] + runs-on: ubuntu-latest + if: | + github.event_name == 'push' && + github.repository == 'icosa-foundation/open-brush' && + (github.ref == 'refs/heads/main' || contains(github.ref, 'refs/tags/v')) + steps: + - name: "Build Changelog" + id: changelog + uses: mikepenz/release-changelog-builder-action@v5 + with: + fromTag: "${{ needs.configuration.outputs.previousfullrelease }}" + toTag: "${{ needs.configuration.outputs.currentrelease }}" + configurationJson: | + { + "categories": [ + { + "title": "## 🚀 Features", + "labels": ["feature", "enhancement"] + }, + { + "title": "## 🎨 UI / UX", + "labels": ["ux"] + }, + { + "title": "## 🐛 Fixes", + "labels": ["fix", "bugfix"] + }, + { + "title": "## 🛠️ Infrastructure", + "labels": ["infrastructure"] + }, + { + "title": "## 📦 Dependencies / Maintenance", + "labels": ["dependencies", "maintenance"] + }, + { + "title": "## 💬 Uncategorized", + "labels": [] + } + ], + "template": "# Changelog since #{{FROM_TAG}}\n\n[Full release details](#{{RELEASE_DIFF}})\n\n#{{CHANGELOG}}\n\n", + "pr_template": "- #{{TITLE}} ([PR ##{{NUMBER}}](#{{URL}}) by @#{{AUTHOR}})\n" + } + + - name: Get the current contents of the docs repository + uses: actions/checkout@v4 + with: + repository: icosa-foundation/open-brush-docs + path: open-brush-docs + ref: master + fetch-depth: 0 + sparse-checkout: | + release-history/ + + - name: Create Changelog file + env: + CHANGELOG: ${{ steps.changelog.outputs.changelog }} + run: | + echo "$CHANGELOG" | tee open-brush-docs/release-history/automatic-changelog.md + + - name: Publish release notes + uses: cpina/github-action-push-to-another-repository@composite-1.5.1 + env: + SSH_DEPLOY_KEY: ${{ secrets.OPENBRUSH_DOCS_SSH_DEPLOY_KEY }} + with: + source-directory: 'open-brush-docs/release-history/' + target-directory: 'release-history/' + destination-github-username: 'icosa-foundation' + destination-repository-name: 'open-brush-docs' + user-name: 'release-note-bot' + user-email: automatic-release@icosa + target-branch: master + + publish_steam: + name: Publish Steam Release + needs: [configuration, build, signmacos] + if: | + github.event_name == 'push' && + github.repository == 'icosa-foundation/open-brush' && + (github.ref == 'refs/heads/main' || contains(github.ref, 'refs/tags/v')) + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + sparse-checkout: | + Support/steam + lfs: true # We don't use LFS, but it adds no time, and leave it here in case we do at some point later + - name: Setup steamcmd + uses: CyberAndrii/setup-steamcmd@v1.2.0 + - name: Restore steam login config + run: | + mkdir -p /home/runner/Steam/config + echo "${{ secrets.STEAM_CONFIG_VDF}}" | base64 -d - | gunzip - > /home/runner/Steam/config/config.vdf + md5sum /home/runner/Steam/config/config.vdf + - name: Download Build Artifacts (Windows OpenXR) + uses: actions/download-artifact@v4 + with: + name: Windows OpenXR + path: build_windows_openxr + - name: Download Build Artifacts (MacOS Signed) + uses: actions/download-artifact@v4 + with: + name: MacOS (signed) + path: build_macos + - name: Download Build Artifacts (Linux) + uses: actions/download-artifact@v4 + with: + name: Linux + path: build_linux + - name: Upload Build + run: | + cd build_macos + tar xvfz OpenBrush.tgz + cd - + pip install -U jinjanator + jinjanate Support/steam/app.vdf.j2 > app.vdf + jinjanate Support/steam/main_depot.win.vdf.j2 > build_windows_openxr/main_depot.vdf + jinjanate Support/steam/main_depot.mac.vdf.j2 > build_macos/main_depot.vdf + jinjanate Support/steam/main_depot.linux.vdf.j2 > build_linux/main_depot.vdf + jinjanate Support/steam/installscript_win.vdf.j2 > build_windows_openxr/installscript_win.vdf + steamcmd +login $STEAM_USERNAME +run_app_build $(pwd)/app.vdf +quit + env: + STEAM_USERNAME: ${{ vars.STEAM_USERNAME }} + STEAM_PASSWORD: ${{ secrets.STEAM_PASSWORD }} + VERSION: ${{ needs.configuration.outputs.version }} + OPEN_BRUSH_APP_ID: ${{ vars.STEAM_APP_ID }} + OPEN_BRUSH_WINDOWS_DEPOT_ID: ${{ vars.STEAM_WINDOWS_DEPOT_ID }} + OPEN_BRUSH_MAC_DEPOT_ID: ${{ vars.STEAM_MAC_DEPOT_ID }} + OPEN_BRUSH_LINUX_DEPOT_ID: ${{ vars.STEAM_LINUX_DEPOT_ID }} + OPEN_BRUSH_WINDOWS_EXECUTABLE: ${{ needs.configuration.outputs.basename}}.exe + CHANNEL: beta + - name: Update steam login secret + run: | + gzip /home/runner/Steam/config/config.vdf -c | base64 | gh secret set --visibility all --org icosa-foundation STEAM_CONFIG_VDF + md5sum /home/runner/Steam/config/config.vdf + env: + GITHUB_TOKEN: ${{ secrets.SECRET_UPDATER_PAT }} + - name: Save logs + uses: actions/upload-artifact@v4 + if: ${{ failure() }} + with: + name: steamcmd logs + path: build_output/ + + publish_pimax: + name: Publish Pimax Release + needs: [configuration, build] + runs-on: windows-latest + env: + PIMAX_APP_ID: ${{ vars.PIMAX_APP_ID }} + PIMAX_USERNAME: ${{ vars.PIMAX_USERNAME }} + PIMAX_PASSWORD: ${{ secrets.PIMAX_PASSWORD }} + VERSION: ${{ needs.configuration.outputs.version}} + if: | + github.event_name == 'push' && + github.repository == 'icosa-foundation/open-brush' && + contains(github.ref, 'refs/tags/v') + + steps: + - name: Download Build Artifacts (Windows Pimax) + uses: actions/download-artifact@v4 + with: + name: Windows Pimax + path: build_windows_pimax + + - name: Publish Pimax Builds + run: | + New-Item "releases" -Type Directory + Move-Item -Path build_windows_pimax/StandaloneWindows64/* releases/ + Set-Location -Path "releases" + Compress-Archive -Path ./* -DestinationPath OpenBrush_Pimax_${Env:VERSION}.zip + Invoke-WebRequest -Uri https://dl.appstore.pimax.com/tools/pimax-dev-util.exe -OutFile pimax-dev-util.exe + ./pimax-dev-util.exe login -u $Env:PIMAX_USERNAME -p $Env:PIMAX_PASSWORD + ./pimax-dev-util.exe upload-pc-build -a $Env:PIMAX_APP_ID -d OpenBrush_Pimax_${Env:VERSION}.zip + + publish_viveport: + name: Publish Viveport Release + needs: [configuration, build] + runs-on: windows-latest + env: + VIVEPORT_EMAIL: ${{ vars.VIVEPORT_EMAIL }} + VIVEPORT_PASSWORD: ${{ secrets.VIVEPORT_PASSWORD }} + VIVEPORT_APP_ID: ${{ vars.VIVEPORT_APP_ID }} + VIVEPORT_ORG_ID: ${{ vars.VIVEPORT_ORG_ID }} + VERSION: ${{ needs.configuration.outputs.version}} + if: | + github.event_name == 'push' && + github.repository == 'icosa-foundation/open-brush' && + contains(github.ref, 'refs/tags/v') + + steps: + - name: Download Build Artifacts (Windows OpenXR) + uses: actions/download-artifact@v4 + with: + name: Windows OpenXR + path: build_windows_openxr + + - name: Publish Viveport Builds + run: | + New-Item "releases" -Type Directory + Move-Item -Path build_windows_openxr/StandaloneWindows64/* releases/ + Set-Location -Path "releases" + Compress-Archive -Path ./* -DestinationPath OpenBrush_Viveport_${Env:VERSION}.zip + Invoke-WebRequest -Uri https://assets-global.viveport.com/static-misc/developer-tool/ViveportUploader.exe -OutFile ViveportUploader.exe + ./ViveportUploader.exe -email ${Env:VIVEPORT_EMAIL} -password ${Env:VIVEPORT_PASSWORD} -filepath OpenBrush_Viveport_${Env:VERSION}.zip -appid ${Env:VIVEPORT_APP_ID} -orgid ${Env:VIVEPORT_ORG_ID} -version ${VERSION} + + publish_itch: + name: Publish Itch.io Release + needs: [configuration, build] + runs-on: ubuntu-latest + env: + ITCH_SUBCHANNEL_NAME: ${{ needs.configuration.outputs.itchchannelname }} + BUTLER_CREDENTIALS: ${{ secrets.BUTLER_CREDENTIALS }} + ITCH_GAME: openbrush + ITCH_USER: openbrush + VERSION: ${{ needs.configuration.outputs.version }} + if: | + github.event_name == 'push' && + github.repository == 'icosa-foundation/open-brush' && + (github.ref == 'refs/heads/main' || contains(github.ref, 'refs/tags/v')) + + steps: + - name: Download Build Artifacts (Windows OpenXR) + uses: actions/download-artifact@v4 + with: + name: Windows OpenXR + path: build_windows_openxr + + - name: Download Build Artifacts (Oculus Quest 2+) + uses: actions/download-artifact@v4 + with: + name: Oculus Quest (2+) + path: build_oculus_quest + + - name: Package Artifacts for release + run: | + mkdir releases + mv build_oculus_quest/*/com.Icosa.OpenBrush*apk releases/OpenBrush_Quest_$VERSION.apk + mv build_windows_openxr/StandaloneWindows64/ releases/OpenBrush_Desktop_$VERSION/ + - name: Publish Windows + uses: josephbmanley/butler-publish-itchio-action@master + env: + CHANNEL: windows-${{ env.ITCH_SUBCHANNEL_NAME }} + PACKAGE: releases/OpenBrush_Desktop_${{ needs.configuration.outputs.version }} + - name: Publish Quest + uses: josephbmanley/butler-publish-itchio-action@master + env: + CHANNEL: android-quest-${{ env.ITCH_SUBCHANNEL_NAME }} + PACKAGE: releases/OpenBrush_Quest_${{ needs.configuration.outputs.version }}.apk + + publish_oculus_quest: + name: Publish Oculus Quest 2+ Release + needs: [configuration, build] + runs-on: macos-latest # the ovr-platform-util tool is only available for Mac and Windows + if: | + github.event_name == 'push' && + github.repository == 'icosa-foundation/open-brush' && + (github.ref == 'refs/heads/main' || contains(github.ref, 'refs/tags/v')) + + steps: + - name: Download Build Artifacts (Oculus Quest 2+) + uses: actions/download-artifact@v4 + with: + name: Oculus Quest (2+) + path: build_oculus_quest + - name: Publish Oculus Builds + env: + VERSION: ${{ needs.configuration.outputs.version }} + PRERELEASE: ${{ needs.configuration.outputs.prerelease }} + RAW_CHANGELOG: ${{ needs.configuration.outputs.rawchangelog }} + OCULUS_QUEST_APP_ID: ${{ vars.OCULUS_QUEST_APP_ID }} + OCULUS_QUEST_APP_SECRET: ${{ secrets.OCULUS_QUEST_APP_SECRET }} + run: | + mkdir releases + mv build_oculus_quest/*/com.Icosa.OpenBrush*apk releases/OpenBrush_Quest_$VERSION.apk + mv build_oculus_quest/*/com.Icosa.OpenBrush*.symbols.zip releases/symbols.zip + + cd releases + unzip symbols.zip + curl -L 'https://www.oculus.com/download_app/?id=1462426033810370' -o ovr-platform-util + chmod 755 ovr-platform-util + + if [ "$PRERELEASE" == "false" ] + then + ./ovr-platform-util upload-quest-build --app-id ${OCULUS_QUEST_APP_ID} --app-secret ${OCULUS_QUEST_APP_SECRET} --apk OpenBrush_Quest_$VERSION.apk --channel LIVE:quest2+ --debug_symbols_dir ./arm64-v8a/ --debug-symbols-pattern '*.so' --age-group MIXED_AGES + else + CHANGELOG="${RAW_CHANGELOG}" + ./ovr-platform-util upload-quest-build --app-id ${OCULUS_QUEST_APP_ID} --app-secret ${OCULUS_QUEST_APP_SECRET} --apk OpenBrush_Quest_$VERSION.apk --channel Beta:quest2+ --debug_symbols_dir ./arm64-v8a/ --debug-symbols-pattern '*.so' --notes "${CHANGELOG}" --age-group MIXED_AGES + fi + + publish_oculus_quest1: + name: Publish Oculus Quest 1 Release + needs: [configuration, build] + runs-on: macos-latest # the ovr-platform-util tool is only available for Mac and Windows + if: | + github.event_name == 'push' && + github.repository == 'icosa-foundation/open-brush' && + (github.ref == 'refs/heads/main' || contains(github.ref, 'refs/tags/v')) + + steps: + - name: Download Build Artifacts (Oculus Quest 1) + uses: actions/download-artifact@v4 + with: + name: Oculus Quest (1) + path: build_oculus_quest1 + - name: Publish Oculus Builds + env: + VERSION: ${{ needs.configuration.outputs.version }} + PRERELEASE: ${{ needs.configuration.outputs.prerelease }} + RAW_CHANGELOG: ${{ needs.configuration.outputs.rawchangelog }} + OCULUS_QUEST_APP_ID: ${{ vars.OCULUS_QUEST_APP_ID }} + OCULUS_QUEST_APP_SECRET: ${{ secrets.OCULUS_QUEST_APP_SECRET }} + run: | + mkdir releases1 + mv build_oculus_quest1/*/com.Icosa.OpenBrush*apk releases1/OpenBrush_Quest1_$VERSION.apk + mv build_oculus_quest1/*/com.Icosa.OpenBrush*.symbols.zip releases1/symbols.zip + + cd releases1 + unzip symbols.zip + curl -L 'https://www.oculus.com/download_app/?id=1462426033810370' -o ovr-platform-util + chmod 755 ovr-platform-util + + if [ "$PRERELEASE" == "false" ] + then + ./ovr-platform-util upload-quest-build --app-id ${OCULUS_QUEST_APP_ID} --app-secret ${OCULUS_QUEST_APP_SECRET} --apk OpenBrush_Quest1_$VERSION.apk --channel LIVE:quest1only --debug_symbols_dir ./arm64-v8a/ --debug-symbols-pattern '*.so' --age-group MIXED_AGES + else + CHANGELOG="${RAW_CHANGELOG}" + ./ovr-platform-util upload-quest-build --app-id ${OCULUS_QUEST_APP_ID} --app-secret ${OCULUS_QUEST_APP_SECRET} --apk OpenBrush_Quest1_$VERSION.apk --channel Beta:quest1only --debug_symbols_dir ./arm64-v8a/ --debug-symbols-pattern '*.so' --notes "${CHANGELOG}" --age-group MIXED_AGES + fi + + publish_oculus_rift: + name: Publish Oculus Rift Release + needs: [configuration, build] + runs-on: macos-latest # the ovr-platform-util tool is only available for Mac and Windows + if: | + github.event_name == 'push' && + github.repository == 'icosa-foundation/open-brush' && + (github.ref == 'refs/heads/main' || contains(github.ref, 'refs/tags/v')) + + steps: + - name: Download Build Artifacts (Windows Rift) + uses: actions/download-artifact@v4 + with: + name: Windows Rift + path: build_windows_rift + - name: Publish Oculus Builds + env: + VERSION: ${{ needs.configuration.outputs.version }} + PRERELEASE: ${{ needs.configuration.outputs.prerelease }} + RAW_CHANGELOG: ${{ needs.configuration.outputs.rawchangelog }} + OCULUS_RIFT_APP_ID: ${{ vars.OCULUS_RIFT_APP_ID }} + OCULUS_RIFT_APP_SECRET: ${{ secrets.OCULUS_RIFT_APP_SECRET }} + run: | + mkdir releases + mv build_windows_rift/StandaloneWindows64/ releases/OpenBrush_Rift_$VERSION/ + cd releases + zip -r OpenBrush_Rift_$VERSION.zip OpenBrush_Rift_$VERSION/ + curl -L 'https://www.oculus.com/download_app/?id=1462426033810370' -o ovr-platform-util + chmod 755 ovr-platform-util + + if [ "$PRERELEASE" == "false" ] + then + ./ovr-platform-util upload-rift-build --app-id ${OCULUS_RIFT_APP_ID} --app-secret ${OCULUS_RIFT_APP_SECRET} --build-dir OpenBrush_Rift_$VERSION --launch-file OpenBrush.exe --channel LIVE --version $VERSION --firewall_exceptions true --redistributables 822786567843179,1675031999409058,2657209094360789 + else + CHANGELOG="${RAW_CHANGELOG}" + ./ovr-platform-util upload-rift-build --app-id ${OCULUS_RIFT_APP_ID} --app-secret ${OCULUS_RIFT_APP_SECRET} --build-dir OpenBrush_Rift_$VERSION --launch-file OpenBrush.exe --channel BETA --version $VERSION --firewall_exceptions true --redistributables 822786567843179,1675031999409058,2657209094360789 --notes "${CHANGELOG}" + fi + + publish_pico: + name: Publish Pico Releases + needs: [configuration, build] + runs-on: ubuntu-latest # the ovr-platform-util tool is only available for Mac and Windows + if: | + github.event_name == 'push' && + github.repository == 'icosa-foundation/open-brush' && + (github.ref == 'refs/heads/main' || contains(github.ref, 'refs/tags/v')) + + steps: + - name: Download Build Artifacts (Android Pico) + uses: actions/download-artifact@v4 + with: + name: Android Pico + path: build_android_pico + - name: Download Build Artifacts (Android Pico CN) + uses: actions/download-artifact@v4 + with: + name: Android Pico (CN) + path: build_android_pico_cn + - name: Publish Pico Builds + env: + VERSION: ${{ needs.configuration.outputs.version }} + PRERELEASE: ${{ needs.configuration.outputs.prerelease }} + RAW_CHANGELOG: ${{ needs.configuration.outputs.rawchangelog }} + PICO_APP_ID: ${{ vars.PICO_APP_ID }} + PICO_APP_SECRET: ${{ secrets.PICO_APP_SECRET }} + run: | + mkdir releases + mv build_android_pico/*/com.Icosa.OpenBrush*apk releases/OpenBrush_Pico_$VERSION.apk + mv build_android_pico_cn/*/com.Icosa.OpenBrush*apk releases/OpenBrush_Pico_CN_$VERSION.apk + + cd releases + # pico-cli v1.0.3 + curl -L 'https://p16-platform-static-va.ibyteimg.com/tos-maliva-i-jo6vmmv194-us/linux-noncn/202304111056/pico-cli?r=1681181814847245000' -o pico-cli + chmod 755 pico-cli + + if [ "$PRERELEASE" == "false" ] + then + # The order here matters, because the Chinese build has a slightly higher version code due to the suffix of 1 vs 0 + ./pico-cli upload-build --app-id $PICO_APP_ID --app-secret $PICO_APP_SECRET --region noncn --apk OpenBrush_Pico_$VERSION.apk --channel 2 --notes-en "Version $VERSION" --device 'PICO Neo3,PICO Neo3 Pro,PICO Neo3 Eye,PICO 4' + ./pico-cli upload-build --app-id $PICO_APP_ID --app-secret $PICO_APP_SECRET --region noncn --apk OpenBrush_Pico_CN_$VERSION.apk --channel 1 --notes-en "Version $VERSION" --device 'PICO Neo3,PICO Neo3 Pro,PICO Neo3 Eye,PICO 4' + else + # For Pico, Beta channels can only get one build, not a separate China / non-China build + ./pico-cli upload-build --app-id $PICO_APP_ID --app-secret $PICO_APP_SECRET --region noncn --apk OpenBrush_Pico_$VERSION.apk --channel 3 --notes-en "Version $VERSION" --device 'PICO Neo3,PICO Neo3 Pro,PICO Neo3 Eye,PICO 4' + fi + + publish_ios_zapbox: + name: Publish Zapbox iOS + needs: [configuration, build] + runs-on: macos-latest + if: | + github.event_name == 'push' && + github.repository == 'icosa-foundation/open-brush' && + (github.ref == 'refs/heads/main' || contains(github.ref, 'refs/tags/v')) + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + sparse-checkout: | + Gemfile + Gemfile.lock + fastlane + + - name: Free extra space + # As of 02/08/2024, this increases free space from 21GB to 47GB + run: | + echo "Initial free space" + df -h + rm -rf "$AGENT_TOOLSDIRECTORY" + echo "Disk space after cleanup of \$AGENT_TOOLSDIRECTORY" + df -h + echo "Deleting all Xcode versions except 15.4" + find /Applications/Xcode_* -maxdepth 0 -type d ! -name 'Xcode_15.4.app' -exec rm -rf {} \; + df -h + find /Applications/Xcode* -name "*.app" -exec du -mcsh {} \; # Shows Xcode app sizes + + - name: Download iOS Artifact + uses: actions/download-artifact@v4 + with: + name: iOS Zapbox + path: build + + - name: Fix File Permissions + run: | + export FILENAME=$(basename $(readlink -f build/iOS/OpenBrush*)) + export IOS_BUILD_PATH=$(pwd)/build/iOS/${FILENAME} + + find $IOS_BUILD_PATH -type f -name "*.sh" -exec chmod +x {} \; + + - name: Run fastlane + env: + APPLE_CONNECT_EMAIL: ${{ secrets.APPLE_CONNECT_EMAIL }} + APPLE_DEVELOPER_EMAIL: ${{ secrets.APPLE_DEVELOPER_EMAIL }} + APPLE_TEAM_ID: ${{ vars.APPLE_TEAM_ID }} + + MATCH_REPOSITORY: ${{ secrets.MATCH_REPOSITORY }} + MATCH_DEPLOY_KEY: ${{ secrets.MATCH_DEPLOY_KEY }} + MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }} + + APPSTORE_ISSUER_ID: ${{ secrets.APPSTORE_ISSUER_ID }} + APPSTORE_KEY_ID: ${{ secrets.APPSTORE_KEY_ID }} + APPSTORE_P8: ${{ secrets.APPSTORE_P8 }} + + IOS_BUNDLE_ID: ${{ vars.IOS_ZAPBOX_BUNDLE_ID }} + PROJECT_NAME: Open Brush for Zapbox + FASTLANE_LANE: ${{ needs.configuration.outputs.fastlanelane }} + run: | + eval "$(ssh-agent -s)" + ssh-add - <<< "${MATCH_DEPLOY_KEY}" + export FILENAME=$(basename $(readlink -f build/iOS/OpenBrush*)) + export IOS_BUILD_PATH=$(pwd)/build/iOS/${FILENAME} + + bundle install + bundle exec fastlane ios ${FASTLANE_LANE} + + - name: Save logs + uses: actions/upload-artifact@v4 + if: ${{ failure() }} + with: + name: fastlane logs + path: /Users/runner/Library/Logs/gym/ diff --git a/.github/workflows/delete_branch_cache.yml b/.github/workflows/delete_branch_cache.yml index 4436190127..756bdf3475 100644 --- a/.github/workflows/delete_branch_cache.yml +++ b/.github/workflows/delete_branch_cache.yml @@ -1,40 +1,40 @@ ---- -# yamllint disable rule:line-length -# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json -# Taken from https://github.com/actions/cache/blob/main/tips-and-workarounds.md#force-deletion-of-caches-overriding-default-cache-eviction-policy -name: cleanup caches by a branch -on: # yamllint disable-line rule:truthy - pull_request: - types: - - closed - workflow_dispatch: - -jobs: - cleanup: - runs-on: ubuntu-latest - steps: - - name: Cleanup - run: | - gh extension install actions/gh-actions-cache - - set -x - REPO=${{ github.repository }} - # The github.ref should be refs/pull/XXX/merge, but for some reason, it was set to 'main' in an actual run. Debugging this would be a ginormous pain, since it can apparently only be properly done on an actual merge/close (when I tried testing it locally with a close, it *did* work!), and so we'll just set this manually and ignore it for now. Accidentally deleting the caches on main is unacceptable. - # BRANCH=${{ github.ref }} - BRANCH=${PR_TARGET_NAME} - - echo "Fetching list of cache key" - cacheKeysForPR=$(gh actions-cache list -R $REPO -B $BRANCH | cut -f 1 ) - - ## Setting this to not fail the workflow while deleting cache keys. - set +e - echo "Deleting caches..." - for cacheKey in $cacheKeysForPR - do - gh actions-cache delete $cacheKey -R $REPO -B $BRANCH --confirm - done - echo "Done" - env: - # Note that this can only run on branches from the repo; otherwise, we don't have write permissions so we can't delete the caches - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - PR_TARGET_NAME: ${{ format('refs/pull/{0}/merge', github.event.number) }} +--- +# yamllint disable rule:line-length +# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json +# Taken from https://github.com/actions/cache/blob/main/tips-and-workarounds.md#force-deletion-of-caches-overriding-default-cache-eviction-policy +name: cleanup caches by a branch +on: # yamllint disable-line rule:truthy + pull_request: + types: + - closed + workflow_dispatch: + +jobs: + cleanup: + runs-on: ubuntu-latest + steps: + - name: Cleanup + run: | + gh extension install actions/gh-actions-cache + + set -x + REPO=${{ github.repository }} + # The github.ref should be refs/pull/XXX/merge, but for some reason, it was set to 'main' in an actual run. Debugging this would be a ginormous pain, since it can apparently only be properly done on an actual merge/close (when I tried testing it locally with a close, it *did* work!), and so we'll just set this manually and ignore it for now. Accidentally deleting the caches on main is unacceptable. + # BRANCH=${{ github.ref }} + BRANCH=${PR_TARGET_NAME} + + echo "Fetching list of cache key" + cacheKeysForPR=$(gh actions-cache list -R $REPO -B $BRANCH | cut -f 1 ) + + ## Setting this to not fail the workflow while deleting cache keys. + set +e + echo "Deleting caches..." + for cacheKey in $cacheKeysForPR + do + gh actions-cache delete $cacheKey -R $REPO -B $BRANCH --confirm + done + echo "Done" + env: + # Note that this can only run on branches from the repo; otherwise, we don't have write permissions so we can't delete the caches + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PR_TARGET_NAME: ${{ format('refs/pull/{0}/merge', github.event.number) }} diff --git a/.github/workflows/export_secrets.yml b/.github/workflows/export_secrets.yml index 034f50b68b..841cfa07f4 100644 --- a/.github/workflows/export_secrets.yml +++ b/.github/workflows/export_secrets.yml @@ -1,33 +1,33 @@ ---- -# yamllint disable rule:line-length -# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json -name: Backup secrets (to OpenSSL encrypted file) -on: # yamllint disable-line rule:truthy - workflow_dispatch: - -jobs: - backup_secrets: - runs-on: ubuntu-latest - steps: - - name: Backup secrets - env: - SECRETS: ${{ toJSON(secrets) }} - VARS: ${{ toJSON(vars) }} - OPENSSL_ITER: 1000 - OPENSSL_PASS: ${{ secrets.SECRET_EXPORT_OPENSSL_PASSWORD }} - run: | - echo "$SECRETS" | tee secrets.txt - echo "$VARS" | tee vars.txt - openssl enc -aes-256-cbc -md sha512 -pbkdf2 -iter $OPENSSL_ITER -salt -in secrets.txt -out secrets.enc.txt -pass pass:$OPENSSL_PASS - openssl enc -aes-256-cbc -md sha512 -pbkdf2 -iter $OPENSSL_ITER -salt -in vars.txt -out vars.enc.txt -pass pass:$OPENSSL_PASS - echo "To decrypt the secrets, use the following command(s):" - echo "openssl enc -aes-256-cbc -d -md sha512 -pbkdf2 -iter $OPENSSL_ITER -salt -in secrets.enc.txt -out secrets.txt -pass pass:" - echo "openssl enc -aes-256-cbc -d -md sha512 -pbkdf2 -iter $OPENSSL_ITER -salt -in vars.enc.txt -out vars.txt -pass pass:" - - - name: Upload encrypted secrets - uses: actions/upload-artifact@v4 - with: - name: exports - path: | - secrets.enc.txt - vars.enc.txt +--- +# yamllint disable rule:line-length +# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json +name: Backup secrets (to OpenSSL encrypted file) +on: # yamllint disable-line rule:truthy + workflow_dispatch: + +jobs: + backup_secrets: + runs-on: ubuntu-latest + steps: + - name: Backup secrets + env: + SECRETS: ${{ toJSON(secrets) }} + VARS: ${{ toJSON(vars) }} + OPENSSL_ITER: 1000 + OPENSSL_PASS: ${{ secrets.SECRET_EXPORT_OPENSSL_PASSWORD }} + run: | + echo "$SECRETS" | tee secrets.txt + echo "$VARS" | tee vars.txt + openssl enc -aes-256-cbc -md sha512 -pbkdf2 -iter $OPENSSL_ITER -salt -in secrets.txt -out secrets.enc.txt -pass pass:$OPENSSL_PASS + openssl enc -aes-256-cbc -md sha512 -pbkdf2 -iter $OPENSSL_ITER -salt -in vars.txt -out vars.enc.txt -pass pass:$OPENSSL_PASS + echo "To decrypt the secrets, use the following command(s):" + echo "openssl enc -aes-256-cbc -d -md sha512 -pbkdf2 -iter $OPENSSL_ITER -salt -in secrets.enc.txt -out secrets.txt -pass pass:" + echo "openssl enc -aes-256-cbc -d -md sha512 -pbkdf2 -iter $OPENSSL_ITER -salt -in vars.enc.txt -out vars.txt -pass pass:" + + - name: Upload encrypted secrets + uses: actions/upload-artifact@v4 + with: + name: exports + path: | + secrets.enc.txt + vars.enc.txt diff --git a/.github/workflows/generate_certs.yml b/.github/workflows/generate_certs.yml index ba20edbb87..ba143640e3 100644 --- a/.github/workflows/generate_certs.yml +++ b/.github/workflows/generate_certs.yml @@ -1,47 +1,47 @@ ---- -# yamllint disable rule:line-length -# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json -name: Generate iOS Certs - -on: # yamllint disable-line rule:truthy - workflow_run: - workflows: ['iOS One-Time Setup'] - types: - - completed - workflow_dispatch: - -jobs: - generate_certs: - if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }} - runs-on: macos-latest - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - sparse-checkout: | - Gemfile - Gemfile.lock - fastlane - - uses: ruby/setup-ruby@v1 - with: - ruby-version: 3.2 - bundler-cache: true - - - name: Build iOS - shell: bash - run: | - eval "$(ssh-agent -s)" - ssh-add - <<< "${MATCH_DEPLOY_KEY}" - bundle exec fastlane ios sync_certificates - env: - APPSTORE_ISSUER_ID: ${{ secrets.APPSTORE_ISSUER_ID }} - APPSTORE_KEY_ID: ${{ secrets.APPSTORE_KEY_ID }} - APPSTORE_P8: ${{ secrets.APPSTORE_P8 }} - - IOS_BUNDLE_ID: ${{ vars.IOS_ZAPBOX_BUNDLE_ID }} - - GH_PAT: ${{ secrets.MATCH_PAT }} - GITHUB_REPOSITORY: ${{ env.GITHUB_REPOSITORY }} - MATCH_REPOSITORY: ${{ secrets.MATCH_REPOSITORY }} - MATCH_DEPLOY_KEY: ${{ secrets.MATCH_DEPLOY_KEY }} - MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }} +--- +# yamllint disable rule:line-length +# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json +name: Generate iOS Certs + +on: # yamllint disable-line rule:truthy + workflow_run: + workflows: ['iOS One-Time Setup'] + types: + - completed + workflow_dispatch: + +jobs: + generate_certs: + if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }} + runs-on: macos-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + sparse-checkout: | + Gemfile + Gemfile.lock + fastlane + - uses: ruby/setup-ruby@v1 + with: + ruby-version: 3.2 + bundler-cache: true + + - name: Build iOS + shell: bash + run: | + eval "$(ssh-agent -s)" + ssh-add - <<< "${MATCH_DEPLOY_KEY}" + bundle exec fastlane ios sync_certificates + env: + APPSTORE_ISSUER_ID: ${{ secrets.APPSTORE_ISSUER_ID }} + APPSTORE_KEY_ID: ${{ secrets.APPSTORE_KEY_ID }} + APPSTORE_P8: ${{ secrets.APPSTORE_P8 }} + + IOS_BUNDLE_ID: ${{ vars.IOS_ZAPBOX_BUNDLE_ID }} + + GH_PAT: ${{ secrets.MATCH_PAT }} + GITHUB_REPOSITORY: ${{ env.GITHUB_REPOSITORY }} + MATCH_REPOSITORY: ${{ secrets.MATCH_REPOSITORY }} + MATCH_DEPLOY_KEY: ${{ secrets.MATCH_DEPLOY_KEY }} + MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }} diff --git a/.github/workflows/get_license.yml b/.github/workflows/get_license.yml index a86886bf28..e4beed0c57 100644 --- a/.github/workflows/get_license.yml +++ b/.github/workflows/get_license.yml @@ -1,22 +1,22 @@ ---- -name: Acquire activation file -on: - workflow_dispatch: - -jobs: - activation: - name: Request manual activation file 🔑 - runs-on: ubuntu-latest - steps: - # Request manual activation file - - name: Request manual activation file - id: getManualLicenseFile - uses: game-ci/unity-request-activation-file@v2 - with: - unityVersion: 2019.4.25f1 - - name: Expose as artifact - # Upload artifact (Unity_v20XX.X.XXXX.alf) - uses: actions/upload-artifact@v4 - with: - name: ${{ steps.getManualLicenseFile.outputs.filePath }} - path: ${{ steps.getManualLicenseFile.outputs.filePath }} +--- +name: Acquire activation file +on: + workflow_dispatch: + +jobs: + activation: + name: Request manual activation file 🔑 + runs-on: ubuntu-latest + steps: + # Request manual activation file + - name: Request manual activation file + id: getManualLicenseFile + uses: game-ci/unity-request-activation-file@v2 + with: + unityVersion: 2019.4.25f1 + - name: Expose as artifact + # Upload artifact (Unity_v20XX.X.XXXX.alf) + uses: actions/upload-artifact@v4 + with: + name: ${{ steps.getManualLicenseFile.outputs.filePath }} + path: ${{ steps.getManualLicenseFile.outputs.filePath }} diff --git a/.github/workflows/ios_setup.yml b/.github/workflows/ios_setup.yml index b46f2bd7b7..0fc7a7eb1b 100644 --- a/.github/workflows/ios_setup.yml +++ b/.github/workflows/ios_setup.yml @@ -1,36 +1,36 @@ ---- -# yamllint disable rule:line-length -# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json -name: iOS One-Time Setup - -on: workflow_dispatch - -jobs: - setup: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - sparse-checkout: | - Gemfile - Gemfile.lock - fastlane - - uses: ruby/setup-ruby@v1 - with: - ruby-version: 3.2 - bundler-cache: true - - # Note that this job cannot be rerun because of https://github.com/joshdholtz/fastlane-plugin-github_action/issues/4 - - name: Build iOS - shell: bash - run: | - bundle exec fastlane ios init_ci - env: - APPSTORE_ISSUER_ID: ${{ secrets.APPSTORE_ISSUER_ID }} - APPSTORE_KEY_ID: ${{ secrets.APPSTORE_KEY_ID }} - APPSTORE_P8: ${{ secrets.APPSTORE_P8 }} - - GH_PAT: ${{ secrets.MATCH_PAT }} - GITHUB_REPOSITORY: ${{ env.GITHUB_REPOSITORY }} - MATCH_REPOSITORY: ${{ secrets.MATCH_REPOSITORY }} +--- +# yamllint disable rule:line-length +# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json +name: iOS One-Time Setup + +on: workflow_dispatch + +jobs: + setup: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + sparse-checkout: | + Gemfile + Gemfile.lock + fastlane + - uses: ruby/setup-ruby@v1 + with: + ruby-version: 3.2 + bundler-cache: true + + # Note that this job cannot be rerun because of https://github.com/joshdholtz/fastlane-plugin-github_action/issues/4 + - name: Build iOS + shell: bash + run: | + bundle exec fastlane ios init_ci + env: + APPSTORE_ISSUER_ID: ${{ secrets.APPSTORE_ISSUER_ID }} + APPSTORE_KEY_ID: ${{ secrets.APPSTORE_KEY_ID }} + APPSTORE_P8: ${{ secrets.APPSTORE_P8 }} + + GH_PAT: ${{ secrets.MATCH_PAT }} + GITHUB_REPOSITORY: ${{ env.GITHUB_REPOSITORY }} + MATCH_REPOSITORY: ${{ secrets.MATCH_REPOSITORY }} diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index e527be76a2..55640652f3 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -1,39 +1,39 @@ ---- -# yamllint disable rule:line-length -name: pre-commit - -on: # yamllint disable-line rule:truthy - pull_request: - push: - branches: - - main # We never expect this to fail, since it must have passed on the pull request, but this will let us create a cache on main that other PRs can use, speeding up the process - -jobs: - pre-commit: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5.3.0 - with: - python-version: '3.12' - - uses: actions/setup-dotnet@v4.1.0 - with: - dotnet-version: '8.0.x' - - name: Install pre-commit - run: python -m pip install pre-commit - shell: bash - - name: Cache pre-commit environments - uses: actions/cache@v4 - with: - path: ~/.cache/pre-commit - key: pre-commit-3|${{ env.pythonLocation }}|${{ hashFiles('.pre-commit-config.yaml') }} - - name: Setup pre-commit environments - run: pre-commit run - - name: Run pre-commit dotnet-format, with retries - uses: Wandalen/wretry.action@v3 - with: - command: pre-commit run dotnet-format --show-diff-on-failure --color=always --all-files || { git checkout -- . ; exit 1 ; } # In case dotnet-format fails, reset the changes it made. This way, we can differentiate between a NuGet failure and a real formatting issue - - name: Remove dotnet-format from the list of pre-commit jobs to run (since we already ran it) - run: yq eval 'del(.repos[] | select(.hooks[].id == "dotnet-format"))' -i .pre-commit-config.yaml - - name: Run the rest of pre-commit - run: pre-commit run --show-diff-on-failure --color=always --all-files +--- +# yamllint disable rule:line-length +name: pre-commit + +on: # yamllint disable-line rule:truthy + pull_request: + push: + branches: + - main # We never expect this to fail, since it must have passed on the pull request, but this will let us create a cache on main that other PRs can use, speeding up the process + +jobs: + pre-commit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5.3.0 + with: + python-version: '3.12' + - uses: actions/setup-dotnet@v4.1.0 + with: + dotnet-version: '8.0.x' + - name: Install pre-commit + run: python -m pip install pre-commit + shell: bash + - name: Cache pre-commit environments + uses: actions/cache@v4 + with: + path: ~/.cache/pre-commit + key: pre-commit-3|${{ env.pythonLocation }}|${{ hashFiles('.pre-commit-config.yaml') }} + - name: Setup pre-commit environments + run: pre-commit run + - name: Run pre-commit dotnet-format, with retries + uses: Wandalen/wretry.action@v3 + with: + command: pre-commit run dotnet-format --show-diff-on-failure --color=always --all-files || { git checkout -- . ; exit 1 ; } # In case dotnet-format fails, reset the changes it made. This way, we can differentiate between a NuGet failure and a real formatting issue + - name: Remove dotnet-format from the list of pre-commit jobs to run (since we already ran it) + run: yq eval 'del(.repos[] | select(.hooks[].id == "dotnet-format"))' -i .pre-commit-config.yaml + - name: Run the rest of pre-commit + run: pre-commit run --show-diff-on-failure --color=always --all-files diff --git a/.github/workflows/test_unity_credentials.yml b/.github/workflows/test_unity_credentials.yml index 3ee00c3f52..418b2b5a42 100644 --- a/.github/workflows/test_unity_credentials.yml +++ b/.github/workflows/test_unity_credentials.yml @@ -1,22 +1,22 @@ ---- -# yamllint disable rule:line-length -# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json -name: Test Unity Credentials -on: - workflow_dispatch: - -env: - UNITY_VERSION: "2021.3.30f1" - UNITY_EMAIL: ${{ vars.UNITY_EMAIL }} - UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }} - UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }} - -jobs: - test_license: - runs-on: ubuntu-latest - steps: - - name: Unity - Activate - uses: game-ci/unity-activate@v2 - - name: Unity - Return License - uses: game-ci/unity-return-license@v2 - if: always() +--- +# yamllint disable rule:line-length +# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json +name: Test Unity Credentials +on: + workflow_dispatch: + +env: + UNITY_VERSION: "2021.3.30f1" + UNITY_EMAIL: ${{ vars.UNITY_EMAIL }} + UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }} + UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }} + +jobs: + test_license: + runs-on: ubuntu-latest + steps: + - name: Unity - Activate + uses: game-ci/unity-activate@v2 + - name: Unity - Return License + uses: game-ci/unity-return-license@v2 + if: always() diff --git a/.github/workflows/third_party_notices.yml b/.github/workflows/third_party_notices.yml index c5814b4c96..8b0d4b09e8 100644 --- a/.github/workflows/third_party_notices.yml +++ b/.github/workflows/third_party_notices.yml @@ -1,19 +1,19 @@ ---- -# yamllint disable rule:line-length -name: Check for updated Third Party Notices - -on: # yamllint disable-line rule:truthy - pull_request: - -jobs: - third-party-notices: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5.3.0 - with: - python-version: '3.12' - - name: Run the generator - run: | - python Support/Python/unitybuild/generate_notice.py - git diff --exit-code +--- +# yamllint disable rule:line-length +name: Check for updated Third Party Notices + +on: # yamllint disable-line rule:truthy + pull_request: + +jobs: + third-party-notices: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5.3.0 + with: + python-version: '3.12' + - name: Run the generator + run: | + python Support/Python/unitybuild/generate_notice.py + git diff --exit-code diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 506bf6203a..bc8ba20e11 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,48 +1,48 @@ ---- -repos: - - repo: https://github.com/adrienverge/yamllint.git - rev: v1.35.1 - hooks: - - id: yamllint - - repo: https://github.com/psf/black - rev: 24.3.0 - hooks: - - id: black - files: ^Support/ - language_version: python3 - - repo: https://github.com/pycqa/flake8 - rev: 7.0.0 - hooks: - - id: flake8 - files: ^Support/ - - repo: https://github.com/PyCQA/pylint.git - rev: v3.1.0 - hooks: - - id: pylint - name: pylint - files: ^Support/ - language_version: python3 - additional_dependencies: - - typing_extensions - args: - - --load-plugins=pylint.extensions.redefined_variable_type,pylint.extensions.bad_builtin - - --disable=import-error - - repo: https://github.com/google/yamlfmt - rev: v0.11.0 - hooks: - - id: yamlfmt - args: - - -conf - - .yamlfmt - - repo: local - hooks: - # Use dotnet format already installed on your machine - - id: dotnet-format - name: dotnet-format - language: system - entry: dotnet format whitespace - types_or: [c#, vb] - exclude: ^(Assets/ThirdParty)|(Packages/)|(Assets/Photon/) - args: - - --folder - - --include +--- +repos: + - repo: https://github.com/adrienverge/yamllint.git + rev: v1.35.1 + hooks: + - id: yamllint + - repo: https://github.com/psf/black + rev: 24.3.0 + hooks: + - id: black + files: ^Support/ + language_version: python3 + - repo: https://github.com/pycqa/flake8 + rev: 7.0.0 + hooks: + - id: flake8 + files: ^Support/ + - repo: https://github.com/PyCQA/pylint.git + rev: v3.1.0 + hooks: + - id: pylint + name: pylint + files: ^Support/ + language_version: python3 + additional_dependencies: + - typing_extensions + args: + - --load-plugins=pylint.extensions.redefined_variable_type,pylint.extensions.bad_builtin + - --disable=import-error + - repo: https://github.com/google/yamlfmt + rev: v0.11.0 + hooks: + - id: yamlfmt + args: + - -conf + - .yamlfmt + - repo: local + hooks: + # Use dotnet format already installed on your machine + - id: dotnet-format + name: dotnet-format + language: system + entry: dotnet format whitespace + types_or: [c#, vb] + exclude: ^(Assets/ThirdParty)|(Packages/)|(Assets/Photon/) + args: + - --folder + - --include diff --git a/Assets/OculusMR/OculusMRController.cs b/Assets/OculusMR/OculusMRController.cs index fd5283d3f5..78860d21ec 100644 --- a/Assets/OculusMR/OculusMRController.cs +++ b/Assets/OculusMR/OculusMRController.cs @@ -1,89 +1,89 @@ -// 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 - { -#if OCULUS_SUPPORTED - 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(new RoomCreateData() - { - roomName = "OculusMRRoom", - maxPlayers = 12 - }); - } - else - { - MultiplayerManager.m_Instance.Connect(new RoomCreateData() - { - roomName = "OculusMRRoom", - maxPlayers = 12 - }); - } - } - - public async void RemoteSyncToAnchor(string uuid) - { - await m_SpatialAnchorManager.SyncToRemoteAnchor(uuid, OVRSpace.StorageLocation.Cloud); - - if (!loadedScene) - { - ovrSceneManager.LoadSceneModel(); - loadedScene = true; - } - } -#endif // OCULUS_SUPPORTED - } -} - +// 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 + { +#if OCULUS_SUPPORTED + 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.JoinRoom(new RoomCreateData() + { + roomName = "OculusMRRoom", + maxPlayers = 12 + }); + } + else + { + MultiplayerManager.m_Instance.JoinRoom(new RoomCreateData() + { + roomName = "OculusMRRoom", + maxPlayers = 12 + }); + } + } + + public async void RemoteSyncToAnchor(string uuid) + { + await m_SpatialAnchorManager.SyncToRemoteAnchor(uuid, OVRSpace.StorageLocation.Cloud); + + if (!loadedScene) + { + ovrSceneManager.LoadSceneModel(); + loadedScene = true; + } + } +#endif // OCULUS_SUPPORTED + } +} + diff --git a/Assets/Prefabs/Panels/MultiplayerPanel.prefab b/Assets/Prefabs/Panels/MultiplayerPanel.prefab index 93afd45381..0761b4d3c9 100644 --- a/Assets/Prefabs/Panels/MultiplayerPanel.prefab +++ b/Assets/Prefabs/Panels/MultiplayerPanel.prefab @@ -32,6 +32,7 @@ Transform: m_ConstrainProportionsScale: 0 m_Children: - {fileID: 2965947989537662772} + - {fileID: 8346410928580794432} - {fileID: 438733611317437076} - {fileID: 3405719730669880946} - {fileID: 1954481780995984980} @@ -59,9 +60,12 @@ MonoBehaviour: m_MeshCollider: {fileID: 6569620} m_ParticleBounds: {x: 0, y: 0, z: 0} m_PanelPopUpMap: - - m_PopUpPrefab: {fileID: 8644332587479430734, guid: 51ce29df58ccd0343a277023a12c50ff, + - m_PopUpPrefab: {fileID: 8644332587479430734, guid: bdb7d2ac05cca8346b210c2b576878cb, type: 3} m_Command: 1005 + - m_PopUpPrefab: {fileID: 8644332587479430734, guid: bdb7d2ac05cca8346b210c2b576878cb, + type: 3} + m_Command: 1009 m_PanelDescription: m_LocalizedPanelDescription: m_TableReference: @@ -107,12 +111,10 @@ MonoBehaviour: m_CanBeDetachedFromWand: 0 m_PopUpGazeDuration: 0.2 m_PromoBorders: [] - m_RoomNumberTextLobby: {fileID: 1974621732855197313} - m_RoomNumberTextRoomSettings: {fileID: 932498118536074667} - m_DoesRoomNumberExist: {fileID: 5365545856024163458} - m_AlertUserInBeginnerMode: {fileID: 5985317331042495584} - m_LobbyElements: {fileID: 3644177191085397888} - m_JoinedElements: {fileID: 6171746570159008301} + m_State: {fileID: 352620620088219705} + m_RoomNumber: {fileID: 9160963822650286723} + m_Nickname: {fileID: 4888819335275065630} + m_Alerts: {fileID: 0} references: version: 2 RefIds: [] @@ -154,7 +156,7 @@ Transform: m_GameObject: {fileID: 169614} serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalPosition: {x: -0.022, y: 0.479, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] @@ -197,7 +199,7 @@ GameObject: m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 - m_IsActive: 1 + m_IsActive: 0 --- !u!4 &498528 Transform: m_ObjectHideFlags: 0 @@ -207,7 +209,7 @@ Transform: m_GameObject: {fileID: 173754} serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalPosition: {x: 0, y: 0.674, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] @@ -232,7 +234,7 @@ BoxCollider: m_ProvidesContacts: 0 m_Enabled: 1 serializedVersion: 3 - m_Size: {x: 2.4, y: 3.3, z: 0.5} + m_Size: {x: 2, y: 2, z: 0.5} m_Center: {x: 0, y: 0, z: 0} --- !u!1 &176750 GameObject: @@ -505,7 +507,7 @@ Transform: m_GameObject: {fileID: 1204492690482570} serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: 0.907, y: -0.255, z: -0.01} + m_LocalPosition: {x: 0.635, y: -0.477, z: -0.01} m_LocalScale: {x: 0.29999998, y: 0.29999998, z: 0.29999998} m_ConstrainProportionsScale: 0 m_Children: @@ -688,9 +690,9 @@ Transform: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1744263076835604} serializedVersion: 2 - m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: -0, y: 0, z: 0} - m_LocalScale: {x: 100, y: 100, z: 100} + m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4696385544466816} @@ -702,7 +704,7 @@ MeshFilter: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1744263076835604} - m_Mesh: {fileID: 4300002, guid: 90e92f1d849f0b448ac3e9a402f08bed, type: 3} + m_Mesh: {fileID: 43497663539397816, guid: eb32a73de999860498953bf6c8960982, type: 3} --- !u!23 &23425245546884932 MeshRenderer: m_ObjectHideFlags: 0 @@ -762,7 +764,7 @@ GameObject: m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 - m_IsActive: 1 + m_IsActive: 0 --- !u!4 &4887086521611946 Transform: m_ObjectHideFlags: 0 @@ -944,7 +946,7 @@ MeshRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_AdditionalVertexStreams: {fileID: 0} ---- !u!1 &1697630693985053451 +--- !u!1 &1032351299171828485 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -952,49 +954,48 @@ GameObject: m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - - component: {fileID: 1141409664942745479} - - component: {fileID: 1760323139199521700} - - component: {fileID: 4490950674500302730} - - component: {fileID: 6816865797365254487} - - component: {fileID: 7781273403086602364} + - component: {fileID: 682210897750110130} + - component: {fileID: 9059887426427637701} + - component: {fileID: 6477758959252966850} + - component: {fileID: 5621504769770497967} + - component: {fileID: 3224196841018929977} m_Layer: 16 - m_Name: join Button + m_Name: ConnectButton m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!4 &1141409664942745479 +--- !u!4 &682210897750110130 Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1697630693985053451} + m_GameObject: {fileID: 1032351299171828485} serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: -0.329, y: 0.31899986, z: -0.042} - m_LocalScale: {x: 0.4, y: 0.4, z: 0.35} + m_LocalPosition: {x: -0.617, y: -0.323, z: -0.042} + m_LocalScale: {x: 0.33, y: 0.33, z: 0.33} m_ConstrainProportionsScale: 0 - m_Children: - - {fileID: 7015233605674447413} - m_Father: {fileID: 2965947989537662772} + m_Children: [] + m_Father: {fileID: 8346410928580794432} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!33 &1760323139199521700 +--- !u!33 &9059887426427637701 MeshFilter: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1697630693985053451} - m_Mesh: {fileID: 4300000, guid: 70d5ab47bcc3aa0439e20bd40350c05a, type: 3} ---- !u!23 &4490950674500302730 + m_GameObject: {fileID: 1032351299171828485} + m_Mesh: {fileID: 4300000, guid: 5501f437160666942ae970f3648fbeb8, type: 3} +--- !u!23 &6477758959252966850 MeshRenderer: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1697630693985053451} + m_GameObject: {fileID: 1032351299171828485} m_Enabled: 1 m_CastShadows: 0 m_ReceiveShadows: 0 @@ -1030,13 +1031,13 @@ MeshRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_AdditionalVertexStreams: {fileID: 0} ---- !u!65 &6816865797365254487 +--- !u!65 &5621504769770497967 BoxCollider: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1697630693985053451} + m_GameObject: {fileID: 1032351299171828485} m_Material: {fileID: 0} m_IncludeLayers: serializedVersion: 2 @@ -1049,15 +1050,15 @@ BoxCollider: m_ProvidesContacts: 0 m_Enabled: 1 serializedVersion: 3 - m_Size: {x: 1.4, y: 1, z: 0.1} + m_Size: {x: 1.1, y: 1.1, z: 0.1} m_Center: {x: -0.000000074505806, y: 0.000000022351742, z: 0} ---- !u!114 &7781273403086602364 +--- !u!114 &3224196841018929977 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1697630693985053451} + m_GameObject: {fileID: 1032351299171828485} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: b78a8d7209bbdc546979b549a875d550, type: 3} @@ -1068,9 +1069,9 @@ MonoBehaviour: m_DescriptionText: m_LocalizedDescription: m_TableReference: - m_TableCollectionName: + m_TableCollectionName: GUID:c84355079ab3f3e4f8f3812258805f86 m_TableEntryReference: - m_KeyId: 0 + m_KeyId: 294636550056648704 m_Key: m_FallbackState: 0 m_WaitForCompletion: 0 @@ -1087,7 +1088,7 @@ MonoBehaviour: m_LocalVariables: [] m_DescriptionActivateSpeed: 12 m_DescriptionZScale: 1 - m_ButtonTexture: {fileID: 2800000, guid: cdc7d38d9e0b30443961d1cc27195586, type: 3} + m_ButtonTexture: {fileID: 2800000, guid: 366faf1780512c44b84568938fb37aaa, type: 3} m_AtlasTexture: 1 m_ToggleButton: 0 m_LongPressReleaseButton: 0 @@ -1097,7 +1098,7 @@ MonoBehaviour: m_HoverScale: 1.1 m_HoverBoxColliderGrow: 0.2 m_AddOverlay: 0 - m_Command: 1004 + m_Command: 1007 m_CommandParam: 1 m_CommandParam2: -1 m_RequiresPopup: 0 @@ -1130,7 +1131,7 @@ MonoBehaviour: references: version: 2 RefIds: [] ---- !u!1 &2296016956045778647 +--- !u!1 &1499154774102387286 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -1138,43 +1139,42 @@ GameObject: m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - - component: {fileID: 47429484269554977} - - component: {fileID: 3374390498020739628} - - component: {fileID: 5985317331042495584} - - component: {fileID: 2841728730450484306} + - component: {fileID: 8994233172856634412} + - component: {fileID: 3100622910448847287} + - component: {fileID: 352620620088219705} m_Layer: 16 - m_Name: AllertBeginnerMode + m_Name: Status m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 - m_IsActive: 0 ---- !u!224 &47429484269554977 + m_IsActive: 1 +--- !u!224 &8994233172856634412 RectTransform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 2296016956045778647} + m_GameObject: {fileID: 1499154774102387286} m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: -0.05} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] - m_Father: {fileID: 2965947989537662772} + m_Father: {fileID: 8346410928580794432} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: 0.143, y: 1.355} + m_AnchoredPosition: {x: 0.147, y: 0.035999954} m_SizeDelta: {x: 1.104868, y: 1.4460607} m_Pivot: {x: 0.5, y: 0.5} ---- !u!23 &3374390498020739628 +--- !u!23 &3100622910448847287 MeshRenderer: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 2296016956045778647} + m_GameObject: {fileID: 1499154774102387286} m_Enabled: 1 m_CastShadows: 0 m_ReceiveShadows: 0 @@ -1210,13 +1210,13 @@ MeshRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_AdditionalVertexStreams: {fileID: 0} ---- !u!114 &5985317331042495584 +--- !u!114 &352620620088219705 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 2296016956045778647} + m_GameObject: {fileID: 1499154774102387286} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 9541d86e2fd84c1d9990edf0852d74ab, type: 3} @@ -1230,7 +1230,9 @@ MonoBehaviour: m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] - m_text: To enter a room turn first set to beginner mode + m_text: 'Status: + +' m_isRightToLeft: 0 m_fontAsset: {fileID: 11400000, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} m_sharedMaterial: {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} @@ -1239,8 +1241,8 @@ MonoBehaviour: m_fontMaterials: [] m_fontColor32: serializedVersion: 2 - rgba: 4278190335 - m_fontColor: {r: 1, g: 0, b: 0, a: 1} + rgba: 4294967295 + m_fontColor: {r: 1, g: 1, b: 1, a: 1} m_enableVertexGradient: 0 m_colorMode: 3 m_fontColorGradient: @@ -1257,15 +1259,15 @@ MonoBehaviour: m_faceColor: serializedVersion: 2 rgba: 4294967295 - m_fontSize: 0.8 - m_fontSizeBase: 0.8 + m_fontSize: 1.5 + m_fontSizeBase: 1.5 m_fontWeight: 400 m_enableAutoSizing: 0 m_fontSizeMin: 18 m_fontSizeMax: 72 m_fontStyle: 0 - m_HorizontalAlignment: 2 - m_VerticalAlignment: 1024 + m_HorizontalAlignment: 1 + m_VerticalAlignment: 512 m_textAlignment: 65535 m_characterSpacing: 0 m_wordSpacing: 0 @@ -1293,32 +1295,16 @@ MonoBehaviour: m_VertexBufferAutoSizeReduction: 0 m_useMaxVisibleDescender: 1 m_pageToDisplay: 1 - m_margin: {x: -0.31141979, y: 0.74942493, z: 0.083378464, w: 0.49275255} + m_margin: {x: -0.31141979, y: -0.12023544, z: -0.1872099, w: 1.1764753} m_isUsingLegacyAnimationComponent: 0 m_isVolumetricText: 0 _SortingLayer: 0 _SortingLayerID: 0 _SortingOrder: 0 m_hasFontAssetChanged: 0 - m_renderer: {fileID: 3374390498020739628} + m_renderer: {fileID: 3100622910448847287} m_maskType: 0 ---- !u!114 &2841728730450484306 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 2296016956045778647} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 69beb381e244f92449b8c4cf954630e9, type: 3} - m_Name: - m_EditorClassIdentifier: - m_TrackedObjects: [] - references: - version: 2 - RefIds: [] ---- !u!1 &2571361884171689698 +--- !u!1 &1697630693985053451 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -1326,42 +1312,49 @@ GameObject: m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - - component: {fileID: 6876281661055682962} - - component: {fileID: 5708211407015971906} - - component: {fileID: 3923005959821366055} + - component: {fileID: 1141409664942745479} + - component: {fileID: 1760323139199521700} + - component: {fileID: 4490950674500302730} + - component: {fileID: 6816865797365254487} + - component: {fileID: 7781273403086602364} m_Layer: 16 - m_Name: Label + m_Name: join Button m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!224 &6876281661055682962 -RectTransform: +--- !u!4 &1141409664942745479 +Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 2571361884171689698} + m_GameObject: {fileID: 1697630693985053451} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: 0, y: 0, z: -0.057142857} - m_LocalScale: {x: 2.5, y: 2.5, z: 2.8571427} + m_LocalPosition: {x: -0.329, y: 0.31899986, z: -0.042} + m_LocalScale: {x: 0.4, y: 0.4, z: 0.35} m_ConstrainProportionsScale: 0 - m_Children: [] - m_Father: {fileID: 7383649761766971007} + m_Children: + - {fileID: 7015233605674447413} + m_Father: {fileID: 2965947989537662772} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0.5, y: 0.5} - m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: -0.56, y: 0.8399991} - m_SizeDelta: {x: 1.104868, y: 1.4460607} - m_Pivot: {x: 0.5, y: 0.5} ---- !u!23 &5708211407015971906 +--- !u!33 &1760323139199521700 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1697630693985053451} + m_Mesh: {fileID: 4300000, guid: 70d5ab47bcc3aa0439e20bd40350c05a, type: 3} +--- !u!23 &4490950674500302730 MeshRenderer: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 2571361884171689698} + m_GameObject: {fileID: 1697630693985053451} m_Enabled: 1 m_CastShadows: 0 m_ReceiveShadows: 0 @@ -1375,7 +1368,7 @@ MeshRenderer: m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: - - {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + - {fileID: 2100000, guid: 40d29de2bdc11f04dbfa25059165916e, type: 2} m_StaticBatchInfo: firstSubMesh: 0 subMeshCount: 0 @@ -1397,143 +1390,151 @@ MeshRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_AdditionalVertexStreams: {fileID: 0} ---- !u!114 &3923005959821366055 -MonoBehaviour: +--- !u!65 &6816865797365254487 +BoxCollider: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 2571361884171689698} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 9541d86e2fd84c1d9990edf0852d74ab, type: 3} - m_Name: - m_EditorClassIdentifier: + m_GameObject: {fileID: 1697630693985053451} m_Material: {fileID: 0} - m_Color: {r: 1, g: 1, b: 1, a: 1} - m_RaycastTarget: 1 - m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} - m_Maskable: 1 - m_OnCullStateChanged: - m_PersistentCalls: - m_Calls: [] - m_text: Edit - m_isRightToLeft: 0 - m_fontAsset: {fileID: 11400000, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} - m_sharedMaterial: {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} - m_fontSharedMaterials: [] - m_fontMaterial: {fileID: 0} - m_fontMaterials: [] - m_fontColor32: + m_IncludeLayers: serializedVersion: 2 - rgba: 4294967295 - m_fontColor: {r: 1, g: 1, b: 1, a: 1} - m_enableVertexGradient: 0 - m_colorMode: 3 - m_fontColorGradient: - topLeft: {r: 1, g: 1, b: 1, a: 1} - topRight: {r: 1, g: 1, b: 1, a: 1} - bottomLeft: {r: 1, g: 1, b: 1, a: 1} - bottomRight: {r: 1, g: 1, b: 1, a: 1} - m_fontColorGradientPreset: {fileID: 0} - m_spriteAsset: {fileID: 0} - m_tintAllSprites: 0 - m_StyleSheet: {fileID: 0} - m_TextStyleHashCode: -1183493901 - m_overrideHtmlColors: 0 - m_faceColor: + m_Bits: 0 + m_ExcludeLayers: serializedVersion: 2 - rgba: 4294967295 - m_fontSize: 1.4 - m_fontSizeBase: 1.4 - m_fontWeight: 400 - m_enableAutoSizing: 0 - m_fontSizeMin: 18 - m_fontSizeMax: 72 - m_fontStyle: 0 - m_HorizontalAlignment: 2 - m_VerticalAlignment: 512 - m_textAlignment: 65535 - m_characterSpacing: 0 - m_wordSpacing: 0 - m_lineSpacing: -30 - m_lineSpacingMax: 0 - m_paragraphSpacing: 0 - m_charWidthMaxAdj: 0 - m_enableWordWrapping: 1 - m_wordWrappingRatios: 0.4 - m_overflowMode: 0 - m_linkedTextComponent: {fileID: 0} - parentLinkedComponent: {fileID: 0} - m_enableKerning: 1 - m_enableExtraPadding: 0 - checkPaddingRequired: 0 - m_isRichText: 1 - m_parseCtrlCharacters: 1 - m_isOrthographic: 0 - m_isCullingEnabled: 0 - m_horizontalMapping: 0 - m_verticalMapping: 0 - m_uvLineOffset: 0 - m_geometrySortingOrder: 0 - m_IsTextObjectScaleStatic: 0 - m_VertexBufferAutoSizeReduction: 0 - m_useMaxVisibleDescender: 1 - m_pageToDisplay: 1 - m_margin: {x: 0.48827845, y: 0.8604479, z: 0.054796636, w: 0.19184232} - m_isUsingLegacyAnimationComponent: 0 - m_isVolumetricText: 0 - _SortingLayer: 0 - _SortingLayerID: 0 - _SortingOrder: 0 - m_hasFontAssetChanged: 0 - m_renderer: {fileID: 5708211407015971906} - m_maskType: 0 ---- !u!1 &2994060372226562491 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 3068395288206314123} - - component: {fileID: 3015414681375869025} - - component: {fileID: 2890656081255549429} - - component: {fileID: 942730275356831299} - m_Layer: 16 - m_Name: Text - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!224 &3068395288206314123 -RectTransform: + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1.4, y: 1, z: 0.1} + m_Center: {x: -0.000000074505806, y: 0.000000022351742, z: 0} +--- !u!114 &7781273403086602364 +MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 2994060372226562491} - m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_GameObject: {fileID: 1697630693985053451} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: b78a8d7209bbdc546979b549a875d550, type: 3} + m_Name: + m_EditorClassIdentifier: + m_DescriptionType: 0 + m_DescriptionYOffset: 0 + m_DescriptionText: + m_LocalizedDescription: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_DescriptionTextExtra: + m_LocalizedDescriptionExtra: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_DescriptionActivateSpeed: 12 + m_DescriptionZScale: 1 + m_ButtonTexture: {fileID: 2800000, guid: cdc7d38d9e0b30443961d1cc27195586, type: 3} + m_AtlasTexture: 1 + m_ToggleButton: 0 + m_LongPressReleaseButton: 0 + m_ButtonHasPressedAudio: 1 + m_ZAdjustHover: -0.02 + m_ZAdjustClick: 0.02 + m_HoverScale: 1.1 + m_HoverBoxColliderGrow: 0.2 + m_AddOverlay: 0 + m_Command: 1004 + m_CommandParam: 1 + m_CommandParam2: -1 + m_RequiresPopup: 0 + m_CenterPopupOnButton: 0 + m_PopupOffset: {x: 0, y: 0, z: 0} + m_PopupText: + m_LocalizedPopup: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_ToggleOnDescription: + m_LocalizedToggleOnDescription: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_ToggleOnTexture: {fileID: 0} + m_AllowUnavailable: 1 + m_LinkedUIObject: {fileID: 0} + m_CommandIgnored: 0 + references: + version: 2 + RefIds: [] +--- !u!1 &2296016956045778647 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 47429484269554977} + - component: {fileID: 3374390498020739628} + - component: {fileID: 5985317331042495584} + - component: {fileID: 2841728730450484306} + m_Layer: 16 + m_Name: AllertBeginnerMode + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 0 +--- !u!224 &47429484269554977 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2296016956045778647} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: -0.05} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] - m_Father: {fileID: 438733611317437076} + m_Father: {fileID: 2965947989537662772} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: 0.104, y: 0.9} + m_AnchoredPosition: {x: 0.143, y: 1.355} m_SizeDelta: {x: 1.104868, y: 1.4460607} m_Pivot: {x: 0.5, y: 0.5} ---- !u!23 &3015414681375869025 +--- !u!23 &3374390498020739628 MeshRenderer: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 2994060372226562491} + m_GameObject: {fileID: 2296016956045778647} m_Enabled: 1 m_CastShadows: 0 m_ReceiveShadows: 0 @@ -1569,13 +1570,13 @@ MeshRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_AdditionalVertexStreams: {fileID: 0} ---- !u!114 &2890656081255549429 +--- !u!114 &5985317331042495584 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 2994060372226562491} + m_GameObject: {fileID: 2296016956045778647} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 9541d86e2fd84c1d9990edf0852d74ab, type: 3} @@ -1589,8 +1590,7 @@ MonoBehaviour: m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] - m_text: You have successfully joined the room! Share the room number with your - collaborators so they can join too. + m_text: To enter a room turn first set to beginner mode m_isRightToLeft: 0 m_fontAsset: {fileID: 11400000, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} m_sharedMaterial: {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} @@ -1599,8 +1599,8 @@ MonoBehaviour: m_fontMaterials: [] m_fontColor32: serializedVersion: 2 - rgba: 4294967295 - m_fontColor: {r: 1, g: 1, b: 1, a: 1} + rgba: 4278190335 + m_fontColor: {r: 1, g: 0, b: 0, a: 1} m_enableVertexGradient: 0 m_colorMode: 3 m_fontColorGradient: @@ -1617,15 +1617,15 @@ MonoBehaviour: m_faceColor: serializedVersion: 2 rgba: 4294967295 - m_fontSize: 1.4 - m_fontSizeBase: 1.4 + m_fontSize: 0.8 + m_fontSizeBase: 0.8 m_fontWeight: 400 m_enableAutoSizing: 0 m_fontSizeMin: 18 m_fontSizeMax: 72 m_fontStyle: 0 m_HorizontalAlignment: 2 - m_VerticalAlignment: 512 + m_VerticalAlignment: 1024 m_textAlignment: 65535 m_characterSpacing: 0 m_wordSpacing: 0 @@ -1653,22 +1653,22 @@ MonoBehaviour: m_VertexBufferAutoSizeReduction: 0 m_useMaxVisibleDescender: 1 m_pageToDisplay: 1 - m_margin: {x: -0.31141979, y: 0.6332202, z: -0.094255626, w: -0.027681828} + m_margin: {x: -0.31141979, y: 0.74942493, z: 0.083378464, w: 0.49275255} m_isUsingLegacyAnimationComponent: 0 m_isVolumetricText: 0 _SortingLayer: 0 _SortingLayerID: 0 _SortingOrder: 0 m_hasFontAssetChanged: 0 - m_renderer: {fileID: 3015414681375869025} + m_renderer: {fileID: 3374390498020739628} m_maskType: 0 ---- !u!114 &942730275356831299 +--- !u!114 &2841728730450484306 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 2994060372226562491} + m_GameObject: {fileID: 2296016956045778647} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 69beb381e244f92449b8c4cf954630e9, type: 3} @@ -1678,7 +1678,7 @@ MonoBehaviour: references: version: 2 RefIds: [] ---- !u!1 &3134540981707160973 +--- !u!1 &2571361884171689698 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -1686,9 +1686,9 @@ GameObject: m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - - component: {fileID: 7015233605674447413} - - component: {fileID: 6931700069523288738} - - component: {fileID: 8426405893577217330} + - component: {fileID: 6876281661055682962} + - component: {fileID: 5708211407015971906} + - component: {fileID: 3923005959821366055} m_Layer: 16 m_Name: Label m_TagString: Untagged @@ -1696,32 +1696,32 @@ GameObject: m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!224 &7015233605674447413 +--- !u!224 &6876281661055682962 RectTransform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 3134540981707160973} + m_GameObject: {fileID: 2571361884171689698} m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: -0.057142857} m_LocalScale: {x: 2.5, y: 2.5, z: 2.8571427} m_ConstrainProportionsScale: 0 m_Children: [] - m_Father: {fileID: 1141409664942745479} + m_Father: {fileID: 7383649761766971007} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} m_AnchoredPosition: {x: -0.56, y: 0.8399991} m_SizeDelta: {x: 1.104868, y: 1.4460607} m_Pivot: {x: 0.5, y: 0.5} ---- !u!23 &6931700069523288738 +--- !u!23 &5708211407015971906 MeshRenderer: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 3134540981707160973} + m_GameObject: {fileID: 2571361884171689698} m_Enabled: 1 m_CastShadows: 0 m_ReceiveShadows: 0 @@ -1757,13 +1757,13 @@ MeshRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_AdditionalVertexStreams: {fileID: 0} ---- !u!114 &8426405893577217330 +--- !u!114 &3923005959821366055 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 3134540981707160973} + m_GameObject: {fileID: 2571361884171689698} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 9541d86e2fd84c1d9990edf0852d74ab, type: 3} @@ -1777,7 +1777,7 @@ MonoBehaviour: m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] - m_text: Join + m_text: Edit m_isRightToLeft: 0 m_fontAsset: {fileID: 11400000, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} m_sharedMaterial: {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} @@ -1847,46 +1847,9 @@ MonoBehaviour: _SortingLayerID: 0 _SortingOrder: 0 m_hasFontAssetChanged: 0 - m_renderer: {fileID: 6931700069523288738} + m_renderer: {fileID: 5708211407015971906} m_maskType: 0 ---- !u!1 &3644177191085397888 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 2965947989537662772} - m_Layer: 16 - m_Name: LobbyElements - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!4 &2965947989537662772 -Transform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 3644177191085397888} - serializedVersion: 2 - m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: 0, y: -0.113, z: 0} - m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 - m_Children: - - {fileID: 6321725960906406704} - - {fileID: 47429484269554977} - - {fileID: 1141409664942745479} - - {fileID: 7383649761766971007} - - {fileID: 1616599776894361023} - - {fileID: 7584752043559634152} - m_Father: {fileID: 415082} - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!1 &4076170750158864688 +--- !u!1 &2994060372226562491 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -1894,42 +1857,43 @@ GameObject: m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - - component: {fileID: 7584752043559634152} - - component: {fileID: 8238648932876523510} - - component: {fileID: 1974621732855197313} + - component: {fileID: 3068395288206314123} + - component: {fileID: 3015414681375869025} + - component: {fileID: 2890656081255549429} + - component: {fileID: 942730275356831299} m_Layer: 16 - m_Name: RoomNumber + m_Name: Text m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!224 &7584752043559634152 +--- !u!224 &3068395288206314123 RectTransform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 4076170750158864688} + m_GameObject: {fileID: 2994060372226562491} m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: -0.05} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] - m_Father: {fileID: 2965947989537662772} + m_Father: {fileID: 438733611317437076} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: 0.586, y: 0.13399984} + m_AnchoredPosition: {x: 0.104, y: 0.9} m_SizeDelta: {x: 1.104868, y: 1.4460607} m_Pivot: {x: 0.5, y: 0.5} ---- !u!23 &8238648932876523510 +--- !u!23 &3015414681375869025 MeshRenderer: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 4076170750158864688} + m_GameObject: {fileID: 2994060372226562491} m_Enabled: 1 m_CastShadows: 0 m_ReceiveShadows: 0 @@ -1965,13 +1929,13 @@ MeshRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_AdditionalVertexStreams: {fileID: 0} ---- !u!114 &1974621732855197313 +--- !u!114 &2890656081255549429 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 4076170750158864688} + m_GameObject: {fileID: 2994060372226562491} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 9541d86e2fd84c1d9990edf0852d74ab, type: 3} @@ -1985,7 +1949,8 @@ MonoBehaviour: m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] - m_text: 202020 + m_text: You have successfully joined the room! Share the room number with your + collaborators so they can join too. m_isRightToLeft: 0 m_fontAsset: {fileID: 11400000, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} m_sharedMaterial: {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} @@ -2012,8 +1977,8 @@ MonoBehaviour: m_faceColor: serializedVersion: 2 rgba: 4294967295 - m_fontSize: 1.7 - m_fontSizeBase: 1.7 + m_fontSize: 1.4 + m_fontSizeBase: 1.4 m_fontWeight: 400 m_enableAutoSizing: 0 m_fontSizeMin: 18 @@ -2048,16 +2013,32 @@ MonoBehaviour: m_VertexBufferAutoSizeReduction: 0 m_useMaxVisibleDescender: 1 m_pageToDisplay: 1 - m_margin: {x: -0.31141979, y: -0.12023544, z: 0.49373662, w: 1.1764753} + m_margin: {x: -0.31141979, y: 0.6332202, z: -0.094255626, w: -0.027681828} m_isUsingLegacyAnimationComponent: 0 m_isVolumetricText: 0 _SortingLayer: 0 _SortingLayerID: 0 _SortingOrder: 0 m_hasFontAssetChanged: 0 - m_renderer: {fileID: 8238648932876523510} + m_renderer: {fileID: 3015414681375869025} m_maskType: 0 ---- !u!1 &4597522067460985994 +--- !u!114 &942730275356831299 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2994060372226562491} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 69beb381e244f92449b8c4cf954630e9, type: 3} + m_Name: + m_EditorClassIdentifier: + m_TrackedObjects: [] + references: + version: 2 + RefIds: [] +--- !u!1 &3134540981707160973 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -2065,43 +2046,42 @@ GameObject: m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - - component: {fileID: 6321725960906406704} - - component: {fileID: 2972459032532661851} - - component: {fileID: 5365545856024163458} - - component: {fileID: 2149637936205218271} + - component: {fileID: 7015233605674447413} + - component: {fileID: 6931700069523288738} + - component: {fileID: 8426405893577217330} m_Layer: 16 - m_Name: DoesRoomExistLabel + m_Name: Label m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!224 &6321725960906406704 +--- !u!224 &7015233605674447413 RectTransform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 4597522067460985994} + m_GameObject: {fileID: 3134540981707160973} m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: 0, y: 0, z: -0.05} - m_LocalScale: {x: 1, y: 1, z: 1} + m_LocalPosition: {x: 0, y: 0, z: -0.057142857} + m_LocalScale: {x: 2.5, y: 2.5, z: 2.8571427} m_ConstrainProportionsScale: 0 m_Children: [] - m_Father: {fileID: 2965947989537662772} + m_Father: {fileID: 1141409664942745479} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: 0.143, y: 1.138} + m_AnchoredPosition: {x: -0.56, y: 0.8399991} m_SizeDelta: {x: 1.104868, y: 1.4460607} m_Pivot: {x: 0.5, y: 0.5} ---- !u!23 &2972459032532661851 +--- !u!23 &6931700069523288738 MeshRenderer: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 4597522067460985994} + m_GameObject: {fileID: 3134540981707160973} m_Enabled: 1 m_CastShadows: 0 m_ReceiveShadows: 0 @@ -2137,13 +2117,13 @@ MeshRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_AdditionalVertexStreams: {fileID: 0} ---- !u!114 &5365545856024163458 +--- !u!114 &8426405893577217330 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 4597522067460985994} + m_GameObject: {fileID: 3134540981707160973} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 9541d86e2fd84c1d9990edf0852d74ab, type: 3} @@ -2157,7 +2137,7 @@ MonoBehaviour: m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] - m_text: This room does not exist yet. By pressing join, the room will be created. + m_text: Join m_isRightToLeft: 0 m_fontAsset: {fileID: 11400000, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} m_sharedMaterial: {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} @@ -2184,15 +2164,15 @@ MonoBehaviour: m_faceColor: serializedVersion: 2 rgba: 4294967295 - m_fontSize: 0.8 - m_fontSizeBase: 0.8 + m_fontSize: 1.4 + m_fontSizeBase: 1.4 m_fontWeight: 400 m_enableAutoSizing: 0 m_fontSizeMin: 18 m_fontSizeMax: 72 m_fontStyle: 0 m_HorizontalAlignment: 2 - m_VerticalAlignment: 1024 + m_VerticalAlignment: 512 m_textAlignment: 65535 m_characterSpacing: 0 m_wordSpacing: 0 @@ -2220,32 +2200,53 @@ MonoBehaviour: m_VertexBufferAutoSizeReduction: 0 m_useMaxVisibleDescender: 1 m_pageToDisplay: 1 - m_margin: {x: -0.31141979, y: 0.6332202, z: 0.083378464, w: 0.49275255} + m_margin: {x: 0.48827845, y: 0.8604479, z: 0.054796636, w: 0.19184232} m_isUsingLegacyAnimationComponent: 0 m_isVolumetricText: 0 _SortingLayer: 0 _SortingLayerID: 0 _SortingOrder: 0 m_hasFontAssetChanged: 0 - m_renderer: {fileID: 2972459032532661851} + m_renderer: {fileID: 6931700069523288738} m_maskType: 0 ---- !u!114 &2149637936205218271 -MonoBehaviour: +--- !u!1 &3644177191085397888 +GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 4597522067460985994} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 69beb381e244f92449b8c4cf954630e9, type: 3} - m_Name: - m_EditorClassIdentifier: - m_TrackedObjects: [] - references: - version: 2 - RefIds: [] ---- !u!1 &4649946342403727185 + serializedVersion: 6 + m_Component: + - component: {fileID: 2965947989537662772} + m_Layer: 16 + m_Name: LobbyElements + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 0 +--- !u!4 &2965947989537662772 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3644177191085397888} + serializedVersion: 2 + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: -0.113, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 6321725960906406704} + - {fileID: 47429484269554977} + - {fileID: 1141409664942745479} + - {fileID: 7383649761766971007} + - {fileID: 1616599776894361023} + - {fileID: 7584752043559634152} + m_Father: {fileID: 415082} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &4076170750158864688 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -2253,42 +2254,42 @@ GameObject: m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - - component: {fileID: 3577413846294365787} - - component: {fileID: 1001195632747271077} - - component: {fileID: 404572183770810615} + - component: {fileID: 7584752043559634152} + - component: {fileID: 8238648932876523510} + - component: {fileID: 1974621732855197313} m_Layer: 16 - m_Name: Label + m_Name: RoomNumber m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!224 &3577413846294365787 +--- !u!224 &7584752043559634152 RectTransform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 4649946342403727185} + m_GameObject: {fileID: 4076170750158864688} m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: -0.05} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] - m_Father: {fileID: 438733611317437076} + m_Father: {fileID: 2965947989537662772} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: 0, y: 0.36000013} + m_AnchoredPosition: {x: 0.586, y: 0.13399984} m_SizeDelta: {x: 1.104868, y: 1.4460607} m_Pivot: {x: 0.5, y: 0.5} ---- !u!23 &1001195632747271077 +--- !u!23 &8238648932876523510 MeshRenderer: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 4649946342403727185} + m_GameObject: {fileID: 4076170750158864688} m_Enabled: 1 m_CastShadows: 0 m_ReceiveShadows: 0 @@ -2324,13 +2325,13 @@ MeshRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_AdditionalVertexStreams: {fileID: 0} ---- !u!114 &404572183770810615 +--- !u!114 &1974621732855197313 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 4649946342403727185} + m_GameObject: {fileID: 4076170750158864688} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 9541d86e2fd84c1d9990edf0852d74ab, type: 3} @@ -2344,7 +2345,7 @@ MonoBehaviour: m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] - m_text: ' Room :' + m_text: 202020 m_isRightToLeft: 0 m_fontAsset: {fileID: 11400000, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} m_sharedMaterial: {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} @@ -2414,9 +2415,9 @@ MonoBehaviour: _SortingLayerID: 0 _SortingOrder: 0 m_hasFontAssetChanged: 0 - m_renderer: {fileID: 1001195632747271077} + m_renderer: {fileID: 8238648932876523510} m_maskType: 0 ---- !u!1 &5921902206488690915 +--- !u!1 &4597522067460985994 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -2424,49 +2425,43 @@ GameObject: m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - - component: {fileID: 7383649761766971007} - - component: {fileID: 2926107150197885019} - - component: {fileID: 8582249604316496249} - - component: {fileID: 3106886073238925572} - - component: {fileID: 1443552876586975744} + - component: {fileID: 6321725960906406704} + - component: {fileID: 2972459032532661851} + - component: {fileID: 5365545856024163458} + - component: {fileID: 2149637936205218271} m_Layer: 16 - m_Name: Edit Button + m_Name: DoesRoomExistLabel m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!4 &7383649761766971007 -Transform: +--- !u!224 &6321725960906406704 +RectTransform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 5921902206488690915} - serializedVersion: 2 + m_GameObject: {fileID: 4597522067460985994} m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: 0.337, y: 0.319, z: -0.042} - m_LocalScale: {x: 0.4, y: 0.4, z: 0.35} + m_LocalPosition: {x: 0, y: 0, z: -0.05} + m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 - m_Children: - - {fileID: 6876281661055682962} + m_Children: [] m_Father: {fileID: 2965947989537662772} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!33 &2926107150197885019 -MeshFilter: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 5921902206488690915} - m_Mesh: {fileID: 4300000, guid: 70d5ab47bcc3aa0439e20bd40350c05a, type: 3} ---- !u!23 &8582249604316496249 + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0.143, y: 1.138} + m_SizeDelta: {x: 1.104868, y: 1.4460607} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!23 &2972459032532661851 MeshRenderer: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 5921902206488690915} + m_GameObject: {fileID: 4597522067460985994} m_Enabled: 1 m_CastShadows: 0 m_ReceiveShadows: 0 @@ -2480,7 +2475,7 @@ MeshRenderer: m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: - - {fileID: 2100000, guid: 40d29de2bdc11f04dbfa25059165916e, type: 2} + - {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} m_StaticBatchInfo: firstSubMesh: 0 subMeshCount: 0 @@ -2502,142 +2497,115 @@ MeshRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_AdditionalVertexStreams: {fileID: 0} ---- !u!65 &3106886073238925572 -BoxCollider: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 5921902206488690915} - m_Material: {fileID: 0} - m_IncludeLayers: - serializedVersion: 2 - m_Bits: 0 - m_ExcludeLayers: - serializedVersion: 2 - m_Bits: 0 - m_LayerOverridePriority: 0 - m_IsTrigger: 0 - m_ProvidesContacts: 0 - m_Enabled: 1 - serializedVersion: 3 - m_Size: {x: 1.4, y: 1, z: 0.1} - m_Center: {x: -0.000000074505806, y: 0.000000022351742, z: 0} ---- !u!114 &1443552876586975744 +--- !u!114 &5365545856024163458 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 5921902206488690915} + m_GameObject: {fileID: 4597522067460985994} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: b78a8d7209bbdc546979b549a875d550, type: 3} + m_Script: {fileID: 11500000, guid: 9541d86e2fd84c1d9990edf0852d74ab, type: 3} m_Name: m_EditorClassIdentifier: - m_DescriptionType: 0 - m_DescriptionYOffset: 0 - m_DescriptionText: - m_LocalizedDescription: - m_TableReference: - m_TableCollectionName: - m_TableEntryReference: - m_KeyId: 0 - m_Key: - m_FallbackState: 0 - m_WaitForCompletion: 0 - m_LocalVariables: [] - m_DescriptionTextExtra: - m_LocalizedDescriptionExtra: - m_TableReference: - m_TableCollectionName: - m_TableEntryReference: - m_KeyId: 0 - m_Key: - m_FallbackState: 0 - m_WaitForCompletion: 0 - m_LocalVariables: [] - m_DescriptionActivateSpeed: 12 - m_DescriptionZScale: 1 - m_ButtonTexture: {fileID: 2800000, guid: cdc7d38d9e0b30443961d1cc27195586, type: 3} - m_AtlasTexture: 1 - m_ToggleButton: 0 - m_LongPressReleaseButton: 0 - m_ButtonHasPressedAudio: 1 - m_ZAdjustHover: -0.02 - m_ZAdjustClick: 0.02 - m_HoverScale: 1.1 - m_HoverBoxColliderGrow: 0.2 - m_AddOverlay: 0 - m_Command: 1005 - m_CommandParam: 1 - m_CommandParam2: -1 - m_RequiresPopup: 1 - m_CenterPopupOnButton: 0 - m_PopupOffset: {x: 0, y: 0, z: 0} - m_PopupText: - m_LocalizedPopup: - m_TableReference: - m_TableCollectionName: - m_TableEntryReference: - m_KeyId: 0 - m_Key: - m_FallbackState: 0 - m_WaitForCompletion: 0 - m_LocalVariables: [] - m_ToggleOnDescription: - m_LocalizedToggleOnDescription: - m_TableReference: - m_TableCollectionName: - m_TableEntryReference: - m_KeyId: 0 - m_Key: - m_FallbackState: 0 - m_WaitForCompletion: 0 - m_LocalVariables: [] - m_ToggleOnTexture: {fileID: 0} - m_AllowUnavailable: 0 - m_LinkedUIObject: {fileID: 0} - m_CommandIgnored: 0 - references: - version: 2 - RefIds: [] ---- !u!1 &6171746570159008301 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 438733611317437076} - m_Layer: 16 - m_Name: JoinedElements - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 0 ---- !u!4 &438733611317437076 -Transform: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_text: This room does not exist yet. By pressing join, the room will be created. + m_isRightToLeft: 0 + m_fontAsset: {fileID: 11400000, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + m_sharedMaterial: {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + m_fontSharedMaterials: [] + m_fontMaterial: {fileID: 0} + m_fontMaterials: [] + m_fontColor32: + serializedVersion: 2 + rgba: 4294967295 + m_fontColor: {r: 1, g: 1, b: 1, a: 1} + m_enableVertexGradient: 0 + m_colorMode: 3 + m_fontColorGradient: + topLeft: {r: 1, g: 1, b: 1, a: 1} + topRight: {r: 1, g: 1, b: 1, a: 1} + bottomLeft: {r: 1, g: 1, b: 1, a: 1} + bottomRight: {r: 1, g: 1, b: 1, a: 1} + m_fontColorGradientPreset: {fileID: 0} + m_spriteAsset: {fileID: 0} + m_tintAllSprites: 0 + m_StyleSheet: {fileID: 0} + m_TextStyleHashCode: -1183493901 + m_overrideHtmlColors: 0 + m_faceColor: + serializedVersion: 2 + rgba: 4294967295 + m_fontSize: 0.8 + m_fontSizeBase: 0.8 + m_fontWeight: 400 + m_enableAutoSizing: 0 + m_fontSizeMin: 18 + m_fontSizeMax: 72 + m_fontStyle: 0 + m_HorizontalAlignment: 2 + m_VerticalAlignment: 1024 + m_textAlignment: 65535 + m_characterSpacing: 0 + m_wordSpacing: 0 + m_lineSpacing: -30 + m_lineSpacingMax: 0 + m_paragraphSpacing: 0 + m_charWidthMaxAdj: 0 + m_enableWordWrapping: 1 + m_wordWrappingRatios: 0.4 + m_overflowMode: 0 + m_linkedTextComponent: {fileID: 0} + parentLinkedComponent: {fileID: 0} + m_enableKerning: 1 + m_enableExtraPadding: 0 + checkPaddingRequired: 0 + m_isRichText: 1 + m_parseCtrlCharacters: 1 + m_isOrthographic: 0 + m_isCullingEnabled: 0 + m_horizontalMapping: 0 + m_verticalMapping: 0 + m_uvLineOffset: 0 + m_geometrySortingOrder: 0 + m_IsTextObjectScaleStatic: 0 + m_VertexBufferAutoSizeReduction: 0 + m_useMaxVisibleDescender: 1 + m_pageToDisplay: 1 + m_margin: {x: -0.31141979, y: 0.6332202, z: 0.083378464, w: 0.49275255} + m_isUsingLegacyAnimationComponent: 0 + m_isVolumetricText: 0 + _SortingLayer: 0 + _SortingLayerID: 0 + _SortingOrder: 0 + m_hasFontAssetChanged: 0 + m_renderer: {fileID: 2972459032532661851} + m_maskType: 0 +--- !u!114 &2149637936205218271 +MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 6171746570159008301} - serializedVersion: 2 - 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: 3068395288206314123} - - {fileID: 3577413846294365787} - - {fileID: 4072971569122874247} - - {fileID: 977540286295220915} - m_Father: {fileID: 415082} - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!1 &6437768756802778087 + m_GameObject: {fileID: 4597522067460985994} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 69beb381e244f92449b8c4cf954630e9, type: 3} + m_Name: + m_EditorClassIdentifier: + m_TrackedObjects: [] + references: + version: 2 + RefIds: [] +--- !u!1 &4649946342403727185 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -2645,23 +2613,23 @@ GameObject: m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - - component: {fileID: 4072971569122874247} - - component: {fileID: 6918572163598264854} - - component: {fileID: 932498118536074667} + - component: {fileID: 3577413846294365787} + - component: {fileID: 1001195632747271077} + - component: {fileID: 404572183770810615} m_Layer: 16 - m_Name: RoomNumber + m_Name: Label m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!224 &4072971569122874247 +--- !u!224 &3577413846294365787 RectTransform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 6437768756802778087} + m_GameObject: {fileID: 4649946342403727185} m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: -0.05} m_LocalScale: {x: 1, y: 1, z: 1} @@ -2671,16 +2639,16 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: 0.586, y: 0.353} + m_AnchoredPosition: {x: 0, y: 0.36000013} m_SizeDelta: {x: 1.104868, y: 1.4460607} m_Pivot: {x: 0.5, y: 0.5} ---- !u!23 &6918572163598264854 +--- !u!23 &1001195632747271077 MeshRenderer: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 6437768756802778087} + m_GameObject: {fileID: 4649946342403727185} m_Enabled: 1 m_CastShadows: 0 m_ReceiveShadows: 0 @@ -2716,13 +2684,13 @@ MeshRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_AdditionalVertexStreams: {fileID: 0} ---- !u!114 &932498118536074667 +--- !u!114 &404572183770810615 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 6437768756802778087} + m_GameObject: {fileID: 4649946342403727185} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 9541d86e2fd84c1d9990edf0852d74ab, type: 3} @@ -2736,7 +2704,7 @@ MonoBehaviour: m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] - m_text: 202020 + m_text: ' Room :' m_isRightToLeft: 0 m_fontAsset: {fileID: 11400000, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} m_sharedMaterial: {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} @@ -2806,9 +2774,9 @@ MonoBehaviour: _SortingLayerID: 0 _SortingOrder: 0 m_hasFontAssetChanged: 0 - m_renderer: {fileID: 6918572163598264854} + m_renderer: {fileID: 1001195632747271077} m_maskType: 0 ---- !u!1 &7389879374748736206 +--- !u!1 &4683949721667896528 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -2816,49 +2784,42 @@ GameObject: m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - - component: {fileID: 977540286295220915} - - component: {fileID: 1862823799823796671} - - component: {fileID: 6441155883621675907} - - component: {fileID: 837553751123664783} - - component: {fileID: 4434619620066107605} + - component: {fileID: 4609499178869085959} + - component: {fileID: 8677733851088090031} + - component: {fileID: 9160963822650286723} m_Layer: 16 - m_Name: Disconnect Button + m_Name: Room m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!4 &977540286295220915 -Transform: +--- !u!224 &4609499178869085959 +RectTransform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 7389879374748736206} - serializedVersion: 2 + m_GameObject: {fileID: 4683949721667896528} m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: 0.01, y: -0.008, z: -0.042} - m_LocalScale: {x: 0.5, y: 0.4, z: 0.35} + m_LocalPosition: {x: 0, y: 0, z: -0.05} + m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 - m_Children: - - {fileID: 5290855516666066687} - m_Father: {fileID: 438733611317437076} + m_Children: [] + m_Father: {fileID: 8346410928580794432} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!33 &1862823799823796671 -MeshFilter: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 7389879374748736206} - m_Mesh: {fileID: 4300000, guid: 70d5ab47bcc3aa0439e20bd40350c05a, type: 3} ---- !u!23 &6441155883621675907 + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0.466, y: -0.26} + m_SizeDelta: {x: 1.104868, y: 1.4460607} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!23 &8677733851088090031 MeshRenderer: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 7389879374748736206} + m_GameObject: {fileID: 4683949721667896528} m_Enabled: 1 m_CastShadows: 0 m_ReceiveShadows: 0 @@ -2872,7 +2833,7 @@ MeshRenderer: m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: - - {fileID: 2100000, guid: 40d29de2bdc11f04dbfa25059165916e, type: 2} + - {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} m_StaticBatchInfo: firstSubMesh: 0 subMeshCount: 0 @@ -2894,77 +2855,1783 @@ MeshRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_AdditionalVertexStreams: {fileID: 0} ---- !u!65 &837553751123664783 -BoxCollider: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 7389879374748736206} - m_Material: {fileID: 0} - m_IncludeLayers: - serializedVersion: 2 - m_Bits: 0 - m_ExcludeLayers: - serializedVersion: 2 - m_Bits: 0 - m_LayerOverridePriority: 0 - m_IsTrigger: 0 - m_ProvidesContacts: 0 - m_Enabled: 1 - serializedVersion: 3 - m_Size: {x: 1.5, y: 1, z: 0.1} - m_Center: {x: -0.000000074505806, y: 0.000000022351742, z: 0} ---- !u!114 &4434619620066107605 +--- !u!114 &9160963822650286723 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 7389879374748736206} + m_GameObject: {fileID: 4683949721667896528} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: b78a8d7209bbdc546979b549a875d550, type: 3} + m_Script: {fileID: 11500000, guid: 9541d86e2fd84c1d9990edf0852d74ab, type: 3} m_Name: m_EditorClassIdentifier: - m_DescriptionType: 0 - m_DescriptionYOffset: 0 - m_DescriptionText: - m_LocalizedDescription: - m_TableReference: - m_TableCollectionName: - m_TableEntryReference: - m_KeyId: 0 - m_Key: - m_FallbackState: 0 - m_WaitForCompletion: 0 - m_LocalVariables: [] - m_DescriptionTextExtra: - m_LocalizedDescriptionExtra: - m_TableReference: - m_TableCollectionName: - m_TableEntryReference: - m_KeyId: 0 - m_Key: - m_FallbackState: 0 - m_WaitForCompletion: 0 - m_LocalVariables: [] - m_DescriptionActivateSpeed: 12 - m_DescriptionZScale: 1 - m_ButtonTexture: {fileID: 2800000, guid: cdc7d38d9e0b30443961d1cc27195586, type: 3} - m_AtlasTexture: 1 - m_ToggleButton: 0 - m_LongPressReleaseButton: 0 - m_ButtonHasPressedAudio: 1 - m_ZAdjustHover: -0.02 - m_ZAdjustClick: 0.02 - m_HoverScale: 1.1 - m_HoverBoxColliderGrow: 0.2 - m_AddOverlay: 0 - m_Command: 1006 - m_CommandParam: 1 - m_CommandParam2: -1 - m_RequiresPopup: 0 + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_text: RoomName + m_isRightToLeft: 0 + m_fontAsset: {fileID: 11400000, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + m_sharedMaterial: {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + m_fontSharedMaterials: [] + m_fontMaterial: {fileID: 0} + m_fontMaterials: [] + m_fontColor32: + serializedVersion: 2 + rgba: 4294967295 + m_fontColor: {r: 1, g: 1, b: 1, a: 1} + m_enableVertexGradient: 0 + m_colorMode: 3 + m_fontColorGradient: + topLeft: {r: 1, g: 1, b: 1, a: 1} + topRight: {r: 1, g: 1, b: 1, a: 1} + bottomLeft: {r: 1, g: 1, b: 1, a: 1} + bottomRight: {r: 1, g: 1, b: 1, a: 1} + m_fontColorGradientPreset: {fileID: 0} + m_spriteAsset: {fileID: 0} + m_tintAllSprites: 0 + m_StyleSheet: {fileID: 0} + m_TextStyleHashCode: -1183493901 + m_overrideHtmlColors: 0 + m_faceColor: + serializedVersion: 2 + rgba: 4294967295 + m_fontSize: 1.5 + m_fontSizeBase: 1.5 + m_fontWeight: 400 + m_enableAutoSizing: 0 + m_fontSizeMin: 18 + m_fontSizeMax: 72 + m_fontStyle: 0 + m_HorizontalAlignment: 1 + m_VerticalAlignment: 512 + m_textAlignment: 65535 + m_characterSpacing: 0 + m_wordSpacing: 0 + m_lineSpacing: -30 + m_lineSpacingMax: 0 + m_paragraphSpacing: 0 + m_charWidthMaxAdj: 0 + m_enableWordWrapping: 1 + m_wordWrappingRatios: 0.4 + m_overflowMode: 0 + m_linkedTextComponent: {fileID: 0} + parentLinkedComponent: {fileID: 0} + m_enableKerning: 1 + m_enableExtraPadding: 0 + checkPaddingRequired: 0 + m_isRichText: 1 + m_parseCtrlCharacters: 1 + m_isOrthographic: 0 + m_isCullingEnabled: 0 + m_horizontalMapping: 0 + m_verticalMapping: 0 + m_uvLineOffset: 0 + m_geometrySortingOrder: 0 + m_IsTextObjectScaleStatic: 0 + m_VertexBufferAutoSizeReduction: 0 + m_useMaxVisibleDescender: 1 + m_pageToDisplay: 1 + m_margin: {x: -0.31141979, y: -0.12023544, z: 0.19494265, w: 1.1764753} + m_isUsingLegacyAnimationComponent: 0 + m_isVolumetricText: 0 + _SortingLayer: 0 + _SortingLayerID: 0 + _SortingOrder: 0 + m_hasFontAssetChanged: 0 + m_renderer: {fileID: 8677733851088090031} + m_maskType: 0 +--- !u!1 &5874556256608128191 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2677652558882906679} + - component: {fileID: 2426349157769062346} + - component: {fileID: 4888819335275065630} + m_Layer: 16 + m_Name: NickName + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &2677652558882906679 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5874556256608128191} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: -0.05} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 8346410928580794432} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0.466, y: -0.5540001} + m_SizeDelta: {x: 1.104868, y: 1.4460607} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!23 &2426349157769062346 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5874556256608128191} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, 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: 0 + 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!114 &4888819335275065630 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5874556256608128191} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9541d86e2fd84c1d9990edf0852d74ab, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_text: NickName + m_isRightToLeft: 0 + m_fontAsset: {fileID: 11400000, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + m_sharedMaterial: {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + m_fontSharedMaterials: [] + m_fontMaterial: {fileID: 0} + m_fontMaterials: [] + m_fontColor32: + serializedVersion: 2 + rgba: 4294967295 + m_fontColor: {r: 1, g: 1, b: 1, a: 1} + m_enableVertexGradient: 0 + m_colorMode: 3 + m_fontColorGradient: + topLeft: {r: 1, g: 1, b: 1, a: 1} + topRight: {r: 1, g: 1, b: 1, a: 1} + bottomLeft: {r: 1, g: 1, b: 1, a: 1} + bottomRight: {r: 1, g: 1, b: 1, a: 1} + m_fontColorGradientPreset: {fileID: 0} + m_spriteAsset: {fileID: 0} + m_tintAllSprites: 0 + m_StyleSheet: {fileID: 0} + m_TextStyleHashCode: -1183493901 + m_overrideHtmlColors: 0 + m_faceColor: + serializedVersion: 2 + rgba: 4294967295 + m_fontSize: 1.5 + m_fontSizeBase: 1.5 + m_fontWeight: 400 + m_enableAutoSizing: 0 + m_fontSizeMin: 18 + m_fontSizeMax: 72 + m_fontStyle: 0 + m_HorizontalAlignment: 1 + m_VerticalAlignment: 512 + m_textAlignment: 65535 + m_characterSpacing: 0 + m_wordSpacing: 0 + m_lineSpacing: -30 + m_lineSpacingMax: 0 + m_paragraphSpacing: 0 + m_charWidthMaxAdj: 0 + m_enableWordWrapping: 1 + m_wordWrappingRatios: 0.4 + m_overflowMode: 0 + m_linkedTextComponent: {fileID: 0} + parentLinkedComponent: {fileID: 0} + m_enableKerning: 1 + m_enableExtraPadding: 0 + checkPaddingRequired: 0 + m_isRichText: 1 + m_parseCtrlCharacters: 1 + m_isOrthographic: 0 + m_isCullingEnabled: 0 + m_horizontalMapping: 0 + m_verticalMapping: 0 + m_uvLineOffset: 0 + m_geometrySortingOrder: 0 + m_IsTextObjectScaleStatic: 0 + m_VertexBufferAutoSizeReduction: 0 + m_useMaxVisibleDescender: 1 + m_pageToDisplay: 1 + m_margin: {x: -0.31141979, y: -0.12023544, z: 0.19494265, w: 1.1764753} + m_isUsingLegacyAnimationComponent: 0 + m_isVolumetricText: 0 + _SortingLayer: 0 + _SortingLayerID: 0 + _SortingOrder: 0 + m_hasFontAssetChanged: 0 + m_renderer: {fileID: 2426349157769062346} + m_maskType: 0 +--- !u!1 &5899900900417153719 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3858004711376697373} + - component: {fileID: 9157174449584494178} + - component: {fileID: 4845429684781977610} + - component: {fileID: 1175572973535382090} + - component: {fileID: 2867525648481663953} + m_Layer: 16 + m_Name: DisconnectButton + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &3858004711376697373 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5899900900417153719} + serializedVersion: 2 + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0.62, y: -0.323, z: -0.042} + m_LocalScale: {x: 0.33, y: 0.33, z: 0.33} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 8346410928580794432} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &9157174449584494178 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5899900900417153719} + m_Mesh: {fileID: 4300000, guid: 5501f437160666942ae970f3648fbeb8, type: 3} +--- !u!23 &4845429684781977610 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5899900900417153719} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 40d29de2bdc11f04dbfa25059165916e, 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: 0 + 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!65 &1175572973535382090 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5899900900417153719} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1.1, y: 1.1, z: 0.1} + m_Center: {x: -0.000000074505806, y: 0.000000022351742, z: 0} +--- !u!114 &2867525648481663953 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5899900900417153719} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: b78a8d7209bbdc546979b549a875d550, type: 3} + m_Name: + m_EditorClassIdentifier: + m_DescriptionType: 0 + m_DescriptionYOffset: 0 + m_DescriptionText: + m_LocalizedDescription: + m_TableReference: + m_TableCollectionName: GUID:c84355079ab3f3e4f8f3812258805f86 + m_TableEntryReference: + m_KeyId: 294637000357126144 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_DescriptionTextExtra: + m_LocalizedDescriptionExtra: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_DescriptionActivateSpeed: 12 + m_DescriptionZScale: 1 + m_ButtonTexture: {fileID: 2800000, guid: 56e5ebeba70465c44bf75491d40b6f3d, type: 3} + m_AtlasTexture: 1 + m_ToggleButton: 0 + m_LongPressReleaseButton: 0 + m_ButtonHasPressedAudio: 1 + m_ZAdjustHover: -0.02 + m_ZAdjustClick: 0.02 + m_HoverScale: 1.1 + m_HoverBoxColliderGrow: 0.2 + m_AddOverlay: 0 + m_Command: 1008 + m_CommandParam: 1 + m_CommandParam2: -1 + m_RequiresPopup: 0 + m_CenterPopupOnButton: 0 + m_PopupOffset: {x: 0, y: 0, z: 0} + m_PopupText: + m_LocalizedPopup: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_ToggleOnDescription: + m_LocalizedToggleOnDescription: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_ToggleOnTexture: {fileID: 0} + m_AllowUnavailable: 1 + m_LinkedUIObject: {fileID: 0} + m_CommandIgnored: 0 + references: + version: 2 + RefIds: [] +--- !u!1 &5921902206488690915 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 7383649761766971007} + - component: {fileID: 2926107150197885019} + - component: {fileID: 8582249604316496249} + - component: {fileID: 3106886073238925572} + - component: {fileID: 1443552876586975744} + m_Layer: 16 + m_Name: Edit Button + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &7383649761766971007 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5921902206488690915} + serializedVersion: 2 + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0.337, y: 0.319, z: -0.042} + m_LocalScale: {x: 0.4, y: 0.4, z: 0.35} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 6876281661055682962} + m_Father: {fileID: 2965947989537662772} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &2926107150197885019 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5921902206488690915} + m_Mesh: {fileID: 4300000, guid: 70d5ab47bcc3aa0439e20bd40350c05a, type: 3} +--- !u!23 &8582249604316496249 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5921902206488690915} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 40d29de2bdc11f04dbfa25059165916e, 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: 0 + 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!65 &3106886073238925572 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5921902206488690915} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1.4, y: 1, z: 0.1} + m_Center: {x: -0.000000074505806, y: 0.000000022351742, z: 0} +--- !u!114 &1443552876586975744 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5921902206488690915} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: b78a8d7209bbdc546979b549a875d550, type: 3} + m_Name: + m_EditorClassIdentifier: + m_DescriptionType: 0 + m_DescriptionYOffset: 0 + m_DescriptionText: + m_LocalizedDescription: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_DescriptionTextExtra: + m_LocalizedDescriptionExtra: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_DescriptionActivateSpeed: 12 + m_DescriptionZScale: 1 + m_ButtonTexture: {fileID: 2800000, guid: cdc7d38d9e0b30443961d1cc27195586, type: 3} + m_AtlasTexture: 1 + m_ToggleButton: 0 + m_LongPressReleaseButton: 0 + m_ButtonHasPressedAudio: 1 + m_ZAdjustHover: -0.02 + m_ZAdjustClick: 0.02 + m_HoverScale: 1.1 + m_HoverBoxColliderGrow: 0.2 + m_AddOverlay: 0 + m_Command: 1005 + m_CommandParam: 1 + m_CommandParam2: -1 + m_RequiresPopup: 1 + m_CenterPopupOnButton: 0 + m_PopupOffset: {x: 0, y: 0, z: 0} + m_PopupText: + m_LocalizedPopup: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_ToggleOnDescription: + m_LocalizedToggleOnDescription: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_ToggleOnTexture: {fileID: 0} + m_AllowUnavailable: 0 + m_LinkedUIObject: {fileID: 0} + m_CommandIgnored: 0 + references: + version: 2 + RefIds: [] +--- !u!1 &6083642199730771223 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 8346410928580794432} + m_Layer: 16 + m_Name: Elements + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &8346410928580794432 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6083642199730771223} + serializedVersion: 2 + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0.336, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 682210897750110130} + - {fileID: 332726704664592371} + - {fileID: 3830748526666750540} + - {fileID: 3858004711376697373} + - {fileID: 8148098001997981085} + - {fileID: 2683548132759635989} + - {fileID: 4609499178869085959} + - {fileID: 2677652558882906679} + - {fileID: 8994233172856634412} + m_Father: {fileID: 415082} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &6171746570159008301 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 438733611317437076} + m_Layer: 16 + m_Name: JoinedElements + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 0 +--- !u!4 &438733611317437076 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6171746570159008301} + serializedVersion: 2 + 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: 3068395288206314123} + - {fileID: 3577413846294365787} + - {fileID: 4072971569122874247} + - {fileID: 977540286295220915} + m_Father: {fileID: 415082} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &6437768756802778087 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4072971569122874247} + - component: {fileID: 6918572163598264854} + - component: {fileID: 932498118536074667} + m_Layer: 16 + m_Name: RoomNumber + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &4072971569122874247 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6437768756802778087} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: -0.05} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 438733611317437076} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0.586, y: 0.353} + m_SizeDelta: {x: 1.104868, y: 1.4460607} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!23 &6918572163598264854 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6437768756802778087} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, 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: 0 + 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!114 &932498118536074667 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6437768756802778087} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9541d86e2fd84c1d9990edf0852d74ab, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_text: 202020 + m_isRightToLeft: 0 + m_fontAsset: {fileID: 11400000, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + m_sharedMaterial: {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + m_fontSharedMaterials: [] + m_fontMaterial: {fileID: 0} + m_fontMaterials: [] + m_fontColor32: + serializedVersion: 2 + rgba: 4294967295 + m_fontColor: {r: 1, g: 1, b: 1, a: 1} + m_enableVertexGradient: 0 + m_colorMode: 3 + m_fontColorGradient: + topLeft: {r: 1, g: 1, b: 1, a: 1} + topRight: {r: 1, g: 1, b: 1, a: 1} + bottomLeft: {r: 1, g: 1, b: 1, a: 1} + bottomRight: {r: 1, g: 1, b: 1, a: 1} + m_fontColorGradientPreset: {fileID: 0} + m_spriteAsset: {fileID: 0} + m_tintAllSprites: 0 + m_StyleSheet: {fileID: 0} + m_TextStyleHashCode: -1183493901 + m_overrideHtmlColors: 0 + m_faceColor: + serializedVersion: 2 + rgba: 4294967295 + m_fontSize: 1.7 + m_fontSizeBase: 1.7 + m_fontWeight: 400 + m_enableAutoSizing: 0 + m_fontSizeMin: 18 + m_fontSizeMax: 72 + m_fontStyle: 0 + m_HorizontalAlignment: 2 + m_VerticalAlignment: 512 + m_textAlignment: 65535 + m_characterSpacing: 0 + m_wordSpacing: 0 + m_lineSpacing: -30 + m_lineSpacingMax: 0 + m_paragraphSpacing: 0 + m_charWidthMaxAdj: 0 + m_enableWordWrapping: 1 + m_wordWrappingRatios: 0.4 + m_overflowMode: 0 + m_linkedTextComponent: {fileID: 0} + parentLinkedComponent: {fileID: 0} + m_enableKerning: 1 + m_enableExtraPadding: 0 + checkPaddingRequired: 0 + m_isRichText: 1 + m_parseCtrlCharacters: 1 + m_isOrthographic: 0 + m_isCullingEnabled: 0 + m_horizontalMapping: 0 + m_verticalMapping: 0 + m_uvLineOffset: 0 + m_geometrySortingOrder: 0 + m_IsTextObjectScaleStatic: 0 + m_VertexBufferAutoSizeReduction: 0 + m_useMaxVisibleDescender: 1 + m_pageToDisplay: 1 + m_margin: {x: -0.31141979, y: -0.12023544, z: 0.49373662, w: 1.1764753} + m_isUsingLegacyAnimationComponent: 0 + m_isVolumetricText: 0 + _SortingLayer: 0 + _SortingLayerID: 0 + _SortingOrder: 0 + m_hasFontAssetChanged: 0 + m_renderer: {fileID: 6918572163598264854} + m_maskType: 0 +--- !u!1 &6530187638887373145 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3830748526666750540} + - component: {fileID: 500043193495277236} + - component: {fileID: 7032505750277561405} + - component: {fileID: 6109381147238388491} + - component: {fileID: 8427148437490211341} + m_Layer: 16 + m_Name: LeaveRoomButton + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &3830748526666750540 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6530187638887373145} + serializedVersion: 2 + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0.208, y: -0.323, z: -0.042} + m_LocalScale: {x: 0.33, y: 0.33, z: 0.33} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 8346410928580794432} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &500043193495277236 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6530187638887373145} + m_Mesh: {fileID: 4300000, guid: 5501f437160666942ae970f3648fbeb8, type: 3} +--- !u!23 &7032505750277561405 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6530187638887373145} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 40d29de2bdc11f04dbfa25059165916e, 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: 0 + 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!65 &6109381147238388491 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6530187638887373145} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1.1, y: 1.1, z: 0.1} + m_Center: {x: -0.000000074505806, y: 0.000000022351742, z: 0} +--- !u!114 &8427148437490211341 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6530187638887373145} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: b78a8d7209bbdc546979b549a875d550, type: 3} + m_Name: + m_EditorClassIdentifier: + m_DescriptionType: 0 + m_DescriptionYOffset: 0 + m_DescriptionText: + m_LocalizedDescription: + m_TableReference: + m_TableCollectionName: GUID:c84355079ab3f3e4f8f3812258805f86 + m_TableEntryReference: + m_KeyId: 294636908816441344 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_DescriptionTextExtra: + m_LocalizedDescriptionExtra: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_DescriptionActivateSpeed: 12 + m_DescriptionZScale: 1 + m_ButtonTexture: {fileID: 2800000, guid: f856211cd511f8445b96396ddd0c59bf, type: 3} + m_AtlasTexture: 1 + m_ToggleButton: 0 + m_LongPressReleaseButton: 0 + m_ButtonHasPressedAudio: 1 + m_ZAdjustHover: -0.02 + m_ZAdjustClick: 0.02 + m_HoverScale: 1.1 + m_HoverBoxColliderGrow: 0.2 + m_AddOverlay: 0 + m_Command: 1006 + m_CommandParam: 1 + m_CommandParam2: -1 + m_RequiresPopup: 0 + m_CenterPopupOnButton: 0 + m_PopupOffset: {x: 0, y: 0, z: 0} + m_PopupText: + m_LocalizedPopup: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_ToggleOnDescription: + m_LocalizedToggleOnDescription: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_ToggleOnTexture: {fileID: 0} + m_AllowUnavailable: 1 + m_LinkedUIObject: {fileID: 0} + m_CommandIgnored: 0 + references: + version: 2 + RefIds: [] +--- !u!1 &7101128147202961155 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 332726704664592371} + - component: {fileID: 5687544308279590595} + - component: {fileID: 8038925281922313174} + - component: {fileID: 1407599492849254995} + - component: {fileID: 9055833328840286294} + m_Layer: 16 + m_Name: JoinRoomButton + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &332726704664592371 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7101128147202961155} + serializedVersion: 2 + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -0.206, y: -0.323, z: -0.042} + m_LocalScale: {x: 0.33, y: 0.33, z: 0.33} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 8346410928580794432} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &5687544308279590595 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7101128147202961155} + m_Mesh: {fileID: 4300000, guid: 5501f437160666942ae970f3648fbeb8, type: 3} +--- !u!23 &8038925281922313174 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7101128147202961155} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 40d29de2bdc11f04dbfa25059165916e, 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: 0 + 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!65 &1407599492849254995 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7101128147202961155} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1.1, y: 1.1, z: 0.1} + m_Center: {x: -0.000000074505806, y: 0.000000022351742, z: 0} +--- !u!114 &9055833328840286294 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7101128147202961155} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: b78a8d7209bbdc546979b549a875d550, type: 3} + m_Name: + m_EditorClassIdentifier: + m_DescriptionType: 0 + m_DescriptionYOffset: 0 + m_DescriptionText: + m_LocalizedDescription: + m_TableReference: + m_TableCollectionName: GUID:c84355079ab3f3e4f8f3812258805f86 + m_TableEntryReference: + m_KeyId: 294636769901092864 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_DescriptionTextExtra: + m_LocalizedDescriptionExtra: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_DescriptionActivateSpeed: 12 + m_DescriptionZScale: 1 + m_ButtonTexture: {fileID: 2800000, guid: 1ac81545f10c4fb4d9ada583e88b2f43, type: 3} + m_AtlasTexture: 1 + m_ToggleButton: 0 + m_LongPressReleaseButton: 0 + m_ButtonHasPressedAudio: 1 + m_ZAdjustHover: -0.02 + m_ZAdjustClick: 0.02 + m_HoverScale: 1.1 + m_HoverBoxColliderGrow: 0.2 + m_AddOverlay: 0 + m_Command: 1004 + m_CommandParam: 1 + m_CommandParam2: -1 + m_RequiresPopup: 0 + m_CenterPopupOnButton: 0 + m_PopupOffset: {x: 0, y: 0, z: 0} + m_PopupText: + m_LocalizedPopup: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_ToggleOnDescription: + m_LocalizedToggleOnDescription: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_ToggleOnTexture: {fileID: 0} + m_AllowUnavailable: 1 + m_LinkedUIObject: {fileID: 0} + m_CommandIgnored: 0 + references: + version: 2 + RefIds: [] +--- !u!1 &7355810358076377629 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2683548132759635989} + - component: {fileID: 5233991143414185181} + - component: {fileID: 454729548256029537} + - component: {fileID: 3902811276401445089} + - component: {fileID: 8163065677955154448} + m_Layer: 16 + m_Name: NickNameEditButton + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &2683548132759635989 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7355810358076377629} + serializedVersion: 2 + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -0.619, y: 0.09, z: -0.042} + m_LocalScale: {x: 0.15, y: 0.15, z: 0.15} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 8346410928580794432} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &5233991143414185181 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7355810358076377629} + m_Mesh: {fileID: 4300000, guid: 5501f437160666942ae970f3648fbeb8, type: 3} +--- !u!23 &454729548256029537 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7355810358076377629} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 40d29de2bdc11f04dbfa25059165916e, 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: 0 + 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!65 &3902811276401445089 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7355810358076377629} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1.1, y: 1.1, z: 0.1} + m_Center: {x: -0.000000074505806, y: 0.000000022351742, z: 0} +--- !u!114 &8163065677955154448 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7355810358076377629} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: b78a8d7209bbdc546979b549a875d550, type: 3} + m_Name: + m_EditorClassIdentifier: + m_DescriptionType: 0 + m_DescriptionYOffset: 0 + m_DescriptionText: + m_LocalizedDescription: + m_TableReference: + m_TableCollectionName: GUID:c84355079ab3f3e4f8f3812258805f86 + m_TableEntryReference: + m_KeyId: 294637384320491520 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_DescriptionTextExtra: + m_LocalizedDescriptionExtra: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_DescriptionActivateSpeed: 12 + m_DescriptionZScale: 1 + m_ButtonTexture: {fileID: 2800000, guid: 0141ac6bf9f578f4e95706cdd2117914, type: 3} + m_AtlasTexture: 1 + m_ToggleButton: 0 + m_LongPressReleaseButton: 0 + m_ButtonHasPressedAudio: 1 + m_ZAdjustHover: -0.02 + m_ZAdjustClick: 0.02 + m_HoverScale: 1.1 + m_HoverBoxColliderGrow: 0.2 + m_AddOverlay: 0 + m_Command: 1009 + m_CommandParam: 1 + m_CommandParam2: -1 + m_RequiresPopup: 1 + m_CenterPopupOnButton: 0 + m_PopupOffset: {x: 0, y: 0, z: 0} + m_PopupText: + m_LocalizedPopup: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_ToggleOnDescription: + m_LocalizedToggleOnDescription: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_ToggleOnTexture: {fileID: 0} + m_AllowUnavailable: 0 + m_LinkedUIObject: {fileID: 0} + m_CommandIgnored: 0 + references: + version: 2 + RefIds: [] +--- !u!1 &7389879374748736206 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 977540286295220915} + - component: {fileID: 1862823799823796671} + - component: {fileID: 6441155883621675907} + - component: {fileID: 837553751123664783} + - component: {fileID: 4434619620066107605} + m_Layer: 16 + m_Name: Disconnect Button + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &977540286295220915 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7389879374748736206} + serializedVersion: 2 + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0.01, y: -0.008, z: -0.042} + m_LocalScale: {x: 0.5, y: 0.4, z: 0.35} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 5290855516666066687} + m_Father: {fileID: 438733611317437076} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &1862823799823796671 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7389879374748736206} + m_Mesh: {fileID: 4300000, guid: 70d5ab47bcc3aa0439e20bd40350c05a, type: 3} +--- !u!23 &6441155883621675907 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7389879374748736206} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 40d29de2bdc11f04dbfa25059165916e, 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: 0 + 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!65 &837553751123664783 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7389879374748736206} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1.5, y: 1, z: 0.1} + m_Center: {x: -0.000000074505806, y: 0.000000022351742, z: 0} +--- !u!114 &4434619620066107605 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7389879374748736206} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: b78a8d7209bbdc546979b549a875d550, type: 3} + m_Name: + m_EditorClassIdentifier: + m_DescriptionType: 0 + m_DescriptionYOffset: 0 + m_DescriptionText: + m_LocalizedDescription: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_DescriptionTextExtra: + m_LocalizedDescriptionExtra: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_DescriptionActivateSpeed: 12 + m_DescriptionZScale: 1 + m_ButtonTexture: {fileID: 2800000, guid: cdc7d38d9e0b30443961d1cc27195586, type: 3} + m_AtlasTexture: 1 + m_ToggleButton: 0 + m_LongPressReleaseButton: 0 + m_ButtonHasPressedAudio: 1 + m_ZAdjustHover: -0.02 + m_ZAdjustClick: 0.02 + m_HoverScale: 1.1 + m_HoverBoxColliderGrow: 0.2 + m_AddOverlay: 0 + m_Command: 1006 + m_CommandParam: 1 + m_CommandParam2: -1 + m_RequiresPopup: 0 + m_CenterPopupOnButton: 0 + m_PopupOffset: {x: 0, y: 0, z: 0} + m_PopupText: + m_LocalizedPopup: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_ToggleOnDescription: + m_LocalizedToggleOnDescription: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_ToggleOnTexture: {fileID: 0} + m_AllowUnavailable: 0 + m_LinkedUIObject: {fileID: 0} + m_CommandIgnored: 0 + references: + version: 2 + RefIds: [] +--- !u!1 &7521510091458286424 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 8148098001997981085} + - component: {fileID: 4216493873191797517} + - component: {fileID: 1879947414287806692} + - component: {fileID: 2655394652004014560} + - component: {fileID: 6215490754924617493} + m_Layer: 16 + m_Name: RoomNameEditButton + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &8148098001997981085 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7521510091458286424} + serializedVersion: 2 + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -0.619, y: 0.384, z: -0.042} + m_LocalScale: {x: 0.15, y: 0.15, z: 0.15} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 8346410928580794432} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &4216493873191797517 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7521510091458286424} + m_Mesh: {fileID: 4300000, guid: 5501f437160666942ae970f3648fbeb8, type: 3} +--- !u!23 &1879947414287806692 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7521510091458286424} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 40d29de2bdc11f04dbfa25059165916e, 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: 0 + 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!65 &2655394652004014560 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7521510091458286424} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1.1, y: 1.1, z: 0.1} + m_Center: {x: -0.000000074505806, y: 0.000000022351742, z: 0} +--- !u!114 &6215490754924617493 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7521510091458286424} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: b78a8d7209bbdc546979b549a875d550, type: 3} + m_Name: + m_EditorClassIdentifier: + m_DescriptionType: 0 + m_DescriptionYOffset: 0 + m_DescriptionText: + m_LocalizedDescription: + m_TableReference: + m_TableCollectionName: GUID:c84355079ab3f3e4f8f3812258805f86 + m_TableEntryReference: + m_KeyId: 294637073212186624 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_DescriptionTextExtra: + m_LocalizedDescriptionExtra: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_DescriptionActivateSpeed: 12 + m_DescriptionZScale: 1 + m_ButtonTexture: {fileID: 2800000, guid: 0141ac6bf9f578f4e95706cdd2117914, type: 3} + m_AtlasTexture: 1 + m_ToggleButton: 0 + m_LongPressReleaseButton: 0 + m_ButtonHasPressedAudio: 1 + m_ZAdjustHover: -0.02 + m_ZAdjustClick: 0.02 + m_HoverScale: 1.1 + m_HoverBoxColliderGrow: 0.2 + m_AddOverlay: 0 + m_Command: 1005 + m_CommandParam: 1 + m_CommandParam2: -1 + m_RequiresPopup: 1 m_CenterPopupOnButton: 0 m_PopupOffset: {x: 0, y: 0, z: 0} m_PopupText: @@ -3434,12 +5101,12 @@ PrefabInstance: - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, type: 3} propertyPath: m_LocalPosition.x - value: 0.76 + value: 0.823 objectReference: {fileID: 0} - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, type: 3} propertyPath: m_LocalPosition.y - value: 1.054 + value: 1.249 objectReference: {fileID: 0} - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, type: 3} diff --git a/Assets/Scripts/GUI/MultiplayerPanel.cs b/Assets/Scripts/GUI/MultiplayerPanel.cs index cf8134465b..72b8c63971 100644 --- a/Assets/Scripts/GUI/MultiplayerPanel.cs +++ b/Assets/Scripts/GUI/MultiplayerPanel.cs @@ -20,11 +20,11 @@ namespace TiltBrush { public class MultiplayerPanel : BasePanel { - [SerializeField] private TextMeshPro m_RoomNumberTextLobby; - [SerializeField] private TextMeshPro m_RoomNumberTextRoomSettings; - [SerializeField] private TextMeshPro m_DoesRoomNumberExist; - [SerializeField] private TextMeshPro m_AlertUserInBeginnerMode; + [SerializeField] private TextMeshPro m_State; + [SerializeField] private TextMeshPro m_RoomNumber; + [SerializeField] private TextMeshPro m_Nickname; + [SerializeField] private TextMeshPro m_Alerts; public string RoomName { @@ -32,8 +32,28 @@ public string RoomName set { data.roomName = value; - UpdateRoomNumberDisplay(); - UpdateRoomExistenceMessage(); + UpdateDisplay(); + } + } + + public string NickName + { + get + { + + if (MultiplayerManager.m_Instance != null) return MultiplayerManager.m_Instance.UserInfo.Nickname; + return ""; + } + set + { + ConnectionUserInfo ui = new ConnectionUserInfo + { + Nickname = value, + UserId = MultiplayerManager.m_Instance.UserInfo.UserId, + Role = MultiplayerManager.m_Instance.UserInfo.Role + }; + MultiplayerManager.m_Instance.UserInfo = ui; + UpdateDisplay(); } } @@ -49,26 +69,19 @@ public void Awake() voiceDisabled = false }; - } - - protected override void OnEnablePanel() - { - base.OnEnablePanel(); + if (MultiplayerManager.m_Instance != null) MultiplayerManager.m_Instance.StateUpdated += OnStateUpdated; - if (m_CurrentMode == Mode.Lobby) - { - UserInBeginnerMode(); - } + UpdateDisplay(); } private void UserInBeginnerMode() { - if (m_AlertUserInBeginnerMode) + if (m_Alerts) { PanelManager panelManager = PanelManager.m_Instance; bool IsAdavancedModeActive = panelManager.AdvancedModeActive(); Debug.Log(IsAdavancedModeActive); - m_AlertUserInBeginnerMode.gameObject.SetActive(IsAdavancedModeActive); + m_Alerts.gameObject.SetActive(IsAdavancedModeActive); } } @@ -89,48 +102,19 @@ private static string GenerateRandomRoomName() return random.Next(100000, 999999).ToString(); } - private void UpdateRoomNumberDisplay() + private void UpdateDisplay() { - if (m_RoomNumberTextLobby) - { - m_RoomNumberTextLobby.text = data.roomName; - } - if (m_RoomNumberTextRoomSettings) - { - m_RoomNumberTextRoomSettings.text = data.roomName; - } - } - - public enum Mode - { - Null, - Lobby, - Joined - } - - [SerializeField] private GameObject m_LobbyElements; - [SerializeField] private GameObject m_JoinedElements; - - private Mode m_CurrentMode = Mode.Lobby; + if (m_RoomNumber) m_RoomNumber.text = "RoomName: " + data.roomName; + if (m_Nickname) m_Nickname.text = "Nickname: " + NickName; - public override void InitPanel() - { - base.InitPanel(); - - InitMultiplayer(); - UpdateRoomNumberDisplay(); - UserInBeginnerMode(); - } - - public async void InitMultiplayer() - { - await MultiplayerManager.m_Instance.Connect(); - MultiplayerManager.m_Instance.Disconnected += OnDisconnected; } - private void OnDisconnected() + private async void Connect() { - UpdateMode(Mode.Lobby); + if (MultiplayerManager.m_Instance != null) + { + await MultiplayerManager.m_Instance.Connect(); + } } private async void JoinRoom() @@ -138,21 +122,7 @@ private async void JoinRoom() if (MultiplayerManager.m_Instance != null) { - - bool success = await MultiplayerManager.m_Instance.JoinRoom(data); - - if (success) - { - - // Additional UI updates or feedback - UpdateMode(Mode.Joined); - UpdateRoomNumberDisplay(); // Update room number display after joining - } - else - { - // Provide user feedback with some UI element - } - + await MultiplayerManager.m_Instance.JoinRoom(data); } } @@ -160,65 +130,15 @@ private async void LeaveRoom() { if (MultiplayerManager.m_Instance != null) { - - bool success = await MultiplayerManager.m_Instance.LeaveRoom(false); - - if (success) - { - - // Additional UI updates or feedback - UpdateMode(Mode.Lobby); - UpdateRoomNumberDisplay(); // Update room number display after joining - } - else - { - // Provide user feedback with some UI element - } - - } - } - - private void UpdateRoomExistenceMessage() - { - if (m_RoomNumberTextLobby) return; - - if (MultiplayerManager.m_Instance != null && m_DoesRoomNumberExist != null) - { - if (MultiplayerManager.m_Instance.DoesRoomNameExist(data.roomName)) - { - m_DoesRoomNumberExist.text = "This room exists. You will be joining an active session. You can change the room number by pressing edit."; - } - else - { - m_DoesRoomNumberExist.text = "This room does not exist yet. By pressing join, the room will be created."; - } - } - } - - private void UpdateMode(Mode newMode) - { - m_CurrentMode = newMode; - m_LobbyElements.SetActive(m_CurrentMode == Mode.Lobby); - m_JoinedElements.SetActive(m_CurrentMode == Mode.Joined); - - // Update room number display if switching to a mode that shows it - if (m_CurrentMode == Mode.Lobby || m_CurrentMode == Mode.Joined) - { - UpdateRoomNumberDisplay(); - } - - if (m_CurrentMode == Mode.Lobby) - { - UserInBeginnerMode(); + await MultiplayerManager.m_Instance.LeaveRoom(false); } } - private void RefreshObjects() + private void OnStateUpdated(ConnectionState newState) { - + m_State.text = "State: " + newState.ToString(); } - // This function serves as a callback from ProfilePopUpButtons that want to // change the mode of the popup on click. public void OnMultiplayerPanelButtonPressed(MultiplayerPanelButton button) @@ -227,18 +147,20 @@ public void OnMultiplayerPanelButtonPressed(MultiplayerPanelButton button) { // Identifier for signaling we understand the info message. case SketchControlsScript.GlobalCommands.Null: - UpdateMode(Mode.Lobby); - RefreshObjects(); + //UpdateMode(Mode.Disconnected); + break; + case SketchControlsScript.GlobalCommands.MultiplayerConnect: + Connect(); break; case SketchControlsScript.GlobalCommands.MultiplayerPanelOptions: - switch ((Mode)button.m_CommandParam) - { - case Mode.Lobby: - UpdateMode(Mode.Lobby); - break; - default: - break; - } + //switch ((Mode)button.m_CommandParam) + //{ + // case Mode.Lobby: + // UpdateMode(Mode.Lobby); + // break; + // default: + // break; + //} break; case SketchControlsScript.GlobalCommands.MultiplayerJoinRoom: JoinRoom(); diff --git a/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs b/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs index 21331fc836..02ade33e7d 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs @@ -25,6 +25,7 @@ public interface IConnectionHandler Task LeaveRoom(bool force = false); ConnectionState State { get; } ConnectionUserInfo UserInfo { get; set; } + string LastError { get; } } public interface IDataConnectionHandler : IConnectionHandler diff --git a/Assets/Scripts/Multiplayer/MultiplayerManager.cs b/Assets/Scripts/Multiplayer/MultiplayerManager.cs index a4a934ba7f..3a1f3b7b10 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerManager.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerManager.cs @@ -18,6 +18,8 @@ using System.Threading.Tasks; using UnityEngine; using Unity.XR.CoreUtils; +using System.ComponentModel.Composition; + #if OCULUS_SUPPORTED using OVRPlatform = Oculus.Platform; #endif @@ -48,6 +50,7 @@ public class MultiplayerManager : MonoBehaviour public Action> remotePlayerJoined; public Action playerLeft; public Action> roomDataRefreshed; + public event Action StateUpdated; private List m_RoomData = new List(); ulong myOculusUserId; @@ -56,7 +59,21 @@ public class MultiplayerManager : MonoBehaviour internal string UserId; [HideInInspector] public string CurrentRoomName; - public ConnectionState State => m_Manager?.State ?? ConnectionState.DISCONNECTED; + //public ConnectionState State => m_Manager?.State ?? ConnectionState.DISCONNECTED; + private ConnectionState _state; + public ConnectionState State + { + get => _state; + private set + { + if (_state != value) + { + _state = value; + StateUpdated?.Invoke(_state); // Trigger the event when the state changes + } + } + } + public string LastError { get; private set; } public ConnectionUserInfo UserInfo { @@ -70,6 +87,8 @@ public ConnectionUserInfo UserInfo } } + public RoomCreateData data; + void Awake() { m_Instance = this; @@ -93,6 +112,8 @@ void Start() } }); #endif + + State = ConnectionState.INITIALISING; switch (m_MultiplayerType) { case MultiplayerType.Photon: @@ -104,13 +125,14 @@ void Start() #endif #if PHOTON_UNITY_NETWORKING && PHOTON_VOICE_DEFINED m_VoiceManager = new PhotonVoiceManager(this); - if (m_Manager != null) ControllerConsoleScript.m_Instance.AddNewLine("PhotonVoiceManager Loaded"); + if (m_VoiceManager != null) ControllerConsoleScript.m_Instance.AddNewLine("PhotonVoiceManager Loaded"); else ControllerConsoleScript.m_Instance.AddNewLine("PhotonVoiceManager Not Loaded"); #endif break; default: return; } + if (m_VoiceManager != null && m_Manager != null) State = ConnectionState.INITIALIZED; localPlayerJoined += OnLocalPlayerJoined; remotePlayerJoined += OnRemotePlayerJoined; @@ -132,45 +154,80 @@ void OnDestroy() public async Task Connect() { + State = ConnectionState.CONNECTING; + var successData = false; - if (m_Manager != null) + if (m_Manager != null) successData = await m_Manager.Connect(); + + var successVoice = false; + if (m_VoiceManager != null) successVoice = await m_VoiceManager.Connect(); + + if (!successData) { - successData = await m_Manager.Connect(); + State = ConnectionState.ERROR; + LastError = m_Manager.LastError; } - var successVoice = false; - if (m_VoiceManager != null) + else if (!successVoice) { - successVoice = await m_VoiceManager.Connect(); + State = ConnectionState.ERROR; + LastError = m_VoiceManager.LastError; } + else State = ConnectionState.IN_LOBBY; + + return successData & successVoice; } public async Task JoinRoom(RoomCreateData RoomData) { - // attempt to connect multiplayer backend - bool success = await m_Manager.JoinRoom(RoomData); - if (!success) return success; + State = ConnectionState.JOINING_ROOM; - // attempt to connect voice backend OPTIONAL - m_VoiceManager?.JoinRoom(RoomData); + bool successData = false; + if (m_Manager != null) successData = await m_Manager.JoinRoom(RoomData); + + bool successVoice = false; + if (m_VoiceManager != null) successVoice = await m_VoiceManager.JoinRoom(RoomData); m_VoiceManager?.StartSpeaking(); - return success; + if (!successData) + { + State = ConnectionState.ERROR; + LastError = m_Manager.LastError; + } + else if (!successVoice) + { + State = ConnectionState.ERROR; + LastError = m_VoiceManager.LastError; + } + else State = ConnectionState.IN_ROOM; + + return successData & successVoice; } public async Task LeaveRoom(bool force = false) { - if (m_Manager != null) + State = ConnectionState.DISCONNECTING; + + bool successData = false; + if (m_Manager != null) successData = await m_Manager.LeaveRoom(); + + bool successVoice = false; + m_VoiceManager?.StopSpeaking(); + if (m_VoiceManager != null) successVoice = await m_VoiceManager.LeaveRoom(); + + if (!successData) { - var success = await m_Manager.LeaveRoom(force); - if (success) - { - m_VoiceManager?.LeaveRoom(); - StopSpeaking(); - } - return success; + State = ConnectionState.ERROR; + LastError = m_Manager.LastError; } - return true; + else if (!successVoice) + { + State = ConnectionState.ERROR; + LastError = m_VoiceManager.LastError; + } + else State = ConnectionState.DISCONNECTED; + + return successData & successVoice; } public bool DoesRoomNameExist(string roomName) @@ -344,5 +401,22 @@ public void StopSpeaking() m_VoiceManager?.StopSpeaking(); } + public bool IsDisconnectable() + { + + return State == ConnectionState.IN_ROOM || State == ConnectionState.IN_LOBBY; + } + public bool IsConnectable() + { + return State == ConnectionState.INITIALIZED || State == ConnectionState.DISCONNECTED; + } + public bool CanJoinRoom() + { + return State == ConnectionState.IN_LOBBY; + } + public bool CanLeaveRoom() + { + return State == ConnectionState.IN_ROOM; + } } } diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs index 65bd747fe4..56a3e12871 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs @@ -40,14 +40,12 @@ public class PhotonManager : IDataConnectionHandler, INetworkRunnerCallbacks private AppSettings m_PhotonAppSettings; - public event Action Disconnected; public ConnectionUserInfo UserInfo { get; set; } public ConnectionState State { get; private set; } public string LastError { get; private set; } - public PhotonManager(MultiplayerManager manager) { m_Manager = manager; diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonVoiceManager.cs b/Assets/Scripts/Multiplayer/Photon/PhotonVoiceManager.cs index 1ae27cb542..64a5593d4c 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonVoiceManager.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonVoiceManager.cs @@ -68,7 +68,7 @@ public async Task Init() return false; } - ControllerConsoleScript.m_Instance.AddNewLine("Runner Initialized"); + ControllerConsoleScript.m_Instance.AddNewLine("Photon Voice Initialized"); State = ConnectionState.INITIALIZED; return true; diff --git a/Assets/Scripts/SketchControlsScript.cs b/Assets/Scripts/SketchControlsScript.cs index 43d9481a62..fe6f317656 100644 --- a/Assets/Scripts/SketchControlsScript.cs +++ b/Assets/Scripts/SketchControlsScript.cs @@ -152,6 +152,9 @@ public enum GlobalCommands MultiplayerJoinRoom = 1004, EditMultiplayerRoomName = 1005, MultiplayerLeaveRoom = 1006, + MultiplayerConnect = 1007, + MultiplayerDisconnect = 1008, + EditMultiplayerNickName = 1009, RenameSketch = 5200, OpenLayerOptionsPopup = 5201, @@ -4519,6 +4522,13 @@ public void IssueGlobalCommand(GlobalCommands rEnum, int iParam1 = -1, panel.RoomName = KeyboardPopUpWindow.m_LastInput; DismissPopupOnCurrentGazeObject(false); break; + } + case GlobalCommands.EditMultiplayerNickName: + { + var panel = (MultiplayerPanel)m_PanelManager.GetActivePanelByType(BasePanel.PanelType.Multiplayer); + panel.NickName = KeyboardPopUpWindow.m_LastInput; + DismissPopupOnCurrentGazeObject(false); + break; } case GlobalCommands.ShowWindowGUI: break; @@ -4852,7 +4862,8 @@ public void IssueGlobalCommand(GlobalCommands rEnum, int iParam1 = -1, case GlobalCommands.Null: break; // Intentionally blank. case GlobalCommands.MultiplayerPanelOptions: break; // Intentionally blank. case GlobalCommands.MultiplayerJoinRoom: break; // Intentionally blank. - case GlobalCommands.MultiplayerLeaveRoom: break; // Intentionally blank. + case GlobalCommands.MultiplayerLeaveRoom: break; // Intentionally blank. + case GlobalCommands.MultiplayerConnect: break; // Intentionally blank. default: Debug.LogError($"Unrecognized command {rEnum}"); break; @@ -5063,9 +5074,18 @@ public bool IsCommandAvailable(GlobalCommands rEnum, int iParam = -1) return m_WidgetManager.AnyActivePathHasAKnot(); case GlobalCommands.GoogleDriveSync: return App.GoogleIdentity.LoggedIn; - case GlobalCommands.RecordCameraPath: return m_WidgetManager.CameraPathsVisible; - case GlobalCommands.AdvancedPanelsToggle: return !(MultiplayerManager.m_Instance.State == ConnectionState.IN_ROOM); - case GlobalCommands.MultiplayerJoinRoom: return !PanelManager.m_Instance.AdvancedModeActive(); + case GlobalCommands.RecordCameraPath: + return m_WidgetManager.CameraPathsVisible; + case GlobalCommands.AdvancedPanelsToggle: + return !(MultiplayerManager.m_Instance.State == ConnectionState.IN_ROOM); + case GlobalCommands.MultiplayerConnect: + return MultiplayerManager.m_Instance.IsConnectable(); + case GlobalCommands.MultiplayerDisconnect: + return MultiplayerManager.m_Instance.IsDisconnectable(); + case GlobalCommands.MultiplayerJoinRoom: + return !PanelManager.m_Instance.AdvancedModeActive() && MultiplayerManager.m_Instance.CanJoinRoom(); + case GlobalCommands.MultiplayerLeaveRoom: + return MultiplayerManager.m_Instance.CanLeaveRoom(); } return true; } diff --git a/Assets/Settings/Localization/Strings/Strings Shared Data.asset b/Assets/Settings/Localization/Strings/Strings Shared Data.asset index e2032ebcc0..b2940246d9 100644 --- a/Assets/Settings/Localization/Strings/Strings Shared Data.asset +++ b/Assets/Settings/Localization/Strings/Strings Shared Data.asset @@ -11,7 +11,7 @@ MonoBehaviour: m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 5b11a58205ec3474ca216360e9fa74a8, type: 3} m_Name: Strings Shared Data - m_EditorClassIdentifier: + m_EditorClassIdentifier: m_TableCollectionName: Strings m_TableCollectionNameGuidString: c84355079ab3f3e4f8f3812258805f86 m_Entries: @@ -3343,6 +3343,30 @@ MonoBehaviour: m_Key: BRUSH_SETTINGS_TRAY_BRUSH_SIZE m_Metadata: m_Items: [] + - m_Id: 294636550056648704 + m_Key: MP_CONNECT + m_Metadata: + m_Items: [] + - m_Id: 294636769901092864 + m_Key: MP_JOIN_ROOM + m_Metadata: + m_Items: [] + - m_Id: 294636908816441344 + m_Key: MP_LEAVE_ROOM + m_Metadata: + m_Items: [] + - m_Id: 294637000357126144 + m_Key: MP_DISCONNECT + m_Metadata: + m_Items: [] + - m_Id: 294637073212186624 + m_Key: MP_EDIT_ROOM_NAME + m_Metadata: + m_Items: [] + - m_Id: 294637384320491520 + m_Key: MP_EDIT_NICK_NAME + m_Metadata: + m_Items: [] m_Metadata: m_Items: [] m_KeyGenerator: diff --git a/Assets/Settings/Localization/Strings/Strings_en.asset b/Assets/Settings/Localization/Strings/Strings_en.asset index 2fd03058df..028b35db1d 100644 --- a/Assets/Settings/Localization/Strings/Strings_en.asset +++ b/Assets/Settings/Localization/Strings/Strings_en.asset @@ -11,7 +11,7 @@ MonoBehaviour: m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: e9620f8c34305754d8cc9a7e49e852d9, type: 3} m_Name: Strings_en - m_EditorClassIdentifier: + m_EditorClassIdentifier: m_LocaleId: m_Code: en m_SharedData: {fileID: 11400000, guid: c84355079ab3f3e4f8f3812258805f86, type: 2} @@ -3540,6 +3540,30 @@ MonoBehaviour: m_Localized: Brush Size m_Metadata: m_Items: [] + - m_Id: 294636550056648704 + m_Localized: Connect + m_Metadata: + m_Items: [] + - m_Id: 294636769901092864 + m_Localized: Join Room + m_Metadata: + m_Items: [] + - m_Id: 294636908816441344 + m_Localized: Leave Room + m_Metadata: + m_Items: [] + - m_Id: 294637000357126144 + m_Localized: Disconnect + m_Metadata: + m_Items: [] + - m_Id: 294637073212186624 + m_Localized: Edite Room Name + m_Metadata: + m_Items: [] + - m_Id: 294637384320491520 + m_Localized: Edit Nickname + m_Metadata: + m_Items: [] references: version: 2 RefIds: [] From d3a81897fcd9cf5f086bd0fe77541fb5cddac8b3 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Wed, 30 Oct 2024 11:34:30 +0000 Subject: [PATCH 063/174] Adding Disconnect Logic Adding Disconnect Logic and Fixing Leave Room Logic --- Assets/Editor/MultiplayerManagerEditor.cs | 6 ++-- Assets/Scripts/GUI/MultiplayerPanel.cs | 15 ++++++-- .../Multiplayer/MultiplayerInterfaces.cs | 2 ++ .../Scripts/Multiplayer/MultiplayerManager.cs | 27 +++++++++++++- .../Multiplayer/Photon/PhotonManager.cs | 32 +++++++++++++++-- .../Multiplayer/Photon/PhotonVoiceManager.cs | 36 +++++++++++++++++-- Assets/Scripts/SketchControlsScript.cs | 1 + 7 files changed, 107 insertions(+), 12 deletions(-) diff --git a/Assets/Editor/MultiplayerManagerEditor.cs b/Assets/Editor/MultiplayerManagerEditor.cs index a2e2e32611..192b9c9e69 100644 --- a/Assets/Editor/MultiplayerManagerEditor.cs +++ b/Assets/Editor/MultiplayerManagerEditor.cs @@ -25,21 +25,21 @@ public override void OnInspectorGUI() roomName = EditorGUILayout.TextField("Room Name", roomName); // Button to join the lobby - if (GUILayout.Button("Join Lobby") && multiplayerManager.State == ConnectionState.INITIALIZED) + if (GUILayout.Button("Join Lobby") ) { ConnectToLobby(); EditorUtility.SetDirty(target); // Mark the object as dirty to recognize state changes } // Button to join the room - if (GUILayout.Button("Join Room") && multiplayerManager.State == ConnectionState.IN_LOBBY) + if (GUILayout.Button("Join Room")) { ConnectToRoom(); EditorUtility.SetDirty(target); // Update inspector on state change } // Button to exit the room - if (GUILayout.Button("Exit Room") && multiplayerManager.State == ConnectionState.IN_ROOM) + if (GUILayout.Button("Exit Room") ) { DisconnectFromRoom(); EditorUtility.SetDirty(target); // Update inspector on state change diff --git a/Assets/Scripts/GUI/MultiplayerPanel.cs b/Assets/Scripts/GUI/MultiplayerPanel.cs index 72b8c63971..3ca8d1a5aa 100644 --- a/Assets/Scripts/GUI/MultiplayerPanel.cs +++ b/Assets/Scripts/GUI/MultiplayerPanel.cs @@ -134,18 +134,24 @@ private async void LeaveRoom() } } + private async void Disconnect() + { + if (MultiplayerManager.m_Instance != null) + { + await MultiplayerManager.m_Instance.Disconnect(); + } + } + private void OnStateUpdated(ConnectionState newState) { m_State.text = "State: " + newState.ToString(); } - // This function serves as a callback from ProfilePopUpButtons that want to - // change the mode of the popup on click. public void OnMultiplayerPanelButtonPressed(MultiplayerPanelButton button) { switch (button.m_Command) { - // Identifier for signaling we understand the info message. + case SketchControlsScript.GlobalCommands.Null: //UpdateMode(Mode.Disconnected); break; @@ -168,6 +174,9 @@ public void OnMultiplayerPanelButtonPressed(MultiplayerPanelButton button) case SketchControlsScript.GlobalCommands.MultiplayerLeaveRoom: LeaveRoom(); break; + case SketchControlsScript.GlobalCommands.MultiplayerDisconnect: + Disconnect(); + break; } } } diff --git a/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs b/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs index 02ade33e7d..079d36e4ce 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs @@ -23,6 +23,7 @@ public interface IConnectionHandler Task Connect(); Task JoinRoom(RoomCreateData data); Task LeaveRoom(bool force = false); + Task Disconnect(); ConnectionState State { get; } ConnectionUserInfo UserInfo { get; set; } string LastError { get; } @@ -63,6 +64,7 @@ public enum ConnectionState IN_ROOM = 8, RECONNECTING = 9, ERROR = 10, + LEAVING_ROOM = 11 } public interface ITransientData diff --git a/Assets/Scripts/Multiplayer/MultiplayerManager.cs b/Assets/Scripts/Multiplayer/MultiplayerManager.cs index 3a1f3b7b10..83535ed25b 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerManager.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerManager.cs @@ -206,7 +206,7 @@ public async Task JoinRoom(RoomCreateData RoomData) public async Task LeaveRoom(bool force = false) { - State = ConnectionState.DISCONNECTING; + State = ConnectionState.LEAVING_ROOM; bool successData = false; if (m_Manager != null) successData = await m_Manager.LeaveRoom(); @@ -215,6 +215,31 @@ public async Task LeaveRoom(bool force = false) m_VoiceManager?.StopSpeaking(); if (m_VoiceManager != null) successVoice = await m_VoiceManager.LeaveRoom(); + if (!successData) + { + State = ConnectionState.ERROR; + LastError = m_Manager.LastError; + } + else if (!successVoice) + { + State = ConnectionState.ERROR; + LastError = m_VoiceManager.LastError; + } + else State = ConnectionState.IN_LOBBY; + + return successData & successVoice; + } + + public async Task Disconnect() + { + State = ConnectionState.DISCONNECTING; + + bool successData = false; + if (m_Manager != null) successData = await m_Manager.Disconnect(); + + bool successVoice = false; + if (m_VoiceManager != null) successVoice = await m_VoiceManager.Disconnect(); + if (!successData) { State = ConnectionState.ERROR; diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs index 56a3e12871..940997a6aa 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs @@ -144,7 +144,7 @@ public async Task JoinRoom(RoomCreateData roomCreateData) } - public async Task LeaveRoom(bool force) + public async Task Disconnect() { State = ConnectionState.DISCONNECTING; @@ -157,16 +157,42 @@ public async Task LeaveRoom(bool force) m_LocalPlayer = null; } - await m_Runner.Shutdown(forceShutdownProcedure: force); + await m_Runner.Shutdown(forceShutdownProcedure: false); GameObject.Destroy(m_Runner.gameObject); - if(m_Runner.IsShutdown) State = ConnectionState.DISCONNECTED; + if(m_Runner.IsShutdown) + { + State = ConnectionState.DISCONNECTED; + ControllerConsoleScript.m_Instance.AddNewLine("Left Room"); + UserInfo = new ConnectionUserInfo { UserId = m_Runner.UserId }; + } + else + { + State = ConnectionState.ERROR; + LastError = $"Failed to disconnect"; + ControllerConsoleScript.m_Instance.AddNewLine(LastError); + } return m_Runner.IsShutdown; } return true; } + public async Task LeaveRoom(bool force) + { + + if (m_Runner != null) + { + bool success = await Disconnect(); + if (!success) return false; + success = await Connect(); + if (!success) return false; + return true; + } + return false; + + } + public void Update() { var copy = m_PlayersSpawning.ToList(); diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonVoiceManager.cs b/Assets/Scripts/Multiplayer/Photon/PhotonVoiceManager.cs index 64a5593d4c..a0efe77cdc 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonVoiceManager.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonVoiceManager.cs @@ -153,12 +153,12 @@ public async Task LeaveRoom(bool force) ControllerConsoleScript.m_Instance.AddNewLine("Initiated leaving the room..."); - while (m_VoiceConnection.ClientState != ClientState.JoinedLobby && m_VoiceConnection.ClientState != ClientState.Disconnected) + while (m_VoiceConnection.ClientState != ClientState.ConnectedToMasterServer) { await Task.Delay(100); } - if (m_VoiceConnection.ClientState == ClientState.JoinedLobby) + if (m_VoiceConnection.ClientState == ClientState.ConnectedToMasterServer) { State = ConnectionState.DISCONNECTED; ControllerConsoleScript.m_Instance.AddNewLine("Successfully left the room and returned to the lobby."); @@ -172,6 +172,38 @@ public async Task LeaveRoom(bool force) } } + public async Task Disconnect() { + + State = ConnectionState.DISCONNECTING; + + if (!m_VoiceConnection.Client.IsConnected) + { + ControllerConsoleScript.m_Instance.AddNewLine("Voice Server is already disconnected."); + return true; + } + + m_VoiceConnection.Client.Disconnect(); + + while (m_VoiceConnection.ClientState != ClientState.Disconnected) + { + await Task.Delay(100); + ControllerConsoleScript.m_Instance.AddNewLine("Disconnecting."); + } + + if (m_VoiceConnection.ClientState == ClientState.Disconnected) + { + State = ConnectionState.DISCONNECTED; + ControllerConsoleScript.m_Instance.AddNewLine("Successfully disconnected from Voice Server."); + return true; + } + else + { + State = ConnectionState.ERROR; + ControllerConsoleScript.m_Instance.AddNewLine("Failed to disconnect from Voice Server."); + return false; + } + } + public bool StartSpeaking() { m_Recorder = m_VoiceConnection.PrimaryRecorder; diff --git a/Assets/Scripts/SketchControlsScript.cs b/Assets/Scripts/SketchControlsScript.cs index fe6f317656..1ce5d9b37e 100644 --- a/Assets/Scripts/SketchControlsScript.cs +++ b/Assets/Scripts/SketchControlsScript.cs @@ -4864,6 +4864,7 @@ public void IssueGlobalCommand(GlobalCommands rEnum, int iParam1 = -1, case GlobalCommands.MultiplayerJoinRoom: break; // Intentionally blank. case GlobalCommands.MultiplayerLeaveRoom: break; // Intentionally blank. case GlobalCommands.MultiplayerConnect: break; // Intentionally blank. + case GlobalCommands.MultiplayerDisconnect: break; // Intentionally blank. default: Debug.LogError($"Unrecognized command {rEnum}"); break; From 5d3073f88d0226eac35a18eff06630f5dc18b184 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Wed, 30 Oct 2024 11:43:14 +0000 Subject: [PATCH 064/174] Fixing logging messages Fixing logging messages --- Assets/Scripts/GUI/MultiplayerPanel.cs | 2 +- .../Scripts/Multiplayer/MultiplayerManager.cs | 5 +- .../Multiplayer/Photon/PhotonManager.cs | 16 +- .../Multiplayer/Photon/PhotonVoiceManager.cs | 34 +- Assets/Scripts/SketchControlsScript.cs | 10451 ++++++++-------- 5 files changed, 5253 insertions(+), 5255 deletions(-) diff --git a/Assets/Scripts/GUI/MultiplayerPanel.cs b/Assets/Scripts/GUI/MultiplayerPanel.cs index 3ca8d1a5aa..0b21f3d20a 100644 --- a/Assets/Scripts/GUI/MultiplayerPanel.cs +++ b/Assets/Scripts/GUI/MultiplayerPanel.cs @@ -151,7 +151,7 @@ public void OnMultiplayerPanelButtonPressed(MultiplayerPanelButton button) { switch (button.m_Command) { - + case SketchControlsScript.GlobalCommands.Null: //UpdateMode(Mode.Disconnected); break; diff --git a/Assets/Scripts/Multiplayer/MultiplayerManager.cs b/Assets/Scripts/Multiplayer/MultiplayerManager.cs index 83535ed25b..826610d6bb 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerManager.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerManager.cs @@ -17,8 +17,6 @@ using System.Linq; using System.Threading.Tasks; using UnityEngine; -using Unity.XR.CoreUtils; -using System.ComponentModel.Composition; #if OCULUS_SUPPORTED using OVRPlatform = Oculus.Platform; @@ -431,14 +429,17 @@ public bool IsDisconnectable() return State == ConnectionState.IN_ROOM || State == ConnectionState.IN_LOBBY; } + public bool IsConnectable() { return State == ConnectionState.INITIALIZED || State == ConnectionState.DISCONNECTED; } + public bool CanJoinRoom() { return State == ConnectionState.IN_LOBBY; } + public bool CanLeaveRoom() { return State == ConnectionState.IN_ROOM; diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs index 940997a6aa..01d308424e 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs @@ -75,12 +75,12 @@ public async Task Init() catch (Exception ex) { State = ConnectionState.ERROR; - LastError = $"Failed to Initialize lobby: {ex.Message}"; + LastError = $"[PhotonManager] Failed to Initialize lobby: {ex.Message}"; ControllerConsoleScript.m_Instance.AddNewLine(LastError); return false; } - ControllerConsoleScript.m_Instance.AddNewLine("Runner Initialized"); + ControllerConsoleScript.m_Instance.AddNewLine("[PhotonManager] Runner Initialized"); State = ConnectionState.INITIALIZED; return true; } @@ -96,12 +96,12 @@ public async Task Connect() if (result.Ok) { State = ConnectionState.IN_LOBBY; - ControllerConsoleScript.m_Instance.AddNewLine("Connected to lobby"); + ControllerConsoleScript.m_Instance.AddNewLine("[PhotonManager] Connected to lobby"); } else { State = ConnectionState.ERROR; - LastError = $"Failed to join lobby: {result.ErrorMessage}"; + LastError = $"[PhotonManager] Failed to join lobby: {result.ErrorMessage}"; ControllerConsoleScript.m_Instance.AddNewLine(LastError); } @@ -130,13 +130,13 @@ public async Task JoinRoom(RoomCreateData roomCreateData) if (result.Ok) { State = ConnectionState.IN_ROOM; - ControllerConsoleScript.m_Instance.AddNewLine("Joined Room"); + ControllerConsoleScript.m_Instance.AddNewLine("[PhotonManager] Joined Room"); UserInfo = new ConnectionUserInfo { UserId = m_Runner.UserId }; } else { State = ConnectionState.ERROR; - LastError = $"Failed to join Room: {result.ErrorMessage}"; + LastError = $"[PhotonManager] Failed to join Room: {result.ErrorMessage}"; ControllerConsoleScript.m_Instance.AddNewLine(LastError); } @@ -163,13 +163,13 @@ public async Task Disconnect() if(m_Runner.IsShutdown) { State = ConnectionState.DISCONNECTED; - ControllerConsoleScript.m_Instance.AddNewLine("Left Room"); + ControllerConsoleScript.m_Instance.AddNewLine("[PhotonManager] Left Room"); UserInfo = new ConnectionUserInfo { UserId = m_Runner.UserId }; } else { State = ConnectionState.ERROR; - LastError = $"Failed to disconnect"; + LastError = $"[PhotonManager] Failed to disconnect"; ControllerConsoleScript.m_Instance.AddNewLine(LastError); } diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonVoiceManager.cs b/Assets/Scripts/Multiplayer/Photon/PhotonVoiceManager.cs index a0efe77cdc..0c3b016d72 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonVoiceManager.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonVoiceManager.cs @@ -49,7 +49,7 @@ public async Task Init() { State = ConnectionState.INITIALISING; m_VoiceConnection = GameObject.FindFirstObjectByType(); - if (m_VoiceConnection == null) throw new Exception("VoiceConnection component not found in scene"); + if (m_VoiceConnection == null) throw new Exception("[PhotonVoiceManager] VoiceConnection component not found in scene"); m_VoiceConnection.Settings = new AppSettings { @@ -63,12 +63,12 @@ public async Task Init() catch (Exception ex) { State = ConnectionState.ERROR; - LastError = $"Failed to Initialize lobby: {ex.Message}"; + LastError = $"[PhotonVoiceManager] Failed to Initialize lobby: {ex.Message}"; ControllerConsoleScript.m_Instance.AddNewLine(LastError); return false; } - ControllerConsoleScript.m_Instance.AddNewLine("Photon Voice Initialized"); + ControllerConsoleScript.m_Instance.AddNewLine("[PhotonVoiceManager] Succesfully initialized"); State = ConnectionState.INITIALIZED; return true; @@ -82,11 +82,11 @@ public async Task Connect() if (!m_VoiceConnection.Client.IsConnected) { - ControllerConsoleScript.m_Instance.AddNewLine("Attempting to connect Voice Server..."); + //ControllerConsoleScript.m_Instance.AddNewLine("[PhotonVoiceManager] Attempting to connect Voice Server..."); m_VoiceConnection.ConnectUsingSettings(); while (!ConnectedToMaster) { - ControllerConsoleScript.m_Instance.AddNewLine("Waiting for Voice Connection to establish..."); + //ControllerConsoleScript.m_Instance.AddNewLine("Waiting for Voice Connection to establish..."); await Task.Delay(100); } } @@ -94,12 +94,12 @@ public async Task Connect() if (ConnectedToMaster) { State = ConnectionState.IN_LOBBY; - ControllerConsoleScript.m_Instance.AddNewLine("Voice Connection successfully established."); + ControllerConsoleScript.m_Instance.AddNewLine("[PhotonVoiceManager] Connection successfully established."); } else { State = ConnectionState.ERROR; - LastError = $"Failed to connect to Voice Server."; + LastError = $"[PhotonVoiceManager] Failed to connect."; ControllerConsoleScript.m_Instance.AddNewLine(LastError); } @@ -125,12 +125,12 @@ public async Task JoinRoom(RoomCreateData RoomData) if (roomJoined) { State = ConnectionState.IN_ROOM; - ControllerConsoleScript.m_Instance.AddNewLine($"Successfully joined room: {RoomData.roomName}"); + ControllerConsoleScript.m_Instance.AddNewLine($"[PhotonVoiceManager] Successfully joined room"); } else { State = ConnectionState.ERROR; - LastError = $"Failed to join or create room: {RoomData}"; + LastError = $"[PhotonVoiceManager] Failed to join or create room"; ControllerConsoleScript.m_Instance.AddNewLine(LastError); } @@ -147,11 +147,11 @@ public async Task LeaveRoom(bool force) if (!leftRoom) { - ControllerConsoleScript.m_Instance.AddNewLine("Failed to initiate leaving the room."); + //ControllerConsoleScript.m_Instance.AddNewLine("[PhotonVoiceManager] Failed to initiate leaving the room."); return false; } - ControllerConsoleScript.m_Instance.AddNewLine("Initiated leaving the room..."); + //ControllerConsoleScript.m_Instance.AddNewLine("Initiated leaving the room..."); while (m_VoiceConnection.ClientState != ClientState.ConnectedToMasterServer) { @@ -161,13 +161,13 @@ public async Task LeaveRoom(bool force) if (m_VoiceConnection.ClientState == ClientState.ConnectedToMasterServer) { State = ConnectionState.DISCONNECTED; - ControllerConsoleScript.m_Instance.AddNewLine("Successfully left the room and returned to the lobby."); + ControllerConsoleScript.m_Instance.AddNewLine("[PhotonVoiceManager] Successfully left the room."); return true; } else { State = ConnectionState.ERROR; - ControllerConsoleScript.m_Instance.AddNewLine("Failed to leave the room properly."); + ControllerConsoleScript.m_Instance.AddNewLine("[PhotonVoiceManager] Failed to leave the room."); return false; } } @@ -178,7 +178,7 @@ public async Task Disconnect() { if (!m_VoiceConnection.Client.IsConnected) { - ControllerConsoleScript.m_Instance.AddNewLine("Voice Server is already disconnected."); + //ControllerConsoleScript.m_Instance.AddNewLine("[PhotonVoiceManager] Voice Server is already disconnected."); return true; } @@ -187,19 +187,19 @@ public async Task Disconnect() { while (m_VoiceConnection.ClientState != ClientState.Disconnected) { await Task.Delay(100); - ControllerConsoleScript.m_Instance.AddNewLine("Disconnecting."); + //ControllerConsoleScript.m_Instance.AddNewLine("Disconnecting."); } if (m_VoiceConnection.ClientState == ClientState.Disconnected) { State = ConnectionState.DISCONNECTED; - ControllerConsoleScript.m_Instance.AddNewLine("Successfully disconnected from Voice Server."); + ControllerConsoleScript.m_Instance.AddNewLine("[PhotonVoiceManager] Successfully disconnected from Server."); return true; } else { State = ConnectionState.ERROR; - ControllerConsoleScript.m_Instance.AddNewLine("Failed to disconnect from Voice Server."); + ControllerConsoleScript.m_Instance.AddNewLine("[PhotonVoiceManager] Failed to disconnect from Server."); return false; } } diff --git a/Assets/Scripts/SketchControlsScript.cs b/Assets/Scripts/SketchControlsScript.cs index 1ce5d9b37e..474c8adac0 100644 --- a/Assets/Scripts/SketchControlsScript.cs +++ b/Assets/Scripts/SketchControlsScript.cs @@ -1,5235 +1,5232 @@ -// Copyright 2020 The Tilt 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 OpenBrush.Multiplayer; -using System; -using System.Collections; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using TiltBrush.Layers; -using UnityEngine; -using UnityEngine.InputSystem; -using SymmetryMode = TiltBrush.PointerManager.SymmetryMode; - -namespace TiltBrush -{ - - public class SketchControlsScript : MonoBehaviour - { - public const string kRemoveHeadsetFyi = "Remove headset to view."; - const string kTiltBrushGalleryUrl = "https://poly.google.com/tiltbrush"; - const string kBlocksGalleryUrl = "https://poly.google.com/blocks"; - const string kPolyMainPageUri = "https://poly.google.com"; - - static public SketchControlsScript m_Instance; - static bool sm_enableGrabHaptics = true; - - // ------------------------------------------------------------ - // Constants and types - // ------------------------------------------------------------ - - public enum GlobalCommands - { - Null, - Save, - SaveNew, - Load, - NewSketch, - StraightEdge, - AutoOrient, - Undo, - Redo, - Tiltasaurus, - LightingHdr, - AudioVisualization, - ResetAllPanels, - SketchOrigin, - SymmetryPlane, - MultiMirror, - ViewOnly, - SaveGallery, - LightingLdr, - ShowSketchFolder, - About, - LoadNamedFile, // iParam1 : (optional) - send through a LoadSpeed as int - DropCam, - CuratedGallery, - Unused_UploadToCloud, - AnalyticsEnabled_Deprecated, - Credits, - LogOutOfGenericCloud, - DraftingVisibility, - DeleteSketch, - ShowWindowGUI, - MorePanels, - Cameras, - FAQ, - ExportRaw, - IRC, - YouTubeChat, - CameraOptions, - StencilsDisabled, - AdvancedTools, - FloatingPanelsMode, - StraightEdgeMeterDisplay, - Sketchbook, - ExportAll, - Lights, - SaveAndUpload, - StraightEdgeShape, - SaveOptions, - SketchbookMenu, - Disco, - ViewOnlineGallery, - CancelUpload, - AdvancedPanelsToggle, - Music, - Duplicate, - ToggleGroupStrokesAndWidgets, - SaveModel, - ViewPolyPage, - ViewPolyGallery, - ExportListed, - RenderCameraPath, - ToggleProfiling, - DoAutoProfile, - DoAutoProfileAndQuit, - ToggleSettings, - SummonMirror, - InvertSelection, - SelectAll, - FlipSelection, - ToggleBrushLab, - ReleaseNotes, - ToggleCameraPostEffects, - ToggleWatermark, - AccountInfo, - // LoadConfirmUnsaved -> LoadWaitOnDownload -> LoadConfirmComplex -> LoadComplexHigh -> Load - LoadConfirmUnsaved, - LoadConfirmComplex, - MemoryWarning, - MemoryExceeded, - ViewLastUpload, - LoadConfirmComplexHigh, - ShowTos, - ShowPrivacy, - ShowQuestSideLoading, - AshleysSketch, - UnloadReferenceImageCatalog, - SaveOnLocalChanges, - ToggleCameraPathVisuals, - ToggleCameraPathPreview, - DeleteCameraPath, - RecordCameraPath, - SelectCameraPath, - ToggleAutosimplification, - ShowGoogleDrive, - GoogleDriveSync_Folder, // iParam1: folder id as DriveSync.SyncedFolderType - GoogleDriveSync, - LoginToGenericCloud, // iParam1: Cloud enum - UploadToGenericCloud, // iParam1: Cloud enum - LoadWaitOnDownload, - SignOutConfirm, - ReadOnlyNotice, - ShowContribution, - - // Open Brush Reserved Enums 1000-1999 - LanguagePopup = 1000, - MultiplayerTogglePanel = 1001, - MultiplayerPanelOptions = 1002, // iParam1: Popup options - MultiplayerJoinRoom = 1004, - EditMultiplayerRoomName = 1005, - MultiplayerLeaveRoom = 1006, - MultiplayerConnect = 1007, - MultiplayerDisconnect = 1008, - EditMultiplayerNickName = 1009, - - RenameSketch = 5200, - OpenLayerOptionsPopup = 5201, - RenameLayer = 5202, - OpenDirectorChooserPopup = 5800, - OpenScriptsCommandsList = 6000, - OpenScriptsList = 6001, - OpenExampleScriptsList = 6002, - SymmetryTwoHanded = 6003, - OpenColorOptionsPopup = 7000, - ChangeSnapAngle = 8000, - MergeBrushStrokes = 10000, - RepaintOptions = 11500, - OpenNumericInputPopup = 12000 - } - - public enum ControlsType - { - KeyboardMouse, - SixDofControllers, - ViewingOnly - } - - public enum DraftingVisibilityOption - { - Visible, - Transparent, - Hidden - } - - public enum InputState - { - Standard, - Pan, - Rotation, - HeadLock, - ControllerLock, - PushPull, - BrushSize, - Save, - Load, - Num - } - - public enum LoadSpeed - { - Normal = -1, - Quick = 1, - } - - const float kControlPointHistoryMaxTime = 0.1f; - - class GazeResult - { - public bool m_HitWithGaze; - public bool m_HitWithController; - // ReSharper disable once NotAccessedField.Local - public bool m_WithinView; - public float m_ControllerDistance; - public Vector3 m_GazePosition; - public Vector3 m_ControllerPosition; - public InputManager.ControllerName m_ControllerName; - } - - class GrabWidgetControllerInfo - { - public InputManager.ControllerName m_Name; - /// Transform of controller at the time the grab started - public TrTransform m_BaseControllerXf; - /// "local" transform of widget (relative to controller), at the time the grab started. - /// The widget isn't parented to the controller, but if it were, this would be its transform. - public TrTransform m_BaseWidgetXf_LS; - } - - struct GrabWidgetHoldPoint - { - // ReSharper disable once NotAccessedField.Local - public InputManager.ControllerName m_Name; - public float m_BirthTime; - public Vector3 m_Pos; // where controller is holding the widget - public Quaternion m_Rot; - } - - class InputStateConfig - { - public bool m_AllowDrawing; - public bool m_AllowMovement; - public bool m_ShowGizmo; - } - - enum FadeState - { - None, - FadeOn, - FadeOff - } - - enum GrabWidgetState - { - None, - OneHand, - TwoHands - } - - enum GrabWorldState - { - Normal, - ResettingTransform, - ResetDone - } - - private enum WorldTransformResetState - { - Default, - Requested, - FadingToBlack, - FadingToScene, - } - - enum RotationType - { - All, - RollOnly - } - - enum GrabIntersectionState - { - RequestIntersections, - ReadBrush, - ReadWand - } - - // ------------------------------------------------------------ - // Inspector data (read-only even if public) - // ------------------------------------------------------------ - - public GameObject m_SketchSurface; - public SketchMemoryScript.PlaybackMode m_DefaultSketchPlaybackMode; - public float m_GazeMaxAngleFromPointing = 85.0f; - public float m_GazeMaxAngleFacingToForward = 80.0f; - - [SerializeField] bool m_AtlasIconTextures; - - [SerializeField] SaveIconTool m_SaveIconTool; - [SerializeField] DropCamWidget m_DropCam; - [SerializeField] string m_CreditsSketchFilename; - [SerializeField] string m_AshleysSketchFilename; - [SerializeField] float m_DefaultSketchLoadSpeed; - [SerializeField] GameObject m_TransformGizmoPrefab; - - [SerializeField] GameObject m_RotationIconPrefab; - [SerializeField] float m_GazeMaxAngleFromFacing = 70.0f; - [SerializeField] float m_GazeMaxDistance = 10.0f; - [SerializeField] float m_GazeControllerPointingDistance; - [SerializeField] float m_GazePanelDectivationDelay = 0.25f; - - [SerializeField] GameObject m_UIReticle; - [SerializeField] GameObject m_UIReticleMobile; - [SerializeField] GameObject m_UIReticleSixDofController; - - [SerializeField] float m_DoubleTapWindow; - [SerializeField] float m_PushPullScale; - [SerializeField] RotationCursorScript m_RotationCursor; - [SerializeField] float m_RotationMaxAngle; - - [SerializeField] float m_RotationScalar; - [SerializeField] float m_RotationRollScalar; - [SerializeField] float m_PanScalar; - - [SerializeField] float m_AdjustToolSizeScalar; - - [SerializeField] GameObject m_IRCChatPrefab; - [SerializeField] GameObject m_YouTubeChatPrefab; - [SerializeField] GameObject m_Decor; - [SerializeField] BaseTool.ToolType m_InitialTool = BaseTool.ToolType.SketchSurface; - [SerializeField] string m_ReleaseNotesURL; - [SerializeField] string m_HelpCenterURL; - [SerializeField] string m_ThirdPartyNoticesURL; - [SerializeField] string m_TosURL; - [SerializeField] string m_PrivacyURL; - [SerializeField] string m_QuestSideLoadingHowToURL; - - [Multiline] - [SerializeField] string m_ContributionPromoText; - [SerializeField] string m_ContributionURL; - - [SerializeField] float m_WorldTransformMinScale = .1f; - [SerializeField] float m_WorldTransformMaxScale = 10.0f; - - [Header("Undo/Redo Hold")] - [SerializeField] float m_UndoRedoHold_DurationBeforeStart; - [SerializeField] float m_UndoRedoHold_RepeatInterval; - - [Header("Pin Cushion")] - [SerializeField] GameObject m_PinCushionPrefab; - - [Header("Grabbing and tossing")] - [SerializeField] float m_GrabWorldFadeSpeed = 8.0f; - [SerializeField] Color m_GrabWorldGridColor = new Color(0.0f, 1.0f, 1.0f, 0.2f); - [SerializeField] ControllerGrabVisuals m_ControllerGrabVisuals; - [SerializeField] float m_WidgetGpuIntersectionRadius; - - [Header("Saving")] - [SerializeField] int m_NumStrokesForSaveIcon = 50; - - [NonSerialized] public Color m_GrabHighlightActiveColor; - [NonSerialized] public bool m_DisableWorldGrabbing = false; - - /// Throwing an object faster than this means it's a "toss". Units are m/s. - public float m_TossThresholdMeters = 3f; - /// Angular motion contributes more towards the toss velocity the larger the object is; - /// or rather, the larger the distance between the grab point and the object's center. - /// To prevent large objects from being too-easily-tossed, bound that distance. - public float m_TossMaxPivotDistMeters = 0.33f; - - // ------------------------------------------------------------ - // Internal data - // ------------------------------------------------------------ - - private SketchSurfacePanel m_SketchSurfacePanel; - private SketchMemoryScript.PlaybackMode m_SketchPlaybackMode; - private GameObject m_TransformGizmo; - private TransformGizmoScript m_TransformGizmoScript; - private GameObject m_RotationIcon; - private float m_MouseDeltaX; - private float m_MouseDeltaY; - private float m_MouseDeltaXScaled; - private float m_MouseDeltaYScaled; - private float m_PositionOffsetResetTapTime; - private bool m_EatToolScaleInput; - - private PanelManager m_PanelManager; - private WidgetManager m_WidgetManager; - private PinCushion m_PinCushion; - private bool m_EatPinCushionInput; - - // This is the gaze that was used to compute m_CurrentGazeHitPoint. - // It is not a general substitute for ViewpointScript.Gaze. - private Ray m_CurrentGazeRay; - private Quaternion m_CurrentHeadOrientation; - private GazeResult[] m_GazeResults; - private int m_CurrentGazeObject; - private bool m_EatInputGazeObject; - private Vector3 m_CurrentGazeHitPoint; - private Ray m_GazeControllerRay; - private Ray m_GazeControllerRayActivePanel; - private bool m_ForcePanelActivation = false; - private float m_GazePanelDectivationCountdown; - private bool m_PanelsVisibilityRequested; - - // Previously Experimental-Model only - private bool m_HeadOffset; - - float m_UndoHold_Timer; - float m_RedoHold_Timer; - - // Grab world member variables. - struct GrabState - { - public InputManager.ControllerName name; - public TrTransform grabTransform; - public bool grabbingWorld; - public bool grabbingGroup; - public bool startedGrabInsideWidget; - public bool eatInput; - private GrabWidget lastWidgetIntersect; - - public void SetHadBestGrabAndTriggerHaptics(GrabWidgetData data) - { - bool dormant = WidgetManager.m_Instance.WidgetsDormant; - if (data != null && !data.m_WidgetScript.AllowDormancy) - { - dormant = false; - } - GrabWidget newInsideWidget = (data != null && !dormant) ? data.m_WidgetScript : null; - if (sm_enableGrabHaptics && newInsideWidget != lastWidgetIntersect) - { - // state changed - if (newInsideWidget != null) - { - // transitioning in - InputManager.m_Instance.TriggerHaptics(name, data.m_WidgetScript.HapticDuration); - } - else - { - // transitioning out - InputManager.m_Instance.TriggerHaptics(name, 0.03f); - } - } - lastWidgetIntersect = newInsideWidget; - } - - public void ClearInsideWidget() - { - lastWidgetIntersect = null; - } - } - private GrabState m_GrabBrush = new GrabState { name = InputManager.ControllerName.Brush }; - private GrabState m_GrabWand = new GrabState { name = InputManager.ControllerName.Wand }; - - private WorldTransformResetState m_WorldTransformResetState = WorldTransformResetState.Default; - private TrTransform m_WorldTransformResetXf = TrTransform.identity; // set when reset requested - private GrabWorldState m_GrabWorldState = GrabWorldState.Normal; - private float m_WorldTransformFadeAmount; - private bool m_AllowWorldTransformLastFrame = false; - private bool m_WorldBeingGrabbed; - private TrTransform m_xfDropCamReset_RS; - - struct GpuIntersectionResult - { - public GpuIntersector.FutureModelResult result; - public List resultList; - } - private Queue m_BrushResults; - private Queue m_WandResults; - private int m_WidgetGpuIntersectionLayer; - - private GrabWidget m_CurrentGrabWidget; - private GrabWidget m_MaybeDriftingGrabWidget; // use only to clear drift - - // References to widgets, cached in the UpdateGrab_None, to be used by helper functions - // for the remainder of the frame. - private GrabWidget m_PotentialGrabWidgetBrush; - private GrabWidget m_PotentialGrabWidgetWand; - - // Flags for the explaining if the m_PotentialGrabWidget_x widgets are able to be interacted with. - // Cached in the UpdateGrab_None, used for the remainder of the frame. - private bool m_PotentialGrabWidgetBrushValid; - private bool m_PotentialGrabWidgetWandValid; - - // References to widget metadata, cached in UpdateGrab_None, to be re-used on "off frames" - // when the GPU intersector is not refreshing the nearest widget to the respective controller. - private GrabWidgetData m_BackupBrushGrabData; - private GrabWidgetData m_BackupWandGrabData; - - private GrabWidgetState m_GrabWidgetState; - private GrabWidgetControllerInfo m_GrabWidgetOneHandInfo; - private TrTransform m_GrabWidgetTwoHandBrushPrev; - private TrTransform m_GrabWidgetTwoHandWandPrev; - private Queue m_GrabWidgetHoldHistory; - - private Quaternion m_RotationOrigin; - private Vector2 m_RotationCursorOffset; - - private bool m_RotationRollActive; - private float m_RotationResetTapTime; - - private RotationType m_CurrentRotationType; - private bool m_AutoOrientAfterRotation; - - private Vector3 m_SurfaceForward; - private Vector3 m_SurfaceRight; - private Vector3 m_SurfaceUp; - - private Vector3 m_SurfaceLockOffset; - private Vector3 m_SurfaceLockBaseSurfacePosition; - private Vector3 m_SurfaceLockBaseControllerPosition; - private Quaternion m_SurfaceLockBaseHeadRotation; - private Quaternion m_SurfaceLockBaseControllerRotation; - private Quaternion m_SurfaceLockBaseSurfaceRotation; - private InputManager.ControllerName m_SurfaceLockActingController; - private float m_SurfaceLockControllerBaseScalar; - private float m_SurfaceLockControllerScalar; - - private bool m_PositioningPanelWithHead; - private Quaternion m_PositioningPanelBaseHeadRotation; - private Vector3 m_PositioningPanelOffset; - private float m_PositioningTimer; - private float m_PositioningSpeed; - - private DraftingVisibilityOption m_DraftingVisibility = DraftingVisibilityOption.Visible; - - private Vector3 m_SketchOrigin; - - private ControlsType m_ControlsType; - private GrabWidget m_IRCChatWidget; - private GrabWidget m_YouTubeChatWidget; - private MultiCamCaptureRig m_MultiCamCaptureRig; - private CameraPathCaptureRig m_CameraPathCaptureRig; - - private bool m_ViewOnly = false; - - private InputState m_CurrentInputState; - private InputStateConfig[] m_InputStateConfigs; - - private GrabIntersectionState m_CurrentGrabIntersectionState; - - private float m_WorldTransformSpeedSmoothed; - - // ------------------------------------------------------------ - // Properties and events - // ------------------------------------------------------------ - - public MultiCamCaptureRig MultiCamCaptureRig - { - get { return m_MultiCamCaptureRig; } - } - - public CameraPathCaptureRig CameraPathCaptureRig - { - get { return m_CameraPathCaptureRig; } - } - - public ControllerGrabVisuals ControllerGrabVisuals - { - get { return m_ControllerGrabVisuals; } - } - - public SketchMemoryScript.PlaybackMode SketchPlaybackMode - { - get { return m_SketchPlaybackMode; } - set { m_SketchPlaybackMode = value; } - } - - public Transform m_Canvas - { - get { return App.Instance.m_CanvasTransform; } - } - - public ControlsType ActiveControlsType - { - get { return m_ControlsType; } - set { m_ControlsType = value; } - } - - public float WorldTransformMinScale - { - get - { - return App.UserConfig.Flags.UnlockScale ? m_WorldTransformMinScale * 0.01f : - m_WorldTransformMinScale; - } - } - - public float WorldTransformMaxScale - { - get - { - return App.UserConfig.Flags.UnlockScale ? m_WorldTransformMaxScale * 10.0f : - m_WorldTransformMaxScale; - } - } - - public void SetInitialTool(BaseTool.ToolType rType) - { - m_InitialTool = rType; - } - - public void SetInFreePaintMode(bool bFreePaint) - { - m_SketchSurfacePanel.SetInFreePaintMode(bFreePaint); - } - - public float GazeMaxDistance - { - get { return m_GazeMaxDistance; } - } - - public InputManager.ControllerName OneHandGrabController - { - get - { - return m_CurrentGrabWidget != null ? - m_GrabWidgetOneHandInfo.m_Name : - InputManager.ControllerName.None; - } - } - - public InputManager.ControllerName PotentialOneHandGrabController(GrabWidget widget) - { - if (m_PotentialGrabWidgetBrush == widget) - { - return InputManager.ControllerName.Brush; - } - else if (m_PotentialGrabWidgetWand == widget) - { - return InputManager.ControllerName.Wand; - } - return OneHandGrabController; - } - - public Vector3 GetSurfaceForward() { return m_SurfaceForward; } - public Vector3 GetSurfaceUp() { return m_SurfaceUp; } - public Vector3 GetSurfaceRight() { return m_SurfaceRight; } - public Vector3 GetSketchOrigin() { return m_SketchOrigin; } - public float GetDefaultSketchLoadSpeed() { return m_DefaultSketchLoadSpeed; } - public Quaternion GetCurrentHeadOrientation() { return m_CurrentHeadOrientation; } - public Vector3 GetUIReticlePos() { return m_UIReticle.transform.position; } - public Vector3 GetSweetSpotPos() { return m_PanelManager.m_SweetSpot.transform.position; } - public void SetSketchOrigin(Vector3 vOrigin) { m_SketchOrigin = vOrigin; } - - public void EatGazeObjectInput() - { - m_EatInputGazeObject = true; - m_GazePanelDectivationCountdown = 0.0f; - PointerManager.m_Instance.EatLineEnabledInput(); - SketchSurfacePanel.m_Instance.EatToolsInput(); - } - public void EatToolScaleInput() { m_EatToolScaleInput = true; } - public void EatGrabInput() - { - m_GrabWand.eatInput = true; - m_GrabBrush.eatInput = true; - } - - public bool ShouldRespondToPadInput(InputManager.ControllerName name) - { - if (name == InputManager.ControllerName.Brush && m_CurrentGazeObject != -1) - { - return m_PanelManager.GetPanel(m_CurrentGazeObject).BrushPadAnimatesOnHover(); - } - return !m_EatToolScaleInput && SketchSurfacePanel.m_Instance.CanAdjustToolSize(); - } - public void ForcePanelActivation(bool bForce) - { - m_ForcePanelActivation = bForce; - if (m_ForcePanelActivation) - { - m_GazePanelDectivationCountdown = m_GazePanelDectivationDelay; - } - } - public bool IsUserInteractingWithUI() - { - return (m_CurrentGazeObject != -1) || (m_GazePanelDectivationCountdown > 0.0f); - } - public bool IsUIBlockingUndoRedo() - { - if (m_CurrentGazeObject != -1) - { - return m_PanelManager.GetPanel(m_CurrentGazeObject).UndoRedoBlocked(); - } - return false; - } - public bool IsUserAbleToInteractWithAnyWidget() - { - return IsUserInteractingWithAnyWidget() || - (m_PotentialGrabWidgetBrush != null && m_PotentialGrabWidgetBrushValid) || - (m_PotentialGrabWidgetWand != null && m_PotentialGrabWidgetWandValid); - } - public bool IsUserInteractingWithAnyWidget() { return m_CurrentGrabWidget != null; } - public bool IsUserGrabbingAnyPanel() - { - return (m_CurrentGrabWidget != null && m_CurrentGrabWidget is PanelWidget); - } - public bool IsUsersBrushIntersectingWithSelectionWidget() - { - return (m_PotentialGrabWidgetBrush != null && - m_PotentialGrabWidgetBrushValid && - m_PotentialGrabWidgetBrush is SelectionWidget); - } - public bool IsUserIntersectingWithSelectionWidget() - { - return IsUsersBrushIntersectingWithSelectionWidget() || - (m_PotentialGrabWidgetWand != null && - m_PotentialGrabWidgetWandValid && - m_PotentialGrabWidgetWand is SelectionWidget); - } - public bool IsUserInteractingWithSelectionWidget() - { - return (m_CurrentGrabWidget != null && m_CurrentGrabWidget is SelectionWidget); - } - - public bool IsUserGrabbingWorld() { return m_GrabWand.grabbingWorld || m_GrabBrush.grabbingWorld; } - public bool IsUserGrabbingWorldWithBrushHand() { return m_GrabBrush.grabbingWorld; } - public bool IsUserTransformingWorld() { return m_GrabWand.grabbingWorld && m_GrabBrush.grabbingWorld; } - public float GetGazePanelActivationRatio() { return m_GazePanelDectivationCountdown / m_GazePanelDectivationDelay; } - public bool IsCurrentGrabWidgetPinned() { return IsUserInteractingWithAnyWidget() && m_CurrentGrabWidget.Pinned; } - public bool CanCurrentGrabWidgetBePinned() { return IsUserInteractingWithAnyWidget() && m_CurrentGrabWidget.AllowPinning; } - public bool DidUserGrabWithBothInside() { return m_GrabBrush.startedGrabInsideWidget && m_GrabWand.startedGrabInsideWidget; } - public bool IsUserGrabbingWidget(GrabWidget widget) { return widget == m_CurrentGrabWidget; } - public bool IsUserTwoHandGrabbingWidget() { return m_GrabWidgetState == GrabWidgetState.TwoHands; } - public bool IsPinCushionShowing() { return m_PinCushion.IsShowing(); } - public bool IsUserLookingAtPanel(BasePanel panel) - { - return m_CurrentGazeObject > -1 && - m_PanelManager.GetAllPanels()[m_CurrentGazeObject].m_Panel == panel; - } - - public SaveIconTool GetSaveIconTool() - { - return m_SaveIconTool; - } - - public DropCamWidget GetDropCampWidget() - { - return m_DropCam; - } - - public bool IsGrabWorldStateStable() - { - return m_GrabWorldState == GrabWorldState.Normal; - } - - // Internal: modify Coords.ScenePose or Coords.CanvasPose depending on the - // state of m_InTransformCanvasMode - TrTransform GrabbedPose - { - get - { - return App.Scene.Pose; - } - set - { - App.Scene.Pose = value; - } - } - - public Transform GazeObjectTransform() - { - if (m_CurrentGazeObject != -1) - { - return m_PanelManager.GetPanel(m_CurrentGazeObject).transform; - } - return null; - } - - public void ForceShowUIReticle(bool bVisible) - { - m_UIReticle.SetActive(bVisible); - } - - public void SetUIReticleTransform(Vector3 vPos, Vector3 vForward) - { - m_UIReticle.transform.position = vPos; - m_UIReticle.transform.forward = vForward; - } - - public bool AtlasIconTextures - { - get { return m_AtlasIconTextures; } - } - - public IconTextureAtlas IconTextureAtlas - { - get { return GetComponent(); } - } - public GrabWidget CurrentGrabWidget => m_CurrentGrabWidget; - - void DismissPopupOnCurrentGazeObject(bool force) - { - if (m_CurrentGazeObject != -1) - { - m_PanelManager.GetPanel(m_CurrentGazeObject).CloseActivePopUp(force); - } - } - - void Awake() - { - m_Instance = this; - - BrushController.m_Instance.BrushSetToDefault += OnBrushSetToDefault; - - IconTextureAtlas.Init(); - - m_MultiCamCaptureRig = GetComponentInChildren(true); - m_MultiCamCaptureRig.Init(); - - m_CameraPathCaptureRig = GetComponentInChildren(true); - m_CameraPathCaptureRig.Init(); - - m_SketchSurfacePanel = m_SketchSurface.GetComponent(); - m_PanelManager = GetComponent(); - m_PanelManager.Init(); - InitGazePanels(); - - m_WidgetManager = GetComponent(); - m_WidgetManager.Init(); - - m_InputStateConfigs = new InputStateConfig[(int)InputState.Num]; - for (int i = 0; i < (int)InputState.Num; ++i) - { - m_InputStateConfigs[i] = new InputStateConfig(); - m_InputStateConfigs[i].m_AllowDrawing = false; - m_InputStateConfigs[i].m_AllowMovement = true; - m_InputStateConfigs[i].m_ShowGizmo = false; - } - - m_InputStateConfigs[(int)InputState.Standard].m_AllowDrawing = true; - m_InputStateConfigs[(int)InputState.Pan].m_AllowDrawing = true; - m_InputStateConfigs[(int)InputState.HeadLock].m_AllowDrawing = true; - m_InputStateConfigs[(int)InputState.ControllerLock].m_AllowDrawing = true; - m_InputStateConfigs[(int)InputState.PushPull].m_AllowDrawing = true; - - m_InputStateConfigs[(int)InputState.Pan].m_AllowMovement = false; - m_InputStateConfigs[(int)InputState.Rotation].m_AllowMovement = false; - m_InputStateConfigs[(int)InputState.ControllerLock].m_AllowMovement = false; - m_InputStateConfigs[(int)InputState.PushPull].m_AllowMovement = false; - m_InputStateConfigs[(int)InputState.BrushSize].m_AllowMovement = false; - - m_InputStateConfigs[(int)InputState.Pan].m_ShowGizmo = true; - m_InputStateConfigs[(int)InputState.Rotation].m_ShowGizmo = true; - m_InputStateConfigs[(int)InputState.HeadLock].m_ShowGizmo = true; - m_InputStateConfigs[(int)InputState.PushPull].m_ShowGizmo = true; - - m_CurrentGazeRay = new Ray(Vector3.zero, Vector3.forward); - m_GazeControllerRay = new Ray(Vector3.zero, Vector3.forward); - m_GazeControllerRayActivePanel = new Ray(Vector3.zero, Vector3.forward); - - m_GrabWidgetHoldHistory = new Queue(); - m_GrabWidgetOneHandInfo = new GrabWidgetControllerInfo(); - - // Initialize world grip members. - m_GrabBrush.grabTransform = TrTransform.identity; - m_GrabWand.grabTransform = TrTransform.identity; - - m_BrushResults = new Queue(); - m_WandResults = new Queue(); - m_WidgetGpuIntersectionLayer = LayerMask.NameToLayer("GpuIntersection"); - m_CurrentGrabIntersectionState = GrabIntersectionState.RequestIntersections; - } - - public void InitGazePanels() - { - // Find all gaze panels. - int iNumGazePanels = m_PanelManager.GetAllPanels().Count; - m_GazeResults = new GazeResult[iNumGazePanels]; - for (int i = 0; i < iNumGazePanels; ++i) - { - m_GazeResults[i] = new GazeResult(); - m_GazeResults[i].m_HitWithGaze = false; - m_GazeResults[i].m_HitWithController = false; - m_GazeResults[i].m_WithinView = false; - m_GazeResults[i].m_GazePosition = new Vector3(); - } - } - - public void OnEnable() - { - // This needs to run before other tools initialize, which is why it's running in OnEnable. - // The sequence is Awake(), OnEnable(), Start(). - if (App.VrSdk.GetControllerDof() == VrSdk.DoF.Six) - { - SetInFreePaintMode(true); - SetInitialTool(BaseTool.ToolType.FreePaintTool); - } - } - - void Start() - { - m_TransformGizmo = (GameObject)Instantiate(m_TransformGizmoPrefab); - m_TransformGizmo.transform.parent = transform; - m_TransformGizmoScript = m_TransformGizmo.GetComponent(); - m_TransformGizmo.SetActive(false); - - m_RotationIcon = (GameObject)Instantiate(m_RotationIconPrefab); - m_RotationIcon.transform.position = m_SketchSurface.transform.position; - m_RotationIcon.transform.parent = m_SketchSurface.transform; - m_RotationIcon.SetActive(false); - - GameObject pinCushionObj = (GameObject)Instantiate(m_PinCushionPrefab); - m_PinCushion = pinCushionObj.GetComponent(); - - m_PositionOffsetResetTapTime = 0.0f; - - m_UndoHold_Timer = m_UndoRedoHold_DurationBeforeStart; - m_RedoHold_Timer = m_UndoRedoHold_DurationBeforeStart; - - m_AutoOrientAfterRotation = true; - m_RotationCursor.gameObject.SetActive(false); - - ResetGrabbedPose(); - m_SketchOrigin = m_SketchSurface.transform.position; - - m_PanelManager.InitPanels(m_ControlsType == ControlsType.SixDofControllers); - - m_UIReticleMobile.SetActive(m_ControlsType == ControlsType.ViewingOnly); - m_UIReticleSixDofController.SetActive(m_ControlsType != ControlsType.ViewingOnly); - - m_PositioningPanelWithHead = false; - m_PositioningSpeed = 16.0f; - - m_CurrentRotationType = RotationType.All; - m_RotationResetTapTime = 0.0f; - - m_CurrentInputState = InputState.Standard; - - m_SketchSurfacePanel.EnableSpecificTool(m_InitialTool); - m_SurfaceLockControllerBaseScalar = m_SketchSurfacePanel.m_PanelSensitivity; - - //after initializing, start with gaze objects hidden - m_CurrentGazeObject = -1; - m_EatInputGazeObject = false; - - // Previously set to 0 in experimental builds - int hidePanelsDelay = 1; - - StartCoroutine(DelayedHidePanels(hidePanelsDelay)); - - m_DropCam.Show(false); - - m_GrabWidgetState = GrabWidgetState.None; - - UpdateDraftingVisibility(); - - m_DisableWorldGrabbing = false; - } - - private IEnumerator DelayedHidePanels(int frames) - { - int stall = frames; - while (stall-- > 0) - { - yield return null; - } - - m_PanelManager.HidePanelsForStartup(); - RequestPanelsVisibility(false); - } - - void Update() - { - // TODO: we need to figure out what transform to pass in here! - // Maybe best _just for now_ to use the scene transform? - TrTransform scenePose = App.Scene.Pose; - Shader.SetGlobalMatrix("xf_CS", scenePose.ToMatrix4x4()); - Shader.SetGlobalMatrix("xf_I_CS", scenePose.inverse.ToMatrix4x4()); - } - - void LateUpdate() - { - // Gracefully exits if we're not recording a video. - VideoRecorderUtils.SerializerNewUsdFrame(); - } - - public bool IsFreepaintToolReady() - { - return - !m_PinCushion.IsShowing() && - !PointerManager.m_Instance.IsStraightEdgeProxyActive() && - !InputManager.m_Instance.ControllersAreSwapping() && - (m_SketchSurfacePanel.IsSketchSurfaceToolActive() || - (m_SketchSurfacePanel.GetCurrentToolType() == BaseTool.ToolType.FreePaintTool)) - ; - } - - public void UpdateControls() - { - UnityEngine.Profiling.Profiler.BeginSample("SketchControlsScript.UpdateControls"); - m_SketchSurfacePanel.m_UpdatedToolThisFrame = false; - - // Verify controllers are available and prune state if they're not. - if ((App.VrSdk.GetControllerDof() == VrSdk.DoF.Six && - App.VrSdk.IsInitializingUnityXR) && App.VrSdk.IsHmdInitialized()) - { - m_PanelManager.SetVisible(false); - PointerManager.m_Instance.RequestPointerRendering(false); - return; - } - - //mouse movement - Vector2 mv = InputManager.m_Instance.GetMouseMoveDelta(); - m_MouseDeltaX = mv.x; - m_MouseDeltaY = mv.y; - - UpdateGazeObjectsAnimationState(); - UpdateCurrentGazeRay(); - m_SketchSurfacePanel.SetBacksideActive(m_CurrentGazeRay.origin); - m_PanelManager.UpdatePanels(); - - m_MouseDeltaXScaled = m_MouseDeltaX * GetAppropriateMovementScalar(); - m_MouseDeltaYScaled = m_MouseDeltaY * GetAppropriateMovementScalar(); - - //this is used for one-shot inputs that don't require state and do not change state - UpdateBaseInput(); - - UpdatePinCushionVisibility(); - - //if the pointer manager is processing, we don't want to respond to input - if (!PointerManager.m_Instance.IsMainPointerProcessingLine()) - { - - //see if we're grabbing a widget - UpdateGrab(); - - //see if we're looking at a gaze object - RefreshCurrentGazeObject(); - - // Tools allowed when widgets aren't grabbed. - bool bWidgetGrabOK = m_GrabWidgetState == GrabWidgetState.None; - - // If we don't have a widget held and we're not grabbing the world with the brush controller, - // update tools. - if (bWidgetGrabOK && !m_GrabBrush.grabbingWorld) - { - if (m_CurrentGazeObject != -1 && !m_WorldBeingGrabbed) - { - UpdateActiveGazeObject(); - - // Allow for standard input (like Undo / Redo) even when gazing at a panel. - if (m_CurrentInputState == InputState.Standard) - { - UpdateStandardInput(); - } - } - else - { - //standard input, no gaze object - if (m_InputStateConfigs[(int)m_CurrentInputState].m_AllowMovement) - { - m_SketchSurfacePanel.UpdateReticleOffset(m_MouseDeltaX, m_MouseDeltaY); - } - - switch (m_CurrentInputState) - { - case InputState.Standard: - UpdateStandardInput(); - break; - case InputState.Pan: - UpdatePanInput(); - break; - case InputState.Rotation: - UpdateRotationInput(); - break; - case InputState.HeadLock: - UpdateHeadLockInput(); - break; - case InputState.ControllerLock: - UpdateControllerLock(); - break; - case InputState.PushPull: - UpdatePushPullInput(); - break; - case InputState.Save: - UpdateSaveInput(); - break; - case InputState.Load: - UpdateLoadInput(); - break; - } - - //keep pointer locked in the right spot, even if it's hidden - if (m_SketchSurfacePanel.ActiveTool.LockPointerToSketchSurface()) - { - Vector3 vPointerPos = Vector3.zero; - Vector3 vPointerForward = Vector3.zero; - m_SketchSurfacePanel.GetReticleTransform(out vPointerPos, out vPointerForward, - (m_ControlsType == ControlsType.ViewingOnly)); - PointerManager.m_Instance.SetMainPointerPosition(vPointerPos); - PointerManager.m_Instance.SetMainPointerForward(vPointerForward); - } - - m_SketchSurfacePanel.AllowDrawing(m_InputStateConfigs[(int)m_CurrentInputState].m_AllowDrawing); - m_SketchSurfacePanel.UpdateCurrentTool(); - - PointerManager.m_Instance.AllowPointerPreviewLine(IsFreepaintToolReady()); - //keep transform gizmo at sketch surface pos - m_TransformGizmo.transform.position = m_SketchSurface.transform.position; - bool bGizmoActive = m_InputStateConfigs[(int)m_CurrentInputState].m_ShowGizmo && m_SketchSurfacePanel.ShouldShowTransformGizmo(); - m_TransformGizmo.SetActive(bGizmoActive); - } - } - } - - // Update any transition to a scene transform reset. - UpdateWorldTransformReset(); - - //update our line after all input and tools have chimed in on the state of it - PointerManager.m_Instance.UpdateLine(); - UnityEngine.Profiling.Profiler.EndSample(); - } - - public void UpdateControlsPostIntro() - { - m_PanelManager.UpdatePanels(); - UpdateCurrentGazeRay(); - UpdateGazeObjectsAnimationState(); - RefreshCurrentGazeObject(); - UpdateSwapControllers(); - if (m_CurrentGazeObject > -1) - { - UpdateActiveGazeObject(); - } - } - - public void UpdateControlsForLoading() - { - UpdateCurrentGazeRay(); - m_PanelManager.UpdatePanels(); - UpdateGazeObjectsAnimationState(); - UpdateGrab(); - UpdateWorldTransformReset(); - - if (m_GrabWidgetState == GrabWidgetState.None && m_CurrentGazeObject == -1 && - m_SketchSurfacePanel.ActiveTool.AvailableDuringLoading() && - !m_GrabBrush.grabbingWorld) - { - m_SketchSurfacePanel.UpdateCurrentTool(); - } - } - - public void UpdateControlsForReset() - { - UpdateGrab(); - UpdateCurrentGazeRay(); - UpdatePinCushionVisibility(); - m_PanelManager.UpdatePanels(); - UpdateGazeObjectsAnimationState(); - PointerManager.m_Instance.UpdateLine(); - } - - public void UpdateControlsForUploading() - { - UpdateCurrentGazeRay(); - UpdatePinCushionVisibility(); - m_PanelManager.UpdatePanels(); - UpdateGazeObjectsAnimationState(); - } - - public void UpdateControlsForMemoryExceeded() - { - UpdateGrab(); - m_SketchSurfacePanel.m_UpdatedToolThisFrame = false; - m_PanelManager.UpdatePanels(); - UpdateCurrentGazeRay(); - UpdateGazeObjectsAnimationState(); - RefreshCurrentGazeObject(); - if (m_CurrentGazeObject > -1) - { - UpdateActiveGazeObject(); - } - } - - void UpdatePinCushionVisibility() - { - // If the pin cushion is showing and the user cancels, eat the input. - // if (m_PinCushion.IsShowing()) - // { - // if (InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Activate) || - // InputManager.Brush.GetControllerGrip() || - // InputManager.Wand.GetControllerGrip() || - // IsUserInteractingWithAnyWidget() || - // IsUserInteractingWithUI()) - // { - // m_EatPinCushionInput = true; - // } - // } - - // If our tool wants the input blocked, maintain the input eat state until - // after the user has let off input. - if (m_SketchSurfacePanel.ActiveTool.BlockPinCushion() || !CanUsePinCushion()) - { - m_EatPinCushionInput = true; - } - - bool show = - InputManager.m_Instance.GetCommand(InputManager.SketchCommands.ShowPinCushion); - m_PinCushion.ShowPinCushion(show && !m_EatPinCushionInput); - m_EatPinCushionInput = m_EatPinCushionInput && show; - } - - bool CanUsePinCushion() - { - return (m_ControlsType == ControlsType.SixDofControllers) && - m_PanelManager.AdvancedModeActive() && - !InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Activate) && - !InputManager.Brush.GetControllerGrip() && - !InputManager.Wand.GetControllerGrip() && - !IsUserInteractingWithAnyWidget() && - !IsUserInteractingWithUI() && - !m_SketchSurfacePanel.ActiveTool.BlockPinCushion() && - App.Instance.IsInStateThatAllowsPainting(); - } - - void UpdateCurrentGazeRay() - { - var head = ViewpointScript.Head; - m_CurrentGazeRay = new Ray(head.position, head.forward); - m_CurrentHeadOrientation = head.rotation; - - // We use the gaze ray for certain shader effects - like edge falloff. - Shader.SetGlobalVector("_WorldSpaceRootCameraPosition", m_CurrentGazeRay.origin); - bool hasController = m_ControlsType == ControlsType.SixDofControllers; - if (hasController) - { - if (InputManager.Brush.IsTrackedObjectValid) - { - Transform rAttachPoint = InputManager.m_Instance.GetBrushControllerAttachPoint(); - m_GazeControllerRay.direction = rAttachPoint.forward; - m_GazeControllerRay.origin = rAttachPoint.position; - } - else - { - // If the brush controller isn't tracked, put our controller ray out of the way. - float fBig = 9999999.0f; - m_GazeControllerRay.direction = Vector3.one; - m_GazeControllerRay.origin = new Vector3(fBig, fBig, fBig); - } - - m_GazeControllerRayActivePanel.direction = m_GazeControllerRay.direction; - m_GazeControllerRayActivePanel.origin = m_GazeControllerRay.origin; - m_GazeControllerRayActivePanel.origin -= (m_GazeControllerRayActivePanel.direction * 0.5f); - } - } - - public void UpdateGazeObjectsAnimationState() - { - // Are the panels allowed to be visible? - bool isSixDof = m_ControlsType == ControlsType.SixDofControllers; - if ((!isSixDof) || - (InputManager.Wand.IsTrackedObjectValid && - !m_SketchSurfacePanel.ActiveTool.HidePanels() && - !App.Instance.IsLoading())) - { - // Transition panels according to requested visibility. - m_PanelManager.SetVisible(m_PanelsVisibilityRequested); - } - else - { - // Transition out. - m_PanelManager.SetVisible(false); - } - } - - void UpdateBaseInput() - { - UnityEngine.Profiling.Profiler.BeginSample("SketchControlScript.UpdateBaseInput"); - if (m_ControlsType == ControlsType.SixDofControllers) - { - m_PanelManager.UpdateWandOrientationControls(); - } - - //allow tool scaling if we're not drawing and our input device is active - bool bScaleInputActive = InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Scale); - bool bScaleCommandActive = - bScaleInputActive - && !InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Activate) - && m_GrabBrush.grabbingWorld == false - && m_CurrentGazeObject == -1 // free up swipe for use by gaze object - && (m_ControlsType != ControlsType.SixDofControllers || InputManager.Brush.IsTrackedObjectValid) - // TODO:Mikesky - very hacky - && SketchSurfacePanel.m_Instance.ActiveTool.m_Type != BaseTool.ToolType.MultiCamTool; - - if (m_EatToolScaleInput) - { - m_EatToolScaleInput = bScaleInputActive; - } - - if (bScaleCommandActive && !m_EatToolScaleInput) - { - if (m_GrabWidgetState == GrabWidgetState.None) - { - //send scale command down to current tool - m_SketchSurfacePanel.UpdateToolSize( - m_AdjustToolSizeScalar * InputManager.m_Instance.GetAdjustedBrushScrollAmount()); - } - - //ugly, but brush size is becoming not an input state - m_MouseDeltaX = 0.0f; - m_MouseDeltaY = 0.0f; - } - - UpdateSwapControllers(); - UnityEngine.Profiling.Profiler.EndSample(); - } - - void UpdateSwapControllers() - { - // Don't allow controller swap in first run intro. - // Don't allow controller swap if we're grabbing a widget. - // Don't allow controller swap if a Logitech pen is present. - if (!TutorialManager.m_Instance.TutorialActive() && - m_GrabWidgetState == GrabWidgetState.None && - !App.VrSdk.VrControls.LogitechPenIsPresent()) - { - if (InputManager.m_Instance.GetCommandDown(InputManager.SketchCommands.SwapControls)) - { - DoSwapControls(); - } - } - } - - public static void DoSwapControls() - { - InputManager.m_Instance.WandOnRight = !InputManager.m_Instance.WandOnRight; - InputManager.m_Instance.GetControllerBehavior(InputManager.ControllerName.Brush) - .DisplayControllerSwapAnimation(); - InputManager.m_Instance.GetControllerBehavior(InputManager.ControllerName.Wand) - .DisplayControllerSwapAnimation(); - AudioManager.m_Instance.PlayControllerSwapSound( - InputManager.m_Instance.GetControllerPosition(InputManager.ControllerName.Brush)); - } - - void UpdateStandardInput() - { - UnityEngine.Profiling.Profiler.BeginSample("SketchControlScript.UpdateStandardInput"); - //debug keys - if (App.UserConfig.Flags.AdvancedKeyboardShortcuts) - { - var camTool = SketchSurfacePanel.m_Instance.ActiveTool as MultiCamTool; - - if (InputManager.m_Instance.GetKeyboardShortcutDown(InputManager.KeyboardShortcut.SaveNew)) - { - IssueGlobalCommand(GlobalCommands.SaveNew, 1); - } - else if (InputManager.m_Instance.GetKeyboardShortcutDown( - InputManager.KeyboardShortcut.SwitchCamera) && camTool != null) - { - camTool.ExternalObjectNextCameraStyle(); // For monoscopic mode - } - else if (InputManager.m_Instance.GetKeyboardShortcutDown( - InputManager.KeyboardShortcut.ViewOnly)) - { - IssueGlobalCommand(GlobalCommands.ViewOnly); - } - else if (InputManager.m_Instance.GetKeyboardShortcutDown( - InputManager.KeyboardShortcut.ToggleScreenMirroring)) - { - ViewpointScript.m_Instance.ToggleScreenMirroring(); - } - else if (InputManager.m_Instance.GetKeyboardShortcutDown( - InputManager.KeyboardShortcut.PreviousTool)) - { - m_SketchSurfacePanel.PreviousTool(); - } - else if (InputManager.m_Instance.GetKeyboardShortcutDown( - InputManager.KeyboardShortcut.NextTool)) - { - m_SketchSurfacePanel.NextTool(); - } - else if (InputManager.m_Instance.GetKeyboardShortcutDown( - InputManager.KeyboardShortcut.CycleSymmetryMode)) - { - var cur = PointerManager.m_Instance.CurrentSymmetryMode; - var next = (cur == SymmetryMode.None) ? SymmetryMode.SinglePlane - : (cur == SymmetryMode.SinglePlane) ? SymmetryMode.DebugMultiple - : (cur == SymmetryMode.DebugMultiple) ? SymmetryMode.MultiMirror - : (cur == SymmetryMode.MultiMirror) ? SymmetryMode.TwoHanded - : SymmetryMode.None; - PointerManager.m_Instance.CurrentSymmetryMode = next; - } - else if (InputManager.m_Instance.GetKeyboardShortcutDown( - InputManager.KeyboardShortcut.Export)) - { - StartCoroutine(ExportCoroutine()); - } - else if (InputManager.m_Instance.GetKeyboardShortcutDown( - InputManager.KeyboardShortcut.StoreHeadTransform) && - InputManager.m_Instance.GetAnyShift()) - { - Transform head = ViewpointScript.Head; - PlayerPrefs.SetFloat("HeadOffset_localPositionX", head.localPosition.x); - PlayerPrefs.SetFloat("HeadOffset_localPositionY", head.localPosition.y); - PlayerPrefs.SetFloat("HeadOffset_localPositionZ", head.localPosition.z); - PlayerPrefs.SetFloat("HeadOffset_localRotationX", head.localRotation.x); - PlayerPrefs.SetFloat("HeadOffset_localRotationY", head.localRotation.y); - PlayerPrefs.SetFloat("HeadOffset_localRotationZ", head.localRotation.z); - PlayerPrefs.SetFloat("HeadOffset_localRotationW", head.localRotation.w); - } - else if (InputManager.m_Instance.GetKeyboardShortcutDown( - InputManager.KeyboardShortcut.RecallHeadTransform)) - { - Transform head = ViewpointScript.Head; - // Toggle the head offset. - if (m_HeadOffset) - { - // Remove the offset. - Transform originalParent = head.parent; - head.SetParent(head.parent.parent); - GameObject.DestroyImmediate(originalParent.gameObject); - m_HeadOffset = false; - } - else - { - // Add the offset. - GameObject newParent = new GameObject(); - newParent.transform.SetParent(head.parent); - newParent.transform.localPosition = Vector3.zero; - newParent.transform.localRotation = Quaternion.identity; - newParent.transform.localScale = Vector3.one; - head.SetParent(newParent.transform); - TrTransform offsetTransform = TrTransform.TR( - new Vector3( - PlayerPrefs.GetFloat("HeadOffset_localPositionX", 0), - PlayerPrefs.GetFloat("HeadOffset_localPositionY", 1.5f), - PlayerPrefs.GetFloat("HeadOffset_localPositionZ", 0)), - new Quaternion( - PlayerPrefs.GetFloat("HeadOffset_localRotationX", 0), - PlayerPrefs.GetFloat("HeadOffset_localRotationY", 0), - PlayerPrefs.GetFloat("HeadOffset_localRotationZ", 0), - PlayerPrefs.GetFloat("HeadOffset_localRotationW", 1))); - TrTransform originalTransformInverse = TrTransform.FromLocalTransform(head).inverse; - TrTransform newParentTransform = offsetTransform * originalTransformInverse; - newParent.transform.localPosition = newParentTransform.translation; - newParent.transform.localRotation = newParentTransform.rotation; - m_HeadOffset = true; - } - } - else if (InputManager.m_Instance.GetKeyboardShortcutDown( - InputManager.KeyboardShortcut.ToggleLightType)) - { - // Toggle between per-pixel & SH lighting on the secondary directional light - Light secondaryLight = App.Scene.GetLight((1)); - if (LightRenderMode.ForceVertex == secondaryLight.renderMode) - { - secondaryLight.renderMode = LightRenderMode.ForcePixel; - } - else - { - secondaryLight.renderMode = LightRenderMode.ForceVertex; - } - } - else if (InputManager.m_Instance.GetKeyboardShortcutDown( - InputManager.KeyboardShortcut.TossWidget)) - { - m_WidgetManager.TossNearestWidget(); - } - else if (InputManager.m_Instance.GetKeyboardShortcutDown( - InputManager.KeyboardShortcut.Reset)) - { - App.Instance.SetDesiredState(App.AppState.LoadingBrushesAndLighting); - } - else if (InputManager.m_Instance.GetKeyboardShortcutDown( - InputManager.KeyboardShortcut.FlyMode)) - { - SketchSurfacePanel.m_Instance.EnableSpecificTool(BaseTool.ToolType.FlyTool); - } - else if (App.Config.m_ToggleProfileOnAppButton && - (InputManager.Wand.GetVrInputDown(VrInput.Button03) || - InputManager.m_Instance.GetKeyboardShortcutDown( - InputManager.KeyboardShortcut.ToggleProfile))) - { - IssueGlobalCommand(GlobalCommands.ToggleProfiling); - } - } - -#if DEBUG - if (InputManager.m_Instance.GetKeyboardShortcutDown( - InputManager.KeyboardShortcut.CheckStrokes)) - { - bool value = !SketchMemoryScript.m_Instance.m_SanityCheckStrokes; - string feature = "Stroke determinism checking"; - SketchMemoryScript.m_Instance.m_SanityCheckStrokes = value; - OutputWindowScript.m_Instance.CreateInfoCardAtController( - InputManager.ControllerName.Brush, - feature + (value ? ": On" : ": Off")); - } -#endif - - bool hasController = m_ControlsType == ControlsType.SixDofControllers; - var mouse = Mouse.current; - - // Toggle default tool. - if (!m_PanelManager.AdvancedModeActive() && - InputManager.m_Instance.GetCommandDown(InputManager.SketchCommands.ToggleDefaultTool) && - !m_SketchSurfacePanel.IsDefaultToolEnabled() && - m_SketchSurfacePanel.ActiveTool.AllowDefaultToolToggle() && - // don't allow tool to change while pointing at panel because there is no visual indication - m_CurrentGazeObject == -1) - { - m_SketchSurfacePanel.EnableDefaultTool(); - AudioManager.m_Instance.PlayPinCushionSound(true); - } - // Pan. - else if (!hasController && mouse.rightButton.isPressed) - { - SwitchState(InputState.Pan); - } - // Controller lock (this must be before rotate/head lock!). - else if (!hasController && - InputManager.m_Instance.GetCommand(InputManager.SketchCommands.LockToController)) - { - SwitchState(InputState.ControllerLock); - } - // Rotate. - else if (!hasController && - InputManager.m_Instance.GetCommand(InputManager.SketchCommands.PivotRotation)) - { - SwitchState(InputState.Rotation); - } - // Head lock. - else if (!hasController && - InputManager.m_Instance.GetCommand(InputManager.SketchCommands.LockToHead)) - { - SwitchState(InputState.HeadLock); - } - // Push pull. - else if (!hasController && - InputManager.m_Instance.GetCommand(InputManager.SketchCommands.AltActivate)) - { - SwitchState(InputState.PushPull); - } - else if (!PointerManager.m_Instance.IsMainPointerCreatingStroke()) - { - // Reset surface. - if (!hasController && - InputManager.m_Instance.GetCommandDown(InputManager.SketchCommands.Reset)) - { - ResetGrabbedPose(); - } - // Undo. - else if (InputManager.m_Instance.GetCommandDown(InputManager.SketchCommands.Undo) && - CanUndo()) - { - IssueGlobalCommand(GlobalCommands.Undo); - } - else if (InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Undo) && - CanUndo() && ShouldRepeatUndo()) - { - m_UndoHold_Timer = m_UndoRedoHold_RepeatInterval; - IssueGlobalCommand(GlobalCommands.Undo); - } - // Redo. - else if (InputManager.m_Instance.GetCommandDown(InputManager.SketchCommands.Redo) && - CanRedo()) - { - IssueGlobalCommand(GlobalCommands.Redo); - } - else if (InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Redo) && - CanRedo() && ShouldRepeatRedo()) - { - m_RedoHold_Timer = m_UndoRedoHold_RepeatInterval; - IssueGlobalCommand(GlobalCommands.Redo); - } - // Reset scene. - else if (!hasController && - InputManager.m_Instance.GetKeyboardShortcutDown( - InputManager.KeyboardShortcut.ResetScene)) - { - // TODO: Should thsi go away? Seems like the "sweetspot" may no longer be used. - if (App.VrSdk.GetControllerDof() == VrSdk.DoF.Two) - { - m_PanelManager.SetSweetSpotPosition(m_CurrentGazeRay.origin); - ResetGrabbedPose(); - } - } - // Straight edge. - else if (!hasController && - InputManager.m_Instance.GetKeyboardShortcutDown( - InputManager.KeyboardShortcut.StraightEdge)) - { - IssueGlobalCommand(GlobalCommands.StraightEdge); - } - // Always fall back on switching tools. - else - { - m_SketchSurfacePanel.CheckForToolSelection(); - } - } - - // Reset undo/redo hold timers. - if (!InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Undo)) - { - m_UndoHold_Timer = m_UndoRedoHold_DurationBeforeStart; - } - if (!InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Redo)) - { - m_RedoHold_Timer = m_UndoRedoHold_DurationBeforeStart; - } - UnityEngine.Profiling.Profiler.EndSample(); - } - - bool CanUndo() - { - return SketchMemoryScript.m_Instance.CanUndo() && - !IsUIBlockingUndoRedo() && - m_PanelManager.GazePanelsAreVisible() && - !m_GrabWand.grabbingWorld && - !InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Activate) && - !SelectionManager.m_Instance.IsAnimatingTossFromGrabbingGroup; - } - - bool CanRedo() - { - return SketchMemoryScript.m_Instance.CanRedo() && - !IsUIBlockingUndoRedo() && - m_PanelManager.GazePanelsAreVisible() && - !m_GrabBrush.grabbingWorld && - !InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Activate) && - !SelectionManager.m_Instance.IsAnimatingTossFromGrabbingGroup; - } - - bool ShouldRepeatUndo() - { - m_UndoHold_Timer -= Time.deltaTime; - return (m_UndoHold_Timer <= 0.0f); - } - - bool ShouldRepeatRedo() - { - m_RedoHold_Timer -= Time.deltaTime; - return (m_RedoHold_Timer <= 0.0f); - } - - // Updates the global state: - // m_CurrentGrabWidget - void UpdateGrab() - { - UnityEngine.Profiling.Profiler.BeginSample("SketchControlScript.UpdateGrab"); - if (m_ControlsType != ControlsType.SixDofControllers) - { - UnityEngine.Profiling.Profiler.EndSample(); - return; - } - - GrabWidget rPrevGrabWidget = m_CurrentGrabWidget; - GrabWidget rPrevPotentialBrush = m_PotentialGrabWidgetBrush; - GrabWidget rPrevPotentialWand = m_PotentialGrabWidgetWand; - if (m_CurrentGrabWidget) - { - m_CurrentGrabWidget.Activate(false); - } - if (m_PotentialGrabWidgetBrush) - { - m_PotentialGrabWidgetBrush.Activate(false); - } - if (m_PotentialGrabWidgetWand) - { - m_PotentialGrabWidgetWand.Activate(false); - } - m_CurrentGrabWidget = null; - m_PotentialGrabWidgetBrush = null; - m_PotentialGrabWidgetWand = null; - m_PotentialGrabWidgetBrushValid = false; - m_PotentialGrabWidgetWandValid = false; - - m_WidgetManager.RefreshNearestWidgetLists(m_CurrentGazeRay, m_CurrentGazeObject); - - if (m_GrabWidgetState == GrabWidgetState.None) - { - UpdateGrab_WasNone(rPrevPotentialBrush, rPrevPotentialWand); - } - else if (m_GrabWidgetState == GrabWidgetState.OneHand) - { - UpdateGrab_WasOneHand(rPrevGrabWidget); - } - else if (m_GrabWidgetState == GrabWidgetState.TwoHands) - { - UpdateGrab_WasTwoHands(rPrevGrabWidget); - } - - // Update grab intersection state. - switch (m_CurrentGrabIntersectionState) - { - case GrabIntersectionState.RequestIntersections: - m_CurrentGrabIntersectionState = GrabIntersectionState.ReadBrush; - break; - case GrabIntersectionState.ReadBrush: - m_CurrentGrabIntersectionState = GrabIntersectionState.ReadWand; - break; - case GrabIntersectionState.ReadWand: - m_CurrentGrabIntersectionState = GrabIntersectionState.RequestIntersections; - break; - } - - if (!TutorialManager.m_Instance.TutorialActive() && m_CurrentGrabWidget == null) - { - UpdateGrab_World(); - } - - App.Instance.SelectionEffect.HighlightForGrab( - m_GrabWidgetState != GrabWidgetState.None || - (m_PotentialGrabWidgetBrush != null && m_PotentialGrabWidgetBrushValid) || - (m_PotentialGrabWidgetWand != null && m_PotentialGrabWidgetWandValid)); - UnityEngine.Profiling.Profiler.EndSample(); - } - - void UpdateGrab_WasNone(GrabWidget rPrevPotentialBrush, GrabWidget rPrevPotentialWand) - { - // if a panel isn't in focus, allow for widget grab - // We can grab a widget as long as we aren't trying to draw with that hand. - bool bActiveInput = - (InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Activate) && - App.Instance.IsInStateThatAllowsPainting()); - - //certain tools don't allow us to mess with widgets - bool bWidgetManipOK = m_SketchSurfacePanel.DoesCurrentToolAllowWidgetManipulation() && - !m_GrabWand.grabbingWorld && !m_GrabBrush.grabbingWorld && IsGrabWorldStateStable() && - App.Instance.IsInStateThatAllowsAnyGrabbing(); - - // Update EatInput flags if they're valid. - if (m_GrabBrush.eatInput) - { - m_GrabBrush.eatInput = InputManager.Brush.GetControllerGrip(); - } - if (m_GrabWand.eatInput) - { - m_GrabWand.eatInput = InputManager.Wand.GetControllerGrip(); - } - - bool bShouldClearWandInside = false; - if (m_CurrentInputState == InputState.Standard && bWidgetManipOK) - { - // If we're in the intersection request state, fire off a new intersection request. If - // we're in the read brush state, update our brush grab data structure. - List brushBests = m_WidgetManager.WidgetsNearBrush; - if (m_CurrentGrabIntersectionState == GrabIntersectionState.RequestIntersections) - { - RequestWidgetIntersection(brushBests, InputManager.ControllerName.Brush); - } - else if (m_CurrentGrabIntersectionState == GrabIntersectionState.ReadBrush) - { - m_BackupBrushGrabData = GetBestWidget(brushBests, m_BrushResults); - } - - if (m_BackupBrushGrabData != null) - { - m_PotentialGrabWidgetBrush = m_BackupBrushGrabData.m_WidgetScript; - - // Allow widget grab if we're not painting. - if (!bActiveInput) - { - m_PotentialGrabWidgetBrush.Activate(true); - m_PotentialGrabWidgetBrushValid = true; - m_PotentialGrabWidgetBrush.VisualizePinState(); - - if (!m_GrabBrush.eatInput && InputManager.Brush.GetControllerGrip()) - { - m_CurrentGrabWidget = m_PotentialGrabWidgetBrush; - if (m_CurrentGrabWidget.Group != SketchGroupTag.None) - { - m_GrabBrush.grabbingGroup = true; - m_CurrentGrabWidget = - SelectionManager.m_Instance.StartGrabbingGroupWithWidget(m_CurrentGrabWidget); - } - UpdateGrab_NoneToOne(InputManager.ControllerName.Brush); - bShouldClearWandInside = true; - m_GrabBrush.startedGrabInsideWidget = true; - } - } - } - m_GrabBrush.SetHadBestGrabAndTriggerHaptics(m_BackupBrushGrabData); - m_ControllerGrabVisuals.BrushInWidgetRange = m_BackupBrushGrabData != null; - - // If we're in the intersection request state, fire off a new intersection request. If - // we're in the read wand state, update our wand grab data structure. - List wandBests = m_WidgetManager.WidgetsNearWand; - if (m_CurrentGrabIntersectionState == GrabIntersectionState.RequestIntersections) - { - RequestWidgetIntersection(wandBests, InputManager.ControllerName.Wand); - } - else if (m_CurrentGrabIntersectionState == GrabIntersectionState.ReadWand) - { - m_BackupWandGrabData = GetBestWidget(wandBests, m_WandResults); - } - - if (m_BackupWandGrabData != null) - { - m_PotentialGrabWidgetWand = m_BackupWandGrabData.m_WidgetScript; - // Allow wand widget grab if brush grab failed. - bool bGrabAllowed = (m_GrabWidgetState == GrabWidgetState.None) && !bActiveInput; - if (bGrabAllowed) - { - m_PotentialGrabWidgetWand.Activate(true); - m_PotentialGrabWidgetWandValid = true; - m_PotentialGrabWidgetWand.VisualizePinState(); - - if (!m_GrabWand.eatInput && InputManager.Wand.GetControllerGrip()) - { - m_CurrentGrabWidget = m_PotentialGrabWidgetWand; - if (m_CurrentGrabWidget.Group != SketchGroupTag.None) - { - m_GrabWand.grabbingGroup = true; - m_CurrentGrabWidget = - SelectionManager.m_Instance.StartGrabbingGroupWithWidget(m_CurrentGrabWidget); - } - UpdateGrab_NoneToOne(InputManager.ControllerName.Wand); - m_GrabBrush.ClearInsideWidget(); - m_GrabWand.startedGrabInsideWidget = true; - } - } - } - m_GrabWand.SetHadBestGrabAndTriggerHaptics(m_BackupWandGrabData); - m_ControllerGrabVisuals.WandInWidgetRange = m_BackupWandGrabData != null; - - // Account for asymmetry in controller processing by clearing after wand has updated - // GrabState.insideWidget according to bestWandGrab. - if (bShouldClearWandInside) - { - m_GrabWand.ClearInsideWidget(); - } - } - - // Update widget collisions if we've got a drifter. - if (m_GrabWidgetState == GrabWidgetState.None) - { - if (m_WidgetManager.ShouldUpdateCollisions()) - { - m_PanelManager.DoCollisionSimulationForWidgetPanels(); - } - } - } - - void UpdateGrab_WasOneHand(GrabWidget rPrevGrabWidget) - { - var controller = InputManager.Controllers[(int)m_GrabWidgetOneHandInfo.m_Name]; - bool shouldRelease = !App.Instance.IsInStateThatAllowsAnyGrabbing(); - if (!InputManager.Controllers[(int)m_GrabWidgetOneHandInfo.m_Name].GetControllerGrip() || - shouldRelease) - { - if (shouldRelease) - { - EatGrabInput(); - } - - Vector3 vLinearVelocity; - Vector3 vAngularVelocity; - if (GetGrabWidgetHoldHistory(out vLinearVelocity, out vAngularVelocity)) - { - rPrevGrabWidget.SetVelocities( - vLinearVelocity, vAngularVelocity, - controller.Transform.position); - } - // One -> None - UpdateGrab_ToNone(rPrevGrabWidget); - } - else - { - // Keep holding on to our widget. - m_CurrentGrabWidget = rPrevGrabWidget; - m_CurrentGrabWidget.Activate(true); - m_CurrentGrabWidget.UserInteracting(true, m_GrabWidgetOneHandInfo.m_Name); - - if (!m_CurrentGrabWidget.Pinned) - { - var info = InputManager.Controllers[(int)m_GrabWidgetOneHandInfo.m_Name]; - var controllerXf = Coords.AsGlobal[info.Transform]; - var newWidgetXf = controllerXf * m_GrabWidgetOneHandInfo.m_BaseWidgetXf_LS; - m_CurrentGrabWidget.RecordAndSetPosRot(newWidgetXf); - - UpdateGrabWidgetHoldHistory(m_GrabWidgetOneHandInfo.m_Name); - } - - m_PanelManager.DoCollisionSimulationForWidgetPanels(); - - // Check for widget pinning. - if (m_CurrentGrabWidget.AllowPinning) - { - if (InputManager.Controllers[(int)m_GrabWidgetOneHandInfo.m_Name].GetCommandDown( - InputManager.SketchCommands.PinWidget)) - { - // If the user initiates a pin action, buzz a bit. - if (!m_CurrentGrabWidget.Pinned) - { - InputManager.m_Instance.TriggerHapticsPulse( - m_GrabWidgetOneHandInfo.m_Name, 3, 0.10f, 0.07f); - } - m_CurrentGrabWidget.Pin(!m_CurrentGrabWidget.Pinned); - SketchSurfacePanel.m_Instance.EatToolsInput(); - m_WidgetManager.RefreshPinAndUnpinLists(); - } - } - - if (m_CurrentGrabWidget is SelectionWidget) - { - if (InputManager.m_Instance.GetCommandDown( - InputManager.SketchCommands.DuplicateSelection)) - { - controller.LastHeldInput = - controller.GetCommandHoldInput(InputManager.SketchCommands.DuplicateSelection); - } - - if (controller.LastHeldInput != null && - InputManager.m_Instance.GetCommandHeld(InputManager.SketchCommands.DuplicateSelection)) - { - SketchControlsScript.m_Instance.IssueGlobalCommand( - SketchControlsScript.GlobalCommands.Duplicate); - } - } - - InputManager.ControllerName otherName = - (m_GrabWidgetOneHandInfo.m_Name == InputManager.ControllerName.Brush) ? - InputManager.ControllerName.Wand : InputManager.ControllerName.Brush; - bool otherInputEaten = - (m_GrabWidgetOneHandInfo.m_Name == InputManager.ControllerName.Brush) ? - m_GrabWand.eatInput : m_GrabBrush.eatInput; - - // See if the other controller decides to grab the widget (unless we're pinned). - if (!m_CurrentGrabWidget.Pinned) - { - if (m_CurrentGrabWidget.AllowTwoHandGrab) - { - if (InputManager.Controllers[(int)otherName].GetControllerGrip()) - { - RequestPanelsVisibility(false); - m_GrabWidgetState = GrabWidgetState.TwoHands; - // Figure out if the new grab starts inside the widget. - Vector3 vOtherGrabPos = TrTransform.FromTransform( - InputManager.m_Instance.GetController(otherName)).translation; - bool bOtherGrabInBounds = m_CurrentGrabWidget.GetActivationScore( - vOtherGrabPos, otherName) >= 0; - m_CurrentGrabWidget.SetUserTwoHandGrabbing( - true, m_GrabWidgetOneHandInfo.m_Name, otherName, bOtherGrabInBounds); - - if (otherName == InputManager.ControllerName.Brush) - { - m_GrabBrush.startedGrabInsideWidget = bOtherGrabInBounds; - } - else - { - m_GrabWand.startedGrabInsideWidget = bOtherGrabInBounds; - } - - m_GrabWidgetTwoHandBrushPrev = TrTransform.FromTransform( - InputManager.m_Instance.GetController(InputManager.ControllerName.Brush)); - m_GrabWidgetTwoHandWandPrev = TrTransform.FromTransform( - InputManager.m_Instance.GetController(InputManager.ControllerName.Wand)); - } - } - } - else if (!otherInputEaten && InputManager.Controllers[(int)otherName].GetControllerGrip()) - { - // If it's a two hand grab but the current grab widget is pinned, grab the world. - UpdateGrab_ToNone(m_CurrentGrabWidget); - m_CurrentGrabWidget = null; - m_ControllerGrabVisuals.SetDesiredVisualState(ControllerGrabVisuals.VisualState.Off); - } - } - } - - // Previous frame was a two-handed grab. - // Handles all the cases where this frame's grab is zero, one, or two hands. - void UpdateGrab_WasTwoHands(GrabWidget rPrevGrabWidget) - { - //keep holding on to our widget - m_CurrentGrabWidget = rPrevGrabWidget; - m_CurrentGrabWidget.Activate(true); - m_CurrentGrabWidget.UserInteracting(true, m_GrabWidgetOneHandInfo.m_Name); - - if (!App.Instance.IsInStateThatAllowsAnyGrabbing()) - { - m_CurrentGrabWidget.SetUserTwoHandGrabbing(false); - UpdateGrab_ToNone(rPrevGrabWidget); - } - else if (!InputManager.Wand.GetControllerGrip()) - { // Look for button release. - m_CurrentGrabWidget.SetUserTwoHandGrabbing(false); - // See if our Brush hand is still within grab range of the widget. - if (m_GrabBrush.startedGrabInsideWidget || - IsControllerNearWidget(InputManager.ControllerName.Brush, m_CurrentGrabWidget)) - { - m_GrabWidgetOneHandInfo.m_Name = InputManager.ControllerName.Brush; - RequestPanelsVisibility(true); - InitializeGrabWidgetControllerInfo(m_GrabWidgetOneHandInfo); - m_GrabWidgetState = GrabWidgetState.OneHand; - } - else - { - // If the Brush hand is beyond the widget, we're not holding it anymore. - UpdateGrab_ToNone(rPrevGrabWidget); - - // Eat input on the brush grip until we release the button. - m_GrabBrush.eatInput = true; - } - } - else if (!InputManager.Brush.GetControllerGrip()) - { - m_CurrentGrabWidget.SetUserTwoHandGrabbing(false); - if (m_GrabWand.startedGrabInsideWidget || - IsControllerNearWidget(InputManager.ControllerName.Wand, m_CurrentGrabWidget)) - { - m_GrabWidgetOneHandInfo.m_Name = InputManager.ControllerName.Wand; - InitializeGrabWidgetControllerInfo(m_GrabWidgetOneHandInfo); - m_GrabWidgetState = GrabWidgetState.OneHand; - } - else - { - UpdateGrab_ToNone(rPrevGrabWidget); - m_GrabWand.eatInput = true; - } - } - else - { - // Both hands still grabbing. - // Check for pin, which forcibly releases one of the hands. - if (m_CurrentGrabWidget.AllowPinning && - InputManager.Controllers[(int)m_GrabWidgetOneHandInfo.m_Name].GetCommandDown( - InputManager.SketchCommands.PinWidget)) - { - // If the user initiates a pin action, buzz a bit. - if (!m_CurrentGrabWidget.Pinned) - { - InputManager.m_Instance.TriggerHapticsPulse( - m_GrabWidgetOneHandInfo.m_Name, 3, 0.10f, 0.07f); - } - - m_CurrentGrabWidget.Pin(!m_CurrentGrabWidget.Pinned); - SketchSurfacePanel.m_Instance.EatToolsInput(); - m_WidgetManager.RefreshPinAndUnpinLists(); - - InitializeGrabWidgetControllerInfo(m_GrabWidgetOneHandInfo); - m_GrabWidgetState = GrabWidgetState.OneHand; - m_CurrentGrabWidget.SetUserTwoHandGrabbing(false); - - // Eat input on the off hand so we don't immediately jump in to world transform. - if (m_GrabWidgetOneHandInfo.m_Name == InputManager.ControllerName.Brush) - { - RequestPanelsVisibility(true); - m_GrabWand.eatInput = true; - } - else - { - m_GrabBrush.eatInput = true; - } - } - - if (!m_CurrentGrabWidget.Pinned) - { - UpdateGrab_ContinuesTwoHands(); - } - } - ClearGrabWidgetHoldHistory(); - m_PanelManager.DoCollisionSimulationForWidgetPanels(); - } - - // Common case for two-handed grab: both the previous and current frames are two-handed. - private void UpdateGrab_ContinuesTwoHands() - { - //holding with two hands, transform accordingly - TrTransform xfBrush = TrTransform.FromTransform(InputManager.Brush.Transform); - TrTransform xfWand = TrTransform.FromTransform(InputManager.Wand.Transform); - Vector2 vSizeRange = m_CurrentGrabWidget.GetWidgetSizeRange(); - - GrabWidget.Axis axis = m_CurrentGrabWidget.GetScaleAxis( - xfWand.translation, xfBrush.translation, - out Vector3 axisDirection, out float axisExtent); - - TrTransform newWidgetXf; - if (axis != GrabWidget.Axis.Invalid) - { - // Scale along a single axis - float deltaScale; - if (App.Config.m_AxisManipulationIsResize) - { - newWidgetXf = MathUtils.TwoPointObjectTransformationAxisResize( - axisDirection, axisExtent, - m_GrabWidgetTwoHandWandPrev, m_GrabWidgetTwoHandBrushPrev, - xfWand, xfBrush, - GetWorkingTransform(m_CurrentGrabWidget), - out deltaScale, - deltaScaleMin: vSizeRange.x / axisExtent, - deltaScaleMax: vSizeRange.y / axisExtent); - } - else - { - newWidgetXf = MathUtils.TwoPointObjectTransformationNonUniformScale( - axisDirection, - m_GrabWidgetTwoHandWandPrev, m_GrabWidgetTwoHandBrushPrev, - xfWand, xfBrush, - GetWorkingTransform(m_CurrentGrabWidget), - out deltaScale, - finalScaleMin: vSizeRange.x, - deltaScaleMin: vSizeRange.x / axisExtent, - deltaScaleMax: vSizeRange.y / axisExtent); - } - - // The above functions return undefined values in newWidgetXf.scale; but that's - // okay because RecordAndSetPosRot ignores xf.scale. - // TODO: do this more cleanly - m_CurrentGrabWidget.RecordAndApplyScaleToAxis(deltaScale, axis); - } - else - { - // Uniform scaling - TrTransform xfObject = GetWorkingTransform(m_CurrentGrabWidget); - Vector3 extents = (m_CurrentGrabWidget is StencilWidget) - ? (m_CurrentGrabWidget as StencilWidget).Extents - : Vector3.one * Mathf.Abs(m_CurrentGrabWidget.GetSignedWidgetSize()); - - // Delta-scale bounds should be based on the smallest/largest extent. - // Irritatingly, the API wants absolute rather than relative scale bounds, - // so they need even more conversion. - float deltaScaleMin = vSizeRange.x / extents.Min(); - float deltaScaleMax = vSizeRange.y / extents.Max(); - if (m_GrabWand.startedGrabInsideWidget && m_GrabBrush.startedGrabInsideWidget) - { - newWidgetXf = MathUtils.TwoPointObjectTransformation( - m_GrabWidgetTwoHandWandPrev, m_GrabWidgetTwoHandBrushPrev, - xfWand, xfBrush, - xfObject, - deltaScaleMin: deltaScaleMin, deltaScaleMax: deltaScaleMax); - } - else if (m_GrabWand.startedGrabInsideWidget) - { - // keep the wand inside the object - newWidgetXf = MathUtils.TwoPointObjectTransformation( - m_GrabWidgetTwoHandWandPrev, m_GrabWidgetTwoHandBrushPrev, - xfWand, xfBrush, - xfObject, - deltaScaleMin: deltaScaleMin, deltaScaleMax: deltaScaleMax, - bUseLeftAsPivot: true); - } - else - { - // keep the brush inside the object (note the brush is the left hand) - newWidgetXf = MathUtils.TwoPointObjectTransformation( - m_GrabWidgetTwoHandBrushPrev, m_GrabWidgetTwoHandWandPrev, - xfBrush, xfWand, - xfObject, - deltaScaleMin: deltaScaleMin, deltaScaleMax: deltaScaleMax, - bUseLeftAsPivot: true); - } - - // Must do separately becvause RecordAndSetPosRot ignores newWidgetXf.scale - m_CurrentGrabWidget.RecordAndSetSize(newWidgetXf.scale); - - float currentSize = Mathf.Abs(m_CurrentGrabWidget.GetSignedWidgetSize()); - if (currentSize == vSizeRange.x || currentSize == vSizeRange.y) - { - InputManager.m_Instance.TriggerHaptics(InputManager.ControllerName.Brush, 0.05f); - InputManager.m_Instance.TriggerHaptics(InputManager.ControllerName.Wand, 0.05f); - } - } - - // Ignores TrTransform.scale - m_CurrentGrabWidget.RecordAndSetPosRot(newWidgetXf); - - m_GrabWidgetTwoHandBrushPrev = xfBrush; - m_GrabWidgetTwoHandWandPrev = xfWand; - } - - void UpdateGrab_NoneToOne(InputManager.ControllerName controllerName) - { - if (m_MaybeDriftingGrabWidget != null && - m_MaybeDriftingGrabWidget.IsMoving() && - !m_MaybeDriftingGrabWidget.IsSpinningFreely) - { - // If a new widget is grabbed but the previous one is still drifting, end the drift. - // TODO: Simplify in the widget animation cleanup. - if (m_MaybeDriftingGrabWidget == m_CurrentGrabWidget) - { - SketchMemoryScript.m_Instance.PerformAndRecordCommand( - new MoveWidgetCommand(m_MaybeDriftingGrabWidget, - m_MaybeDriftingGrabWidget.LocalTransform, m_MaybeDriftingGrabWidget.CustomDimension, - final: true), - discardIfNotMerged: true); - } - m_MaybeDriftingGrabWidget.ClearVelocities(); - } - - // UserInteracting should be the first thing that happens here so OnUserBeginInteracting can - // be called before everything else. - m_CurrentGrabWidget.UserInteracting(true, controllerName); - m_CurrentGrabWidget.ClearVelocities(); - ClearGrabWidgetHoldHistory(); - - //set our info names according to this controller's name - m_GrabWidgetOneHandInfo.m_Name = controllerName; - InitializeGrabWidgetControllerInfo(m_GrabWidgetOneHandInfo); - - PointerManager.m_Instance.AllowPointerPreviewLine(false); - PointerManager.m_Instance.RequestPointerRendering(false); - m_SketchSurfacePanel.RequestHideActiveTool(true); - if (m_GrabWidgetOneHandInfo.m_Name == InputManager.ControllerName.Wand) - { - RequestPanelsVisibility(false); - } - - // Notify visuals. - ControllerGrabVisuals.VisualState visualState = - m_GrabWidgetOneHandInfo.m_Name == InputManager.ControllerName.Brush ? - ControllerGrabVisuals.VisualState.WidgetBrushGrip : - ControllerGrabVisuals.VisualState.WidgetWandGrip; - m_ControllerGrabVisuals.SetDesiredVisualState(visualState); - m_ControllerGrabVisuals.SetHeldWidget(m_CurrentGrabWidget.transform); - - //if a gaze object had focus when we grabbed this widget, take focus off the object - ResetActivePanel(); - m_UIReticle.SetActive(false); - - // Prep all other grab widgets for collision. - m_PanelManager.PrimeCollisionSimForWidgets(m_CurrentGrabWidget); - - m_GrabWidgetState = GrabWidgetState.OneHand; - m_WidgetManager.WidgetsDormant = false; - PointerManager.m_Instance.EatLineEnabledInput(); - - m_BackupWandGrabData = null; - m_BackupBrushGrabData = null; - } - - void UpdateGrab_ToNone(GrabWidget rPrevGrabWidget) - { - m_MaybeDriftingGrabWidget = rPrevGrabWidget; - - m_GrabWidgetState = GrabWidgetState.None; - PointerManager.m_Instance.RequestPointerRendering(!App.Instance.IsLoading() && - m_SketchSurfacePanel.ShouldShowPointer()); - RequestPanelsVisibility(true); - m_SketchSurfacePanel.RequestHideActiveTool(false); - rPrevGrabWidget.UserInteracting(false); - - // Disable grab visuals. - m_ControllerGrabVisuals.SetDesiredVisualState(ControllerGrabVisuals.VisualState.Off); - m_ControllerGrabVisuals.SetHeldWidget(null); - - if (m_GrabBrush.grabbingGroup || m_GrabWand.grabbingGroup) - { - SelectionManager.m_Instance.EndGrabbingGroupWithWidget(); - m_GrabBrush.grabbingGroup = false; - m_GrabWand.grabbingGroup = false; - } - } - - void RequestWidgetIntersection(List candidates, - InputManager.ControllerName controllerName) - { - // Get locals based off what controller we're using. - Queue resultQueue = null; - Vector3 controllerPos = Vector3.zero; - if (controllerName == InputManager.ControllerName.Brush) - { - resultQueue = m_BrushResults; - controllerPos = InputManager.m_Instance.GetBrushControllerAttachPoint().position; - } - else - { - resultQueue = m_WandResults; - controllerPos = InputManager.m_Instance.GetWandControllerAttachPoint().position; - } - - // If we don't have a candidate that has a GPU object, don't bother firing off a GPU request. - bool requestGpuIntersection = false; - - // Fire off a new GPU intersection with all widgets that can use it. - for (int i = 0; i < candidates.Count; ++i) - { - if (candidates[i].m_WidgetScript.HasGPUIntersectionObject()) - { - candidates[i].m_WidgetScript.SetGPUIntersectionObjectLayer(m_WidgetGpuIntersectionLayer); - requestGpuIntersection = true; - } - } - - if (requestGpuIntersection) - { - GpuIntersectionResult newRequest = new GpuIntersectionResult(); - newRequest.resultList = new List(); - newRequest.result = App.Instance.GpuIntersector.RequestModelIntersections( - controllerPos, m_WidgetGpuIntersectionRadius, newRequest.resultList, 8, - (1 << m_WidgetGpuIntersectionLayer)); - - // The new result will only be null when the intersector is disabled. - if (newRequest.result != null) - { - resultQueue.Enqueue(newRequest); - } - - for (int i = 0; i < candidates.Count; ++i) - { - if (candidates[i].m_WidgetScript.HasGPUIntersectionObject()) - { - candidates[i].m_WidgetScript.RestoreGPUIntersectionObjectLayer(); - } - } - } - } - - GrabWidgetData GetBestWidget(List candidates, - Queue resultQueue) - { - // Discard futures that are too old. - while (resultQueue.Count > 0) - { - if (Time.frameCount - resultQueue.Peek().result.StartFrame < 5) - { - break; - } - resultQueue.Dequeue(); - } - - // If the oldest future is ready, use its intersection result to update the candidates. - GpuIntersectionResult finishedResult; - if (resultQueue.Count > 0 && resultQueue.Peek().result.IsReady) - { - finishedResult = resultQueue.Dequeue(); - } - else - { - finishedResult.resultList = new List(); - } - - // TODO: Speed this up. - for (int i = 0; i < candidates.Count; ++i) - { - if (candidates[i].m_WidgetScript.HasGPUIntersectionObject()) - { - // If a candidate can't find itself in the finished results list, it's not eligible. - bool candidateValid = false; - for (int j = 0; j < finishedResult.resultList.Count; ++j) - { - if (candidates[i].m_WidgetScript.Equals(finishedResult.resultList[j].widget)) - { - candidateValid = true; - break; - } - } - - if (candidateValid) - { - // If a candidate has a GPU intersection object and we found it in this list, - // not only is it valid, but it's as valid as it can be. - candidates[i].m_ControllerScore = 1.0f; - } - else - { - candidates[i].m_NearController = false; - } - } - } - - // Run through the candidates and pick - GrabWidgetData best = null; - for (int i = 0; i < candidates.Count; ++i) - { - var candidate = candidates[i]; - if (!candidate.m_NearController) continue; - - // For media widgets - only select from the active layer - if (candidate.m_WidgetScript is MediaWidget - && candidate.m_WidgetScript.Canvas != App.Scene.ActiveCanvas) continue; - - if (best == null || candidate.m_ControllerScore > best.m_ControllerScore) - { - best = candidate; - } - } - return best; - } - - void InitializeGrabWidgetControllerInfo(GrabWidgetControllerInfo info) - { - Transform controller = InputManager.Controllers[(int)info.m_Name].Transform; - Transform widget = m_CurrentGrabWidget.GrabTransform_GS; - TrTransform newWidgetXf = Coords.AsGlobal[widget]; - - info.m_BaseControllerXf = Coords.AsGlobal[controller]; - info.m_BaseWidgetXf_LS = info.m_BaseControllerXf.inverse * newWidgetXf; - } - - // returns the transform of the true widget (not the snapped one for those that can be) - private TrTransform GetWorkingTransform(GrabWidget w) - { - TrTransform ret = w.GetGrabbedTrTransform(); - ret.scale = w.GetSignedWidgetSize(); - return ret; - } - - // Initiate the world transform reset animation. - public void RequestWorldTransformReset(bool toSavedXf = false) - { - if (WorldIsReset(toSavedXf)) - { - return; - } - - m_WorldTransformResetXf = - toSavedXf ? SketchMemoryScript.m_Instance.InitialSketchTransform : TrTransform.identity; - m_WorldTransformResetState = WorldTransformResetState.Requested; - - App.Scene.disableTiltProtection = false; - } - - void UpdateWorldTransformReset() - { - switch (m_WorldTransformResetState) - { - case WorldTransformResetState.Requested: - ViewpointScript.m_Instance.FadeToColor(Color.black, m_GrabWorldFadeSpeed); - m_WorldTransformResetState = WorldTransformResetState.FadingToBlack; - m_xfDropCamReset_RS = Coords.AsRoom[m_DropCam.transform]; - PointerManager.m_Instance.EatLineEnabledInput(); - PointerManager.m_Instance.AllowPointerPreviewLine(false); - break; - case WorldTransformResetState.FadingToBlack: - m_WorldTransformFadeAmount += m_GrabWorldFadeSpeed * Time.deltaTime; - if (m_WorldTransformFadeAmount >= 1.0f) - { - App.Scene.Pose = m_WorldTransformResetXf; - m_WorldTransformFadeAmount = 1.0f; - m_WorldTransformResetState = WorldTransformResetState.FadingToScene; - ViewpointScript.m_Instance.FadeToScene(m_GrabWorldFadeSpeed); - m_DropCam.transform.position = m_xfDropCamReset_RS.translation; - m_DropCam.transform.rotation = m_xfDropCamReset_RS.rotation; - PointerManager.m_Instance.AllowPointerPreviewLine(true); - } - break; - case WorldTransformResetState.FadingToScene: - m_WorldTransformFadeAmount -= m_GrabWorldFadeSpeed * Time.deltaTime; - if (m_WorldTransformFadeAmount <= 0.0f) - { - m_WorldTransformFadeAmount = 0.0f; - m_WorldTransformResetState = WorldTransformResetState.Default; - } - break; - } - } - - bool CheckToggleTiltProtection() - { - if ( - InputManager.Wand.GetCommandDown(InputManager.SketchCommands.Redo) || - InputManager.Brush.GetCommandDown(InputManager.SketchCommands.Redo) - ) - { - App.Scene.disableTiltProtection = !App.Scene.disableTiltProtection; - - return !App.Scene.disableTiltProtection; - } - - return false; - - } - - void UpdateGrab_World() - { - bool bAllowWorldTransform = m_SketchSurfacePanel.ActiveTool.AllowWorldTransformation() && - (m_GrabWorldState != GrabWorldState.ResetDone) && - (!PointerManager.m_Instance.IsMainPointerCreatingStroke() || App.Instance.IsLoading()) && - App.Instance.IsInStateThatAllowsAnyGrabbing() && - !m_DisableWorldGrabbing; - - bool bWorldGrabWandPrev = m_GrabWand.grabbingWorld; - bool bWorldGrabBrushPrev = m_GrabBrush.grabbingWorld; - m_GrabWand.grabbingWorld = bAllowWorldTransform && !m_GrabWand.eatInput && - InputManager.Wand.GetControllerGrip(); - m_GrabBrush.grabbingWorld = bAllowWorldTransform && !m_GrabBrush.eatInput && - InputManager.Brush.GetControllerGrip() && - (m_CurrentGazeObject == -1); - - bool grabsChanged = (bWorldGrabWandPrev != m_GrabWand.grabbingWorld) || - (bWorldGrabBrushPrev != m_GrabBrush.grabbingWorld); - bool bAllowWorldTransformChanged = - bAllowWorldTransform != m_AllowWorldTransformLastFrame; - int nGrabs = m_GrabWand.grabbingWorld ? 1 : 0; - nGrabs += m_GrabBrush.grabbingWorld ? 1 : 0; - - // Allow grabbing again if grabs have changed and we're done resetting. - if (m_GrabWorldState == GrabWorldState.ResetDone && grabsChanged) - { - m_GrabWorldState = GrabWorldState.Normal; - } - - // Update panels visibility if brush grip has changed. - if (bWorldGrabWandPrev != m_GrabWand.grabbingWorld) - { - RequestPanelsVisibility(!m_GrabWand.grabbingWorld); - } - - // Update tool visibility if brush grip has changed. - if (bWorldGrabBrushPrev != m_GrabBrush.grabbingWorld) - { - m_SketchSurfacePanel.RequestHideActiveTool(m_GrabBrush.grabbingWorld); - PointerManager.m_Instance.AllowPointerPreviewLine(!m_GrabBrush.grabbingWorld); - PointerManager.m_Instance.RequestPointerRendering(!m_GrabBrush.grabbingWorld - && m_SketchSurfacePanel.ShouldShowPointer() && !App.Instance.IsLoading()); - } - - // Reset m_WorldBeingGrabbed and only set it when world is actually being grabbed. - bool bWorldBeingGrabbedPrev = m_WorldBeingGrabbed; - m_WorldBeingGrabbed = false; - - // Move the world if it has been grabbed. - if (m_GrabWorldState == GrabWorldState.Normal && bAllowWorldTransform) - { - if (nGrabs == 2) - { - // Two-handed world movement. - m_WorldBeingGrabbed = true; - TrTransform grabXfWand = TrTransform.FromTransform( - InputManager.m_Instance.GetController(InputManager.ControllerName.Wand)); - TrTransform grabXfBrush = TrTransform.FromTransform( - InputManager.m_Instance.GetController(InputManager.ControllerName.Brush)); - - // Offset the controller positions so that they're centered on the grips. - Vector3 gripPos = InputManager.Controllers[(int)InputManager.ControllerName.Brush].Geometry.GripAttachPoint.localPosition; - gripPos.x = 0.0f; - grabXfWand.translation += grabXfWand.MultiplyVector(gripPos); - grabXfBrush.translation += grabXfBrush.MultiplyVector(gripPos); - - // Are we initiating two hand transform this frame? - if (!bWorldGrabWandPrev || !bWorldGrabBrushPrev) - { - PointerManager.m_Instance.EnableLine(false); - PointerManager.m_Instance.AllowPointerPreviewLine(false); - PointerManager.m_Instance.RequestPointerRendering(false); - // Initiate audio loop - m_WorldTransformSpeedSmoothed = 0.0f; - AudioManager.m_Instance.WorldGrabLoop(true); - } - else - { - TrTransform xfOld = GrabbedPose; - TrTransform xfNew; - float deltaScaleMin = WorldTransformMinScale / xfOld.scale; - float deltaScaleMax = WorldTransformMaxScale / xfOld.scale; - bool fixOffset = false; - fixOffset = CheckToggleTiltProtection(); - xfNew = MathUtils.TwoPointObjectTransformation( - m_GrabBrush.grabTransform, m_GrabWand.grabTransform, - grabXfBrush, grabXfWand, - xfOld, - rotationAxisConstraint: (App.Scene.disableTiltProtection ? default(Vector3) : Vector3.up), - deltaScaleMin: deltaScaleMin, deltaScaleMax: deltaScaleMax); - float fCurrentWorldTransformSpeed = - Mathf.Abs((xfNew.scale - xfOld.scale) / Time.deltaTime); - m_WorldTransformSpeedSmoothed = - Mathf.Lerp(m_WorldTransformSpeedSmoothed, fCurrentWorldTransformSpeed, - AudioManager.m_Instance.m_WorldGrabLoopSmoothSpeed * Time.deltaTime); - AudioManager.m_Instance.ChangeLoopVolume("WorldGrab", - Mathf.Clamp(m_WorldTransformSpeedSmoothed / - AudioManager.m_Instance.m_WorldGrabLoopAttenuation, 0f, - AudioManager.m_Instance.m_WorldGrabLoopMaxVolume)); - - if (fixOffset) - { - Vector3 midPoint = Vector3.Lerp(grabXfBrush.translation, grabXfWand.translation, 0.5f); - - Vector3 localMidPointOldXF = xfOld.inverse * midPoint; - - // assign this to force the axial protection - GrabbedPose = xfNew; - xfNew = GrabbedPose; - - Vector3 midPointXFNew = xfNew * localMidPointOldXF; - - TrTransform xfDelta1 = TrTransform.T(midPoint - midPointXFNew); - xfNew = xfDelta1 * xfNew; - } - GrabbedPose = xfNew; - } - - // Update last states. - m_GrabBrush.grabTransform = grabXfBrush; - m_GrabWand.grabTransform = grabXfWand; - } - } - else if (m_GrabWorldState == GrabWorldState.ResettingTransform) - { - if (m_WorldTransformResetState == WorldTransformResetState.FadingToScene) - { - ResetGrabbedPose(); - PanelManager.m_Instance.ExecuteOnPanel(x => x.OnPanelMoved()); - - // World can't be transformed right after a reset until grab states have changed. - if (bAllowWorldTransform) - { - bAllowWorldTransform = false; - bAllowWorldTransformChanged = - bAllowWorldTransform != m_AllowWorldTransformLastFrame; - } - - // Set the grab world state on exit. - if (nGrabs == 0) - { - m_GrabWorldState = GrabWorldState.Normal; - } - else - { - m_GrabWorldState = GrabWorldState.ResetDone; - } - } - } - - if (grabsChanged || bAllowWorldTransformChanged) - { - // Fade in grid when doing two handed spin. - if (nGrabs == 2 && !bAllowWorldTransformChanged) - { - ViewpointScript.m_Instance.FadeGroundPlaneIn(m_GrabWorldGridColor, m_GrabWorldFadeSpeed); - } - else - { - ViewpointScript.m_Instance.FadeGroundPlaneOut(m_GrabWorldFadeSpeed); - } - } - - // Update visuals for world transform - if (grabsChanged) - { - bool bDoubleGrip = m_GrabBrush.grabbingWorld && m_GrabWand.grabbingWorld; - bool bSingleGrip = m_GrabBrush.grabbingWorld || m_GrabWand.grabbingWorld; - Vector3 vControllersMidpoint = - (InputManager.m_Instance.GetControllerPosition(InputManager.ControllerName.Brush) + - InputManager.m_Instance.GetControllerPosition(InputManager.ControllerName.Wand)) * 0.5f; - - // Update transform line visuals - if (bDoubleGrip) - { - m_ControllerGrabVisuals.SetDesiredVisualState(ControllerGrabVisuals.VisualState.WorldDoubleGrip); - AudioManager.m_Instance.WorldGrabbed(vControllersMidpoint); - } - else if (bSingleGrip) - { - if (m_GrabWand.grabbingWorld) - { - m_ControllerGrabVisuals.SetDesiredVisualState(ControllerGrabVisuals.VisualState.WorldWandGrip); - } - else - { - m_ControllerGrabVisuals.SetDesiredVisualState(ControllerGrabVisuals.VisualState.WorldBrushGrip); - } - - if (!bWorldGrabWandPrev && !bWorldGrabBrushPrev) - { - AudioManager.m_Instance.WorldGrabbed(vControllersMidpoint); - } - else - { - AudioManager.m_Instance.WorldGrabLoop(false); - } - } - else - { - m_ControllerGrabVisuals.SetDesiredVisualState(ControllerGrabVisuals.VisualState.Off); - AudioManager.m_Instance.WorldGrabLoop(false); - } - - if (m_GrabWand.grabbingWorld || m_GrabBrush.grabbingWorld) - { - m_WidgetManager.WidgetsDormant = false; - PointerManager.m_Instance.EatLineEnabledInput(); - } - } - - // Reset scene transform if we're gripping and press the track pad. - bool wandReset = m_GrabWand.grabbingWorld && - InputManager.Wand.GetCommandDown(InputManager.SketchCommands.WorldTransformReset); - bool brushReset = m_GrabBrush.grabbingWorld && - InputManager.Brush.GetCommandDown(InputManager.SketchCommands.WorldTransformReset); - if ((wandReset || brushReset) && !WorldIsReset(toSavedXf: false)) - { - m_GrabBrush.eatInput = true; - m_GrabWand.eatInput = true; - m_EatToolScaleInput = true; - m_GrabWorldState = GrabWorldState.ResettingTransform; - RequestWorldTransformReset(); - AudioManager.m_Instance.PlayTransformResetSound(); - } - - // Update the skybox rotation with the new scene rotation. - if (RenderSettings.skybox) - { - Quaternion sceneQuaternion = App.Instance.m_SceneTransform.rotation; - RenderSettings.skybox.SetVector( - "_SkyboxRotation", - new Vector4(sceneQuaternion.x, sceneQuaternion.y, sceneQuaternion.z, sceneQuaternion.w)); - } - - // Update last frame members. - m_AllowWorldTransformLastFrame = bAllowWorldTransform; - } - - /// If lhs and rhs are overlapping, return the smallest vector that would - /// cause rhs to stop overlapping; otherwise, return 0. - /// lhs: an antisphere (solid outside, empty inside) - /// rhs: a sphere (empty outside, solid inside) - private static Vector3 GetOverlap_Antisphere_Sphere( - Vector3 lhsCenter, float lhsRadius, - Vector3 rhsCenter, float rhsRadius) - { - // If anyone passes negative values, they are a bad person - lhsRadius = Mathf.Abs(lhsRadius); - rhsRadius = Mathf.Abs(rhsRadius); - // Without loss of generality, can recenter on lhs - rhsCenter -= lhsCenter; - lhsCenter -= lhsCenter; - - float maxDistance = lhsRadius - rhsRadius; - - // Edge case: sphere does not fit in antisphere - if (maxDistance <= 0) - { - return -rhsCenter; - } - - float penetrationDistance = Mathf.Max(0, rhsCenter.magnitude - maxDistance); - return -penetrationDistance * rhsCenter.normalized; - } - - public static bool IsValidScenePose(TrTransform xf, float radialBounds) - { - // Simple and dumb implementation for now. - return xf == MakeValidScenePose(xf, radialBounds); - } - - /// This is like MakeValidScenePose, but it guarantees that: - /// - The return value is a valid result of Lerp(scene0, scene1, t), - /// for some handwavy definition of "lerp" - /// - The lerp "t" is in [0, 1] - /// - IsValidScenePose(return value) is true, subject to the previous constraints. - /// - /// Think of it as doing a cast from scene0 to scene1. - public static TrTransform MakeValidSceneMove( - TrTransform scene0, TrTransform scene1, float radialBounds) - { - if (IsValidScenePose(scene1, radialBounds)) - { - return scene1; - } - if (!IsValidScenePose(scene0, radialBounds)) - { - Debug.LogError("Invalid scene cast start"); - return scene0; - } - - // We don't support lerping either of these - Debug.Assert(scene0.rotation == scene1.rotation); - Debug.Assert(scene0.scale == scene1.scale); - - Vector3 vRoom0 = -scene0.translation; - Vector3 vRoom1 = -scene1.translation; - float radius = (scene0.scale - * radialBounds - * App.METERS_TO_UNITS) - App.Instance.RoomRadius; - - float t0, t1; - bool success = MathUtils.RaySphereIntersection( - vRoom0, vRoom1 - vRoom0, - Vector3.zero, radius, out t0, out t1); - if (!success) - { - // If this were more important, we could solve for the t of the closest approach - return scene0; - } - - // t0 is expected to be < 0 (room starts inside the fence) - // t1 is expected to be in [0, 1] (room ends outside the fence) - - // Constraints: - // - Lerp t must be in [0, 1]. (Do not move past the requested endpoint) - // - Lerp t should be as high as possible but < t1. (Do not exit the sphere) - float t = Mathf.Clamp(t1, 0, 1); - - TrTransform sceneT = TrTransform.TRS( - Vector3.Lerp(scene0.translation, scene1.translation, t), - scene0.rotation, - scene0.scale); - return MakeValidScenePose(sceneT, radialBounds); - } - - /// Returns a new ScenePose TrTransform that does not cause the room - /// to violate the hard scene bounds. - /// - /// scenePose - The current, possibly invalid scene pose - public static TrTransform MakeValidScenePose(TrTransform scenePose, float radialBounds) - { - scenePose.scale = Mathf.Clamp( - scenePose.scale, - SketchControlsScript.m_Instance.WorldTransformMinScale, - SketchControlsScript.m_Instance.WorldTransformMaxScale); - - // Anything not explicitly qualified is in room space. - - float roomRadius = App.Instance.RoomRadius; - Vector3 roomCenter = Vector3.zero; - - float fenceRadius = scenePose.scale * radialBounds - * App.METERS_TO_UNITS; - Vector3 fenceCenter = scenePose.translation; - - Vector3 moveRoom = GetOverlap_Antisphere_Sphere( - fenceCenter, fenceRadius, roomCenter, roomRadius); - Vector3 moveFence = -moveRoom; - - scenePose.translation += moveFence; - return scenePose; - } - - /// Clears data used by GetGrabWidgetHoldHistory() - /// Should be called any time m_GrabWidgetOneHandInfo changes - void ClearGrabWidgetHoldHistory() - { - m_GrabWidgetHoldHistory.Clear(); - } - - /// Collects data for use with GetGrabWidgetHoldHistory() - void UpdateGrabWidgetHoldHistory(InputManager.ControllerName name) - { - float t = Time.realtimeSinceStartup; - var info = InputManager.Controllers[(int)name]; - m_GrabWidgetHoldHistory.Enqueue(new GrabWidgetHoldPoint - { - m_Name = name, - m_BirthTime = t, - m_Pos = info.Transform.position, - m_Rot = info.Transform.rotation - }); - - // Trim the fat off our widget history - while (m_GrabWidgetHoldHistory.Count > 0 && - t - m_GrabWidgetHoldHistory.Peek().m_BirthTime >= kControlPointHistoryMaxTime) - { - m_GrabWidgetHoldHistory.Dequeue(); - } - } - - /// Returns possibly-smoothed linear and angular velocities. May fail. - /// Angular velocity is returned as an axial vector whose length() is degrees/second - bool GetGrabWidgetHoldHistory(out Vector3 vLinearVelocity, out Vector3 vAngularVelocity) - { - vLinearVelocity = vAngularVelocity = Vector3.zero; - if (m_GrabWidgetHoldHistory.Count < 2) - { - return false; - } - - // We need pairs of elements, so a simple foreach() won't quite work. - // Maybe using linq .First() and .Skip() would be okay. - using (IEnumerator enumerator = m_GrabWidgetHoldHistory.GetEnumerator()) - { - if (!enumerator.MoveNext()) - { - return false; - } - - // Infinitesimal rotations commute, and scaled-axis-angle rotations commute - // "better" than other rotation formats. - Vector3 totalDeltaTheta = Vector3.zero; - - GrabWidgetHoldPoint first = enumerator.Current; - GrabWidgetHoldPoint prev = first; - GrabWidgetHoldPoint current = first; - while (enumerator.MoveNext()) - { - current = enumerator.Current; - - // For our quaternion, find the difference, convert it to angle/axis, and sum it - // Find delta such that delta * prev = cur - // left-multiply because we want it in world-space. - // multiply vs prev since we want the delta that takes us forward in time - // rather than backward in time. - Quaternion dtheta = current.m_Rot * Quaternion.Inverse(prev.m_Rot); - // Assume the rotation took the shorter path - if (dtheta.w < 0) - { - dtheta.Set(-dtheta.x, -dtheta.y, -dtheta.z, -dtheta.w); - } - - float degrees; - Vector3 axis; - dtheta.ToAngleAxis(out degrees, out axis); - totalDeltaTheta += (axis * degrees); - prev = current; - } - - // Linear velocity calculation doesn't need to look at intermediate points - Vector3 totalDeltaPosition = current.m_Pos - first.m_Pos; - float totalDeltaTime = current.m_BirthTime - first.m_BirthTime; - if (totalDeltaTime == 0) - { - return false; - } - - vLinearVelocity = totalDeltaPosition / totalDeltaTime; - vAngularVelocity = totalDeltaTheta / totalDeltaTime; - return true; - } - } - - bool IsControllerNearWidget(InputManager.ControllerName name, GrabWidget widget) - { - Vector3 vControllerPos = InputManager.m_Instance.GetControllerAttachPointPosition(name); - return widget.GetActivationScore(vControllerPos, name) >= 0.0f; - } - - void RefreshCurrentGazeObject() - { - UnityEngine.Profiling.Profiler.BeginSample("SketchControlScript.RefreshCurrentGazeObject"); - int iPrevGazeObject = m_CurrentGazeObject; - m_CurrentGazeObject = -1; - bool bGazeAllowed = (m_CurrentInputState == InputState.Standard) - && !InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Activate) - && !m_SketchSurfacePanel.ActiveTool.InputBlocked() - && (m_GrabWidgetState == GrabWidgetState.None) - && !m_GrabBrush.grabbingWorld - && !m_PinCushion.IsShowing() - && !PointerManager.MainPointerIsPainting() - ; - - bool bGazeDeactivationOverrideWithInput = false; - List aAllPanels = m_PanelManager.GetAllPanels(); - - bool hasController = m_ControlsType == ControlsType.SixDofControllers; - - //if we're re-positioning a panel, keep it active - if (m_PositioningPanelWithHead) - { - m_CurrentGazeObject = iPrevGazeObject; - } - // Only activate gaze objects if we're in standard input mode, and if we don't have the 'draw' - // button held. - else if ((bGazeAllowed || (iPrevGazeObject != -1))) - { - //reset hit flags - for (int i = 0; i < m_GazeResults.Length; ++i) - { - m_GazeResults[i].m_HitWithGaze = false; - m_GazeResults[i].m_HitWithController = false; - m_GazeResults[i].m_WithinView = false; - } - - // If we're in controller mode, find the nearest colliding widget that might get in our way. - float fNearestWidget = 99999.0f; - if (hasController) - { - fNearestWidget = m_WidgetManager.DistanceToNearestWidget(m_GazeControllerRay); - } - - //check all panels for gaze hit - bool bRequireVisibilityCheck = !hasController || (iPrevGazeObject == -1); - if (m_PanelManager.PanelsAreStable()) - { - RaycastHit rHitInfo; - bool bRayHit = false; - int panelsHit = 0; - for (int i = 0; i < aAllPanels.Count; ++i) - { - // Ignore fixed panels when they are not visible. - if (!m_PanelManager.GazePanelsAreVisible() && aAllPanels[i].m_Panel.m_Fixed) - { - continue; - } - - if (aAllPanels[i].m_Panel.gameObject.activeSelf && aAllPanels[i].m_Panel.IsAvailable()) - { - //make sure this b-snap is in view - Vector3 vToPanel = aAllPanels[i].m_Panel.transform.position - m_CurrentGazeRay.origin; - vToPanel.Normalize(); - if (!bRequireVisibilityCheck || Vector3.Angle(vToPanel, m_CurrentGazeRay.direction) < m_GazeMaxAngleFromFacing) - { - if (hasController) - { - if (aAllPanels[i].m_Panel.HasMeshCollider()) - { - //make sure the angle between the pointer and the panel forward is below our max angle - if (Vector3.Angle(aAllPanels[i].m_Panel.transform.forward, m_GazeControllerRay.direction) < m_GazeMaxAngleFromPointing) - { - //make sure the angle between the user-to-panel and the panel forward is reasonable - if (Vector3.Angle(aAllPanels[i].m_Panel.transform.forward, vToPanel) < m_GazeMaxAngleFacingToForward) - { - m_GazeResults[i].m_WithinView = true; - - bRayHit = false; - bRayHit = aAllPanels[i].m_Panel.RaycastAgainstMeshCollider( - m_GazeControllerRay, out rHitInfo, m_GazeControllerPointingDistance); - - if (bRayHit) - { - //if the ray starts inside the panel, we won't get a good hit point, it'll just be zero - if (rHitInfo.point.sqrMagnitude > 0.1f) - { - if (rHitInfo.distance < fNearestWidget) - { - m_GazeResults[i].m_ControllerDistance = rHitInfo.distance; - m_GazeResults[i].m_ControllerPosition = rHitInfo.point; - m_GazeResults[i].m_HitWithController = true; - panelsHit++; - } - } - } - } - } - } - } - else - { - m_GazeResults[i].m_WithinView = true; - if (aAllPanels[i].m_Panel.GetCollider().Raycast(m_CurrentGazeRay, out rHitInfo, m_GazeMaxDistance)) - { - m_GazeResults[i].m_GazePosition = rHitInfo.point; - m_GazeResults[i].m_HitWithGaze = true; - } - } - } - } - } - - // No panels hit within normal ray distance. - // Check if previous panel still pointed to. - if (panelsHit == 0) - { - if (iPrevGazeObject != -1) - { - // Don't allow any panel to hold focus if it's facing away from the user. - Vector3 vToPanel = aAllPanels[iPrevGazeObject].m_Panel.transform.position - - m_CurrentGazeRay.origin; - vToPanel.Normalize(); - if (Vector3.Angle(aAllPanels[iPrevGazeObject].m_Panel.transform.forward, vToPanel) < - m_GazeMaxAngleFacingToForward) - { - float fDist = m_GazeControllerPointingDistance * 1.5f; - bRayHit = aAllPanels[iPrevGazeObject].m_Panel.RaycastAgainstMeshCollider( - m_GazeControllerRayActivePanel, out rHitInfo, fDist); - if (bRayHit) - { - if (rHitInfo.point.sqrMagnitude > 0.1f) - { - if (rHitInfo.distance < fNearestWidget) - { - m_GazeResults[iPrevGazeObject].m_ControllerDistance = rHitInfo.distance; - m_GazeResults[iPrevGazeObject].m_ControllerPosition = rHitInfo.point; - m_GazeResults[iPrevGazeObject].m_HitWithController = true; - } - } - } - } - } - } - } - - //determine what panel we hit, take the one with the lowest controller distance - float fControllerDist = 999.0f; - int iControllerIndex = -1; - if (hasController) - { - for (int i = 0; i < m_GazeResults.Length; ++i) - { - if (m_GazeResults[i].m_HitWithController) - { - if (m_GazeResults[i].m_ControllerDistance < fControllerDist) - { - iControllerIndex = i; - fControllerDist = m_GazeResults[i].m_ControllerDistance; - } - } - } - } - - //if we found something near our controller, take it - if (iControllerIndex != -1) - { - m_CurrentGazeObject = iControllerIndex; - m_CurrentGazeHitPoint = m_GazeResults[iControllerIndex].m_ControllerPosition; - - // TODO: This should not be hardcoded once multiple pointers are allowed. - m_GazeResults[m_CurrentGazeObject].m_ControllerName = InputManager.ControllerName.Brush; - if (m_GazeResults[m_CurrentGazeObject].m_HitWithGaze) - { - //average with the gaze position if we hit that too - m_CurrentGazeHitPoint += m_GazeResults[m_CurrentGazeObject].m_GazePosition; - m_CurrentGazeHitPoint *= 0.5f; - } - } - else - { - //nothing near the controller, see if we're looking at the previous - if (iPrevGazeObject != -1 && m_GazeResults[iPrevGazeObject].m_HitWithGaze) - { - m_CurrentGazeObject = iPrevGazeObject; - m_CurrentGazeHitPoint = m_GazeResults[m_CurrentGazeObject].m_GazePosition; - } - else - { - //controller and gaze not near panel, pick the first panel we're looking at - for (int i = 0; i < m_GazeResults.Length; ++i) - { - if (m_GazeResults[i].m_HitWithGaze) - { - m_CurrentGazeObject = i; - m_CurrentGazeHitPoint = m_GazeResults[i].m_GazePosition; - break; - } - } - } - } - - //forcing users to look away from gaze panel - if (m_EatInputGazeObject && m_CurrentGazeObject != -1) - { - m_CurrentGazeObject = -1; - } - else if (m_CurrentGazeObject == -1) - { - m_EatInputGazeObject = false; - } - } - - //if we're staring at a panel, keep our countdown fresh - if (m_CurrentGazeObject != -1 || m_ForcePanelActivation) - { - m_GazePanelDectivationCountdown = m_GazePanelDectivationDelay; - } - else - { - if (InputManager.m_Instance.GetCommandDown(InputManager.SketchCommands.Activate)) - { - bGazeDeactivationOverrideWithInput = true; - m_GazePanelDectivationCountdown = 0.0f; - } - else - { - m_GazePanelDectivationCountdown -= Time.deltaTime; - } - if (m_GazePanelDectivationCountdown > 0.0f) - { - m_CurrentGazeObject = iPrevGazeObject; - } - } - - //update our positioning timer - if (m_PositioningPanelWithHead) - { - m_PositioningTimer += m_PositioningSpeed * Time.deltaTime; - m_PositioningTimer = Mathf.Min(m_PositioningTimer, 1.0f); - } - else - { - m_PositioningTimer -= m_PositioningSpeed * Time.deltaTime; - m_PositioningTimer = Mathf.Max(m_PositioningTimer, 0.0f); - } - - //prime objects if we change targets - if (iPrevGazeObject != m_CurrentGazeObject) - { - //if we're switching panels, make sure the pointer doesn't streak - PointerManager.m_Instance.DisablePointerPreviewLine(); - - if (iPrevGazeObject != -1) - { - aAllPanels[iPrevGazeObject].m_Panel.PanelGazeActive(false); - aAllPanels[iPrevGazeObject].m_Panel.SetPositioningPercent(0.0f); - } - if (m_CurrentGazeObject != -1) - { - //make sure our line is disabled - if (m_GazeResults[m_CurrentGazeObject].m_ControllerName == InputManager.ControllerName.Brush) - { - PointerManager.m_Instance.EnableLine(false); - PointerManager.m_Instance.AllowPointerPreviewLine(false); - } - - aAllPanels[m_CurrentGazeObject].m_Panel.PanelGazeActive(true); - aAllPanels[m_CurrentGazeObject].m_Panel.SetPositioningPercent(0.0f); - - if (m_GazeResults[m_CurrentGazeObject].m_ControllerName == InputManager.ControllerName.Brush) - { - m_SketchSurfacePanel.RequestHideActiveTool(true); - } - } - else - { - //if we don't have a panel, we need to enable the pointer according to the current tool - PointerManager.m_Instance.RefreshFreePaintPointerAngle(); - PointerManager.m_Instance.RequestPointerRendering(m_SketchSurfacePanel.ShouldShowPointer()); - m_UIReticle.SetActive(false); - m_SketchSurfacePanel.RequestHideActiveTool(false); - if (!bGazeDeactivationOverrideWithInput) - { - m_SketchSurfacePanel.EatToolsInput(); - } - } - - m_PositioningPanelWithHead = false; - } - UnityEngine.Profiling.Profiler.EndSample(); - } - - void UpdateActiveGazeObject() - { - BasePanel currentPanel = m_PanelManager.GetPanel(m_CurrentGazeObject); - currentPanel.SetPositioningPercent(m_PositioningTimer); - bool hasController = m_ControlsType == ControlsType.SixDofControllers; - // Update positioning behavior. - if (m_PositioningPanelWithHead) - { - if (!InputManager.m_Instance.GetCommand(InputManager.SketchCommands.LockToHead) && - !InputManager.m_Instance.GetCommand(InputManager.SketchCommands.LockToController)) - { - // No more positioning. - m_PositioningPanelWithHead = false; - m_PanelManager.m_SweetSpot.EnableBorderSphere(false, Vector3.zero, 0.0f); - currentPanel.PanelHasStoppedMoving(); - } - else - { - //lock the panel to the sweet spot bounds in the direction the user is looking - Quaternion qDiff = m_CurrentHeadOrientation * Quaternion.Inverse(m_PositioningPanelBaseHeadRotation); - Vector3 vAdjustedOffset = qDiff * m_PositioningPanelOffset; - - Vector3 vNewPos = m_PanelManager.m_SweetSpot.transform.position + vAdjustedOffset; - currentPanel.transform.position = vNewPos; - - vAdjustedOffset.Normalize(); - currentPanel.transform.forward = vAdjustedOffset; - - float fHighlightRadius = currentPanel.m_BorderSphereHighlightRadius; - m_PanelManager.m_SweetSpot.EnableBorderSphere(true, vNewPos, fHighlightRadius * m_PositioningTimer); - - //once we've moved this panel, run the simulation on the other panels to resolve collisions - m_PanelManager.DoCollisionSimulationForKeyboardMouse(currentPanel); - } - } - else - { - // It's possible that, on this frame, before this function was called, active gaze was pulled - // from this panel. In this case, we want to skip updating this frame. - // This happens when a panel has gaze and world grab dismisses all panels, for example. - if (currentPanel.IsActive()) - { - //orient to gaze - if (hasController) - { - currentPanel.UpdatePanel(m_GazeControllerRay.direction, m_CurrentGazeHitPoint); - } - else - { - currentPanel.UpdatePanel(m_CurrentGazeRay.direction, m_CurrentGazeHitPoint); - } - } - - if (!hasController) - { - //lock to head if we're holding a lock button.. - bool bLockToHead = InputManager.m_Instance.GetCommand(InputManager.SketchCommands.LockToHead) || - InputManager.m_Instance.GetCommand(InputManager.SketchCommands.LockToController); - - if (bLockToHead) - { - m_PositioningPanelWithHead = true; - m_PositioningPanelBaseHeadRotation = m_CurrentHeadOrientation; - m_PositioningPanelOffset = currentPanel.transform.position - - m_PanelManager.m_SweetSpot.transform.position; - - currentPanel.ResetPanelFlair(); - - //prime all other panels for movement - m_PanelManager.PrimeCollisionSimForKeyboardMouse(); - } - } - - PointerManager.m_Instance.RequestPointerRendering(false); - currentPanel.UpdateReticleOffset(m_MouseDeltaX, m_MouseDeltaY); - } - - // Keep reticle locked in the right spot. - Vector3 reticlePos = Vector3.zero; - Vector3 reticleForward = Vector3.zero; - if (hasController) - { - currentPanel.GetReticleTransformFromPosDir(m_CurrentGazeHitPoint, - m_GazeControllerRay.direction, out reticlePos, out reticleForward); - } - else - { - currentPanel.GetReticleTransform(out reticlePos, out reticleForward, - (m_ControlsType == ControlsType.ViewingOnly)); - } - - SetUIReticleTransform(reticlePos, -reticleForward); - m_UIReticle.SetActive(GetGazePanelActivationRatio() >= 1.0f); - } - - public void ResetActivePanel() - { - m_PanelManager.ResetPanel(m_CurrentGazeObject); - PointerManager.m_Instance.DisablePointerPreviewLine(); - m_PositioningPanelWithHead = false; - m_CurrentGazeObject = -1; - } - - void UpdatePanInput() - { - if (Mouse.current.rightButton.isPressed) - { - Vector3 vPanDiff = Vector3.zero; - vPanDiff += (Vector3.right * m_MouseDeltaXScaled); - vPanDiff += (Vector3.up * m_MouseDeltaYScaled); - Vector3 vSurfacePos = m_SketchSurface.transform.position; - m_SketchSurface.transform.position = vSurfacePos + vPanDiff; - } - else - { - float fCurrentTime = Time.realtimeSinceStartup; - if (fCurrentTime - m_PositionOffsetResetTapTime < m_DoubleTapWindow) - { - if (m_CurrentGazeObject == -1) - { - ResetGrabbedPose(); - } - } - m_PositionOffsetResetTapTime = fCurrentTime; - - SwitchState(InputState.Standard); - } - } - - void UpdateRotationInput() - { - if (InputManager.m_Instance.GetCommand(InputManager.SketchCommands.PivotRotation)) - { - bool bAltInputActive = InputManager.m_Instance.GetCommand(InputManager.SketchCommands.AltActivate); - bool bRollRotation = m_RotationRollActive || bAltInputActive || m_CurrentRotationType == RotationType.RollOnly; - m_RotationIcon.SetActive(bRollRotation); - if (bRollRotation) - { - m_RotationCursorOffset.x += m_MouseDeltaXScaled; - float fRotationAmount = m_RotationCursorOffset.x * -m_RotationRollScalar; - - Quaternion qOffsetRotation = Quaternion.AngleAxis(fRotationAmount, m_SurfaceForward); - Quaternion qNewRotation = qOffsetRotation * m_RotationOrigin; - m_SketchSurface.transform.rotation = qNewRotation; - - m_RotationRollActive = true; - m_RotationCursor.gameObject.SetActive(false); - } - else - { - //update offset with mouse movement - m_RotationCursorOffset.x += m_MouseDeltaXScaled; - m_RotationCursorOffset.y += m_MouseDeltaYScaled; - - //get offset in model space - Vector3 vSurfaceBounds = m_SketchSurface.transform.localScale * 0.5f; - m_RotationCursorOffset.x = Mathf.Clamp(m_RotationCursorOffset.x, -vSurfaceBounds.x, vSurfaceBounds.x); - m_RotationCursorOffset.y = Mathf.Clamp(m_RotationCursorOffset.y, -vSurfaceBounds.y, vSurfaceBounds.y); - float fCursorOffsetDist = m_RotationCursorOffset.magnitude; - float fMaxCursorOffsetDist = vSurfaceBounds.x; - - //transform offset in to world space - Vector3 vTransformedOffset = m_RotationOrigin * m_RotationCursorOffset; - vTransformedOffset.Normalize(); - - //get world space rotation axis - Vector3 vSketchSurfaceRotationAxis = Vector3.Cross(vTransformedOffset, m_SurfaceForward); - vSketchSurfaceRotationAxis.Normalize(); - - //amount to rotate is determined by offset distance from origin - float fSketchSurfaceRotationAngle = Mathf.Min(fCursorOffsetDist / fMaxCursorOffsetDist, 1.0f); - fSketchSurfaceRotationAngle *= m_RotationMaxAngle; - - //set new surface rotation by combining base rotation with angle/axis rotation - Quaternion qOffsetRotation = Quaternion.AngleAxis(fSketchSurfaceRotationAngle, vSketchSurfaceRotationAxis); - Quaternion qNewRotation = qOffsetRotation * m_RotationOrigin; - m_SketchSurface.transform.rotation = qNewRotation; - - //set position of rotation cursor - Vector3 vNewTransformedOffset = qNewRotation * m_RotationCursorOffset; - m_RotationCursor.transform.position = m_SketchSurface.transform.position + vNewTransformedOffset; - m_RotationCursor.transform.rotation = qNewRotation; - - //set position of guide lines - Vector2 vToCenter = m_RotationCursorOffset; - vToCenter.Normalize(); - float fOffsetAngle = Vector2.Angle(vToCenter, Vector2.up); - m_RotationCursor.PositionCursorLines(m_SketchSurface.transform.position, m_SketchSurface.transform.forward, fOffsetAngle, vSurfaceBounds.x * 2.0f); - } - } - else - { - float fCurrentTime = Time.realtimeSinceStartup; - if (fCurrentTime - m_RotationResetTapTime < m_DoubleTapWindow) - { - //reset drawing surface rotation - m_SketchSurface.transform.rotation = Quaternion.identity; - } - m_RotationResetTapTime = fCurrentTime; - - m_SurfaceForward = m_SketchSurface.transform.forward; - m_SurfaceRight = m_SketchSurface.transform.right; - m_SurfaceUp = m_SketchSurface.transform.up; - - if (!m_RotationRollActive && m_AutoOrientAfterRotation && m_SketchSurfacePanel.IsSketchSurfaceToolActive()) - { - //get possible auto rotations - Quaternion qQuatUp = OrientSketchSurfaceToUp(); - Quaternion qQuatForward = OrientSketchSurfaceToForward(); - - //get the angle between our current and desired auto-rotation - float toUpAngle = Quaternion.Angle(qQuatUp, m_SketchSurface.transform.rotation); - float toForwardAngle = Quaternion.Angle(qQuatForward, m_SketchSurface.transform.rotation); - - //set our new rotation to be whichever autorotation is closeset - Quaternion qNewRotation; - if (Mathf.Abs(toUpAngle) < Mathf.Abs(toForwardAngle)) - { - qNewRotation = qQuatUp; - } - else - { - qNewRotation = qQuatForward; - } - - //update the sketch surface - m_SketchSurface.transform.rotation = qNewRotation; - - m_SurfaceForward = m_SketchSurface.transform.forward; - m_SurfaceRight = m_SketchSurface.transform.right; - m_SurfaceUp = m_SketchSurface.transform.up; - } - - SwitchState(InputState.Standard); - } - } - - void UpdateHeadLockInput() - { - if (InputManager.m_Instance.GetCommand(InputManager.SketchCommands.LockToHead)) - { - //compute new position/orientation of sketch surface - Vector3 vTransformedOffset = m_CurrentHeadOrientation * m_SurfaceLockOffset; - Vector3 vSurfacePos = m_CurrentGazeRay.origin + vTransformedOffset; - - Quaternion qDiff = m_CurrentHeadOrientation * Quaternion.Inverse(m_SurfaceLockBaseHeadRotation); - Quaternion qNewSurfaceRot = qDiff * m_SurfaceLockBaseSurfaceRotation; - - m_SketchSurface.transform.position = vSurfacePos; - m_SketchSurface.transform.rotation = qNewSurfaceRot; - } - else - { - m_SurfaceForward = m_SketchSurface.transform.forward; - m_SurfaceRight = m_SketchSurface.transform.right; - m_SurfaceUp = m_SketchSurface.transform.up; - - SwitchState(InputState.Standard); - } - } - - void UpdateControllerLock() - { - if (InputManager.m_Instance.GetCommand(InputManager.SketchCommands.LockToController)) - { - //compute new position/orientation of sketch surface - Vector3 vControllerDiff = InputManager.m_Instance.GetControllerPosition(m_SurfaceLockActingController) - m_SurfaceLockBaseControllerPosition; - m_SketchSurface.transform.position = m_SurfaceLockBaseSurfacePosition + (vControllerDiff * m_SurfaceLockControllerScalar); - - Quaternion qDiff = InputManager.m_Instance.GetControllerRotation(m_SurfaceLockActingController) * Quaternion.Inverse(m_SurfaceLockBaseControllerRotation); - m_SketchSurface.transform.rotation = qDiff * m_SurfaceLockBaseSurfaceRotation; - } - else - { - m_SurfaceForward = m_SketchSurface.transform.forward; - m_SurfaceRight = m_SketchSurface.transform.right; - m_SurfaceUp = m_SketchSurface.transform.up; - - SwitchState(InputState.Standard); - } - } - - void UpdatePushPullInput() - { - bool bRotationActive = InputManager.m_Instance.GetCommand(InputManager.SketchCommands.PivotRotation); - bool bInputActive = InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Activate); - bool bAltInputActive = InputManager.m_Instance.GetCommand(InputManager.SketchCommands.AltActivate); - - if (bRotationActive && bInputActive) - { - SwitchState(InputState.Rotation); - } - else if (bAltInputActive) - { - Vector3 vPos = m_SketchSurface.transform.position; - float fBigDiff = Mathf.Abs(m_MouseDeltaXScaled) > Mathf.Abs(m_MouseDeltaYScaled) ? -m_MouseDeltaXScaled : m_MouseDeltaYScaled; - vPos += Vector3.forward * fBigDiff; - - m_SketchSurface.transform.position = vPos; - } - else - { - SwitchState(InputState.Standard); - } - } - - void UpdateSaveInput() - { - if (!InputManager.m_Instance.GetKeyboardShortcut(InputManager.KeyboardShortcut.Save)) - { - SwitchState(InputState.Standard); - } - } - - void UpdateLoadInput() - { - if (!InputManager.m_Instance.GetKeyboardShortcut(InputManager.KeyboardShortcut.Load)) - { - SwitchState(InputState.Standard); - } - } - - void OnBrushSetToDefault() - { - BrushDescriptor rDefaultBrush = BrushCatalog.m_Instance.DefaultBrush; - PointerManager.m_Instance.SetBrushForAllPointers(rDefaultBrush); - PointerManager.m_Instance.SetAllPointersBrushSize01(0.5f); - PointerManager.m_Instance.MarkAllBrushSizeUsed(); - } - - public void AssignControllerMaterials(InputManager.ControllerName controller) - { - ControllerGeometry geometry = InputManager.GetControllerGeometry(controller); - - // Start from a clean state - geometry.ResetAll(); - - // If the tutorial is enabled, override all materials. - if (TutorialManager.m_Instance.TutorialActive()) - { - InputManager.m_Instance - .GetControllerTutorial(controller) - ?.AssignControllerMaterials(controller); - return; - } - - // If we're grabbing the world, get the materials from the world transform panel. - if (m_GrabBrush.grabbingWorld && controller == InputManager.ControllerName.Brush) - { - TrTransform scenePose = App.Scene.Pose; - if (scenePose.scale != 1 || scenePose.translation != Vector3.zero - || scenePose.rotation != Quaternion.identity) - { - geometry.ShowWorldTransformReset(); - } - return; - } - else if (m_GrabWand.grabbingWorld && controller == InputManager.ControllerName.Wand) - { - TrTransform scenePose = App.Scene.Pose; - if (scenePose.scale != 1 || scenePose.translation != Vector3.zero - || scenePose.rotation != Quaternion.identity) - { - geometry.ShowWorldTransformReset(); - } - return; - } - - // Not grabbing the world, so see if we're grabbing a widget. - if (m_GrabWidgetState != GrabWidgetState.None) - { - m_CurrentGrabWidget.AssignControllerMaterials(controller); - return; - } - - // See if we're highlighting a widget and if that matters. - if (m_CurrentGrabWidget != null && m_CurrentGrabWidget.HasHoverInteractions()) - { - m_CurrentGrabWidget.AssignHoverControllerMaterials(controller); - return; - } - - // Not grabbing the world or a widget, see if we're interacting with a panel. - if (controller == InputManager.ControllerName.Brush && m_CurrentGazeObject != -1) - { - BasePanel panel = m_PanelManager.GetPanel(m_CurrentGazeObject); - panel.AssignControllerMaterials(controller); - return; - } - - // Defaults. - if (controller == InputManager.ControllerName.Wand) - { - if (App.CurrentState != App.AppState.Standard || m_PanelManager.IntroSketchbookMode) - { - // If app is not in standard mode, the actions represented by subsequent material - // assigments cannot be taken. - return; - } - bool creatingStroke = PointerManager.m_Instance.IsMainPointerCreatingStroke(); - bool allowPainting = App.Instance.IsInStateThatAllowsPainting(); - - InputManager.Wand.Geometry.ShowRotatePanels(); - InputManager.Wand.Geometry.ShowUndoRedo(CanUndo() && !creatingStroke && allowPainting, - CanRedo() && !creatingStroke && allowPainting); - } - - // Show the pin cushion icon on the button if it's available. - if (controller == InputManager.ControllerName.Brush && CanUsePinCushion()) - { - InputManager.Brush.Geometry.ShowPinCushion(); - } - - // Finally, override with tools. - m_SketchSurfacePanel.AssignControllerMaterials(controller); - } - - public float GetControllerPadShaderRatio( - InputManager.ControllerName controller, VrInput input) - { - // If we're interacting with a panel, get touch ratio from the panel. - if (controller == InputManager.ControllerName.Brush && m_CurrentGazeObject != -1) - { - BasePanel panel = m_PanelManager.GetPanel(m_CurrentGazeObject); - return panel.GetControllerPadShaderRatio(controller); - } - return SketchSurfacePanel.m_Instance.GetCurrentToolSizeRatio(controller, input); - } - - void SwitchState(InputState rDesiredState) - { - //exit current state - switch (m_CurrentInputState) - { - case InputState.Pan: - m_TransformGizmoScript.ResetTransform(); - break; - case InputState.PushPull: - m_TransformGizmoScript.ResetTransform(); - break; - case InputState.Rotation: - m_RotationRollActive = false; - m_RotationIcon.SetActive(false); - m_RotationCursor.gameObject.SetActive(false); - break; - } - - bool bSketchSurfaceToolActive = m_SketchSurfacePanel.IsSketchSurfaceToolActive(); - - //enter new state - switch (rDesiredState) - { - case InputState.Pan: - m_TransformGizmoScript.SetTransformForPan(); - break; - case InputState.PushPull: - m_TransformGizmoScript.SetTransformForPushPull(); - break; - case InputState.Rotation: - if (bSketchSurfaceToolActive) - { - m_SketchSurface.transform.position = PointerManager.m_Instance.MainPointer.transform.position; - m_SketchSurfacePanel.ResetReticleOffset(); - } - m_RotationOrigin = m_SketchSurface.transform.rotation; - m_RotationCursorOffset = Vector2.zero; - m_RotationCursor.transform.position = m_SketchSurface.transform.position; - m_RotationCursor.transform.rotation = m_SketchSurface.transform.rotation; - m_RotationCursor.ClearCursorLines(m_SketchSurface.transform.position); - m_RotationCursor.gameObject.SetActive(bSketchSurfaceToolActive); - break; - case InputState.HeadLock: - m_SurfaceLockBaseHeadRotation = m_CurrentHeadOrientation; - m_SurfaceLockBaseSurfaceRotation = m_SketchSurface.transform.rotation; - m_SurfaceLockOffset = m_SketchSurface.transform.position - m_CurrentGazeRay.origin; - m_SurfaceLockOffset = Quaternion.Inverse(m_SurfaceLockBaseHeadRotation) * m_SurfaceLockOffset; - break; - case InputState.ControllerLock: - if (bSketchSurfaceToolActive) - { - m_SketchSurface.transform.position = PointerManager.m_Instance.MainPointer.transform.position; - m_SketchSurfacePanel.ResetReticleOffset(); - } - m_SurfaceLockActingController = InputManager.m_Instance.GetDominantController(InputManager.SketchCommands.LockToController); - m_SurfaceLockBaseSurfaceRotation = m_SketchSurface.transform.rotation; - m_SurfaceLockBaseControllerRotation = InputManager.m_Instance.GetControllerRotation(m_SurfaceLockActingController); - m_SurfaceLockBaseSurfacePosition = m_SketchSurface.transform.position; - m_SurfaceLockBaseControllerPosition = InputManager.m_Instance.GetControllerPosition(m_SurfaceLockActingController); - m_SurfaceLockControllerScalar = m_SketchSurfacePanel.m_PanelSensitivity / m_SurfaceLockControllerBaseScalar; - break; - case InputState.Save: - IssueGlobalCommand(GlobalCommands.Save); - break; - case InputState.Load: - IssueGlobalCommand(GlobalCommands.Load); - break; - } - - m_CurrentInputState = rDesiredState; - } - - public void RequestPanelsVisibility(bool bVisible) - { - // Always false in viewonly mode - bVisible = m_ViewOnly ? false : bVisible; - m_PanelsVisibilityRequested = bVisible; - } - - Quaternion OrientSketchSurfaceToUp() - { - //project the world up vector on to the surface plane - Vector3 vUpOnSurfacePlane = Vector3.up - (Vector3.Dot(Vector3.up, m_SurfaceForward) * m_SurfaceForward); - vUpOnSurfacePlane.Normalize(); - - //get the angle between the surface up and the projected world up - float fUpOnSurfacePlaneAngle = Vector3.Angle(vUpOnSurfacePlane, m_SurfaceUp); - Vector3 vUpCross = Vector3.Cross(vUpOnSurfacePlane, m_SurfaceUp); - vUpCross.Normalize(); - if (Vector3.Dot(vUpCross, m_SurfaceForward) > 0.0f) - { - fUpOnSurfacePlaneAngle *= -1.0f; - } - - //rotate around the surface foward by the angle diff - Quaternion qOrientToUp = Quaternion.AngleAxis(fUpOnSurfacePlaneAngle, m_SurfaceForward); - Quaternion qNewRotation = qOrientToUp * m_SketchSurface.transform.rotation; - return qNewRotation; - } - - Quaternion OrientSketchSurfaceToForward() - { - //project the world forward vector on to the surface plane - Vector3 vForwardOnSurfacePlane = Vector3.forward - (Vector3.Dot(Vector3.forward, m_SurfaceForward) * m_SurfaceForward); - vForwardOnSurfacePlane.Normalize(); - - //get the angle between the surface up and the projected world forward - float fForwardOnSurfacePlaneAngle = Vector3.Angle(vForwardOnSurfacePlane, m_SurfaceUp); - Vector3 vUpCross = Vector3.Cross(vForwardOnSurfacePlane, m_SurfaceUp); - vUpCross.Normalize(); - if (Vector3.Dot(vUpCross, m_SurfaceForward) > 0.0f) - { - fForwardOnSurfacePlaneAngle *= -1.0f; - } - - //rotate around the surface foward by the angle diff - Quaternion qOrientToForward = Quaternion.AngleAxis(fForwardOnSurfacePlaneAngle, m_SurfaceForward); - Quaternion qNewRotation = qOrientToForward * m_SketchSurface.transform.rotation; - return qNewRotation; - } - - /// Reset the scene or the canvas, depending on the current mode - public void ResetGrabbedPose(bool everything = false) - { - //update sketch surface position with offset to sweet spot - m_SketchSurface.transform.position = m_PanelManager.GetSketchSurfaceResetPos(); - if (everything) - { - App.Scene.Pose = TrTransform.identity; - Coords.CanvasLocalPose = TrTransform.identity; - } - App.Scene.Pose = TrTransform.identity; - - //reset orientation and pointer - ResetSketchSurfaceOrientation(); - m_SketchSurfacePanel.ResetReticleOffset(); - PointerManager.m_Instance.DisablePointerPreviewLine(); - PointerManager.m_Instance.SetPointerPreviewLineDelayTimer(); - } - - public void ResetSketchSurfaceOrientation() - { - m_SketchSurface.transform.rotation = Quaternion.identity; - m_SurfaceForward = m_SketchSurface.transform.forward; - m_SurfaceRight = m_SketchSurface.transform.right; - m_SurfaceUp = m_SketchSurface.transform.up; - } - - float GetAppropriateMovementScalar() - { - switch (m_CurrentInputState) - { - case InputState.Pan: return m_PanScalar; - case InputState.Rotation: return m_RotationScalar; - case InputState.PushPull: return m_PushPullScale; - } - - return 1.0f; - } - - // TODO - it'd be great if we could disentangle this from the multicam. - IEnumerator RenderPathAndQuit() - { -#if USD_SUPPORTED - App.Instance.SetDesiredState(App.AppState.OfflineRendering); - SketchSurfacePanel.m_Instance.EnableSpecificTool(BaseTool.ToolType.MultiCamTool); - MultiCamTool multiCam = SketchSurfacePanel.m_Instance.ActiveTool as MultiCamTool; - Debug.Assert(multiCam != null); // Something's gone wrong if we've been unable to find multicam! - if (multiCam == null) - { - yield break; - } - multiCam.ExternalObjectForceCameraStyle(MultiCamStyle.Video); - MultiCamCaptureRig.ForceClippingPlanes(MultiCamStyle.Video); - // Give the video tool time to switch - TODO - be a little more graceful here - yield return new WaitForSeconds(2); - // Make sure the videos have had time to load, and set playing ones to start - while (VideoCatalog.Instance.IsScanning) - { - yield return null; - } - foreach (var widget in WidgetManager.m_Instance.VideoWidgets) - { - if (widget.VideoController.Playing) - { - widget.VideoController.Position = 0; - } - } - yield return null; - var ssMgr = MultiCamCaptureRig.ManagerFromStyle(MultiCamStyle.Video); - ssMgr.SetScreenshotResolution(App.UserConfig.Video.OfflineResolution); - multiCam.StartVideoCapture(MultiCamTool.GetSaveName(MultiCamStyle.Video), offlineRender: true); - App.Instance.FrameCountDisplay.gameObject.SetActive(true); - App.Instance.FrameCountDisplay.SetFramesTotal(VideoRecorderUtils.NumFramesInUsdSerializer); - while (VideoRecorderUtils.ActiveVideoRecording != null) - { - App.Instance.FrameCountDisplay.SetCurrentFrame( - VideoRecorderUtils.ActiveVideoRecording.FrameCount); - yield return null; - } - ssMgr.SetScreenshotResolution(App.UserConfig.Video.Resolution); -#else - Debug.LogError("Render path requires USD support"); - yield return null; -#endif - QuitApp(); - } - - IEnumerator ExportListAndQuit() - { - App.Config.m_ForceDeterministicBirthTimeForExport = true; - List filesToExport = new List(); - foreach (string filePattern in App.Config.m_FilePatternsToExport) - { - bool absolute = Path.IsPathRooted(filePattern); - string directory = absolute ? Path.GetDirectoryName(filePattern) : App.UserSketchPath(); - string filename = Path.GetFileName(filePattern); - var tiltFiles = Directory.GetFiles(directory, filename); - filesToExport.AddRange(tiltFiles); - // Also look at .tilt files which have been unzipped into directory format - var tiltDirs = Directory.GetDirectories(directory, filename) - .Where(n => n.EndsWith(".tilt")); - filesToExport.AddRange(tiltDirs); - } - - using (var coroutine = LoadAndExportList(filesToExport)) - { - while (coroutine.MoveNext()) - { - yield return coroutine.Current; - } - } - QuitApp(); - } - - void QuitApp() - { - // We're done! Quit! -#if UNITY_EDITOR - UnityEditor.EditorApplication.isPlaying = false; -#else - Application.Quit(); -#endif - } - - // This coroutine must be run to completion or disposed. - IEnumerator LoadAndExportList(List filenames) - { - foreach (var filename in filenames) - { - using (var coroutine = LoadAndExport(filename)) - { - while (coroutine.MoveNext()) - { - yield return coroutine.Current; - } - } - } - } - - // This coroutine must be run to completion or disposed. - IEnumerator LoadAndExportAll() - { - SketchSet sketchSet = SketchCatalog.m_Instance.GetSet(SketchSetType.User); - for (int i = 0; i < SketchCatalog.m_Instance.GetSet(SketchSetType.User).NumSketches; ++i) - { - SceneFileInfo rInfo = sketchSet.GetSketchSceneFileInfo(i); - using (var coroutine = LoadAndExport(rInfo.FullPath)) - { - while (coroutine.MoveNext()) - { - yield return coroutine.Current; - } - } - } - } - - /// Loads a .tilt file completely. - /// This may be slightly buggy; it's not currently used for production. - /// This coroutine must be run to completion or disposed. - public IEnumerable LoadTiltFile(string filename) - { - using (var unused = new SceneSettings.RequestInstantSceneSwitch()) - { - IssueGlobalCommand( - GlobalCommands.LoadNamedFile, - iParam1: (int)LoadSpeed.Quick, sParam: filename); - yield return null; - while (App.Instance.IsLoading()) - { - yield return null; - } - - // I don't know why App.Instance.IsLoading() doesn't cover this, but it doesn't. - while (m_WidgetManager.CreatingMediaWidgets) - { - yield return null; - } - while (WidgetManager.m_Instance.AreMediaWidgetsStillLoading()) - { - yield return null; - } - - // This is kind of a hack. - // Despite the RequestInstantSceneSwitch above, I think scene colors still require - // a few frames to settle; also, GrabWidgets need to register themselves on the - // first frame, etc. - for (int i = 0; i < 10; ++i) - { - yield return null; - } - } - } - - // This coroutine must be run to completion or disposed. - IEnumerator LoadAndExport(string filename) - { - foreach (var val in LoadTiltFile(filename)) - { - yield return val; - } - using (var coroutine = ExportCoroutine()) - { - while (coroutine.MoveNext()) - { - yield return coroutine.Current; - } - } - } - - IEnumerator ExportCoroutine() - { - return OverlayManager.m_Instance.RunInCompositor( - OverlayType.Export, () => - { - // Sort of a kludge: put stuff back into the main canvas - SelectionManager.m_Instance.ClearActiveSelection(); - Export.ExportScene(); - }, 0.25f, false, true); - } - - private void SaveModel() - { -#if USD_SUPPORTED - var current = SaveLoadScript.m_Instance.SceneFile; - string basename = (current.Valid) - ? Path.GetFileNameWithoutExtension(current.FullPath) - : "Untitled"; - string directoryName = FileUtils.GenerateNonexistentFilename( - App.ModelLibraryPath(), basename, ""); - - string usdname = Path.Combine(directoryName, basename + ".usd"); - // TODO: export selection only, though this is still only experimental. The blocking - // issue to implement this is that the export collector needs to expose this as an option. - // - // SelectionManager.m_Instance.HasSelection - // ? SelectionManager.m_Instance.SelectedStrokes - // : null - ExportUsd.ExportPayload(usdname); - OutputWindowScript.m_Instance.CreateInfoCardAtController( - InputManager.ControllerName.Brush, "Model created!"); -#endif - } - - /// Generates a view from the previous thumbnail viewpoint. - public void GenerateReplacementSaveIcon() - { - if (SaveLoadScript.m_Instance.LastThumbnail_SS.HasValue) - { - TrTransform thumbnailInGlobalSpace = App.Scene.Pose * - SaveLoadScript.m_Instance.LastThumbnail_SS.Value; - - m_SaveIconTool.ProgrammaticCaptureSaveIcon(thumbnailInGlobalSpace.translation, - thumbnailInGlobalSpace.rotation); - } - else - { - GenerateBestGuessSaveIcon(); - } - } - - public void GenerateBestGuessSaveIcon() - { - TrTransform camXform = GenerateBestGuessSaveIconTransform(); - m_SaveIconTool.ProgrammaticCaptureSaveIcon(camXform.translation, camXform.rotation); - } - - /// This positions the save icon camera at the user's head position, and faces it towards - /// the most recent strokes the user has created. - /// If there are no strokes, it faces towards the 'most recent' models. - /// Sadly we cannot really mix the two as we don't know when the models were instantiated. - public TrTransform GenerateBestGuessSaveIconTransform(int itemsToEnumerate = 0) - { - if (itemsToEnumerate == 0) - { - itemsToEnumerate = m_NumStrokesForSaveIcon; - } - int startIndex = Mathf.Max(0, SketchMemoryScript.AllStrokesCount() - itemsToEnumerate); - var lastFewStrokes = SketchMemoryScript.AllStrokes().Skip(startIndex).ToArray(); - - Bounds bounds; - if (lastFewStrokes.Length > 0) - { - bounds = new Bounds(lastFewStrokes.First().m_ControlPoints.First().m_Pos, Vector3.zero); - foreach (var stroke in lastFewStrokes.Skip(1)) - { - bounds.Encapsulate(stroke.m_ControlPoints.First().m_Pos); - bounds.Encapsulate(stroke.m_ControlPoints.Last().m_Pos); - } - } - else - { - // If we have no strokes, just use the aggregates bounding boxes of the blocks models. - var models = m_WidgetManager.ModelWidgets.ToArray(); - // we should always have models to get here, but just in case... - if (models.Length > 0) - { - startIndex = Mathf.Max(0, models.Length - itemsToEnumerate); - bounds = models[startIndex].WorldSpaceBounds; - for (int i = startIndex + 1; i < models.Length; ++i) - { - bounds.Encapsulate(models[i].WorldSpaceBounds); - } - } - else - { - bounds = new Bounds(new Vector3(0, 1, -100000), Vector3.one); // some point in the distance - } - } - - Vector3 camPos = ViewpointScript.Head.position; - Vector3 worldPos = App.Scene.Pose.MultiplyPoint(bounds.center); - Quaternion direction = Quaternion.LookRotation(worldPos - camPos); - return TrTransform.TR(camPos, direction); - } - - - public void GenerateBoundingBoxSaveIcon() - { - Vector3 vNewCamPos; - { - Bounds rCanvasBounds = App.Scene.AllCanvases - .Select(canvas => canvas.GetCanvasBoundingBox()) - .Aggregate((b1, b2) => - { - b1.Encapsulate(b2); - return b1; - }); - - //position the camera at the center of the canvas bounds - vNewCamPos = rCanvasBounds.center; - - //back the camera up, along -z until we can see the extent of the bounds - float fCanvasWidth = rCanvasBounds.max.x - rCanvasBounds.min.x; - float fCanvasHeight = rCanvasBounds.max.y - rCanvasBounds.min.y; - float fLargerExtent = Mathf.Max(fCanvasHeight, fCanvasWidth); - - //half fov for camera - float fHalfFOV = m_SaveIconTool.ScreenshotManager.LeftEye.fieldOfView * 0.5f; - - //TODO: find the real reason this isn't working as it should - float fMagicNumber = 1.375f; - - //set new cam position and zero out orientation - float fBackupDistance = (fLargerExtent * 0.5f) - * Mathf.Tan(Mathf.Deg2Rad * fHalfFOV) * fMagicNumber; - vNewCamPos.z = rCanvasBounds.min.z - fBackupDistance; - } - - m_SaveIconTool.ProgrammaticCaptureSaveIcon(vNewCamPos, Quaternion.identity); - } - - private void MergeBrushStrokes(SceneFileInfo fileInfo) - { - m_PanelManager.ToggleSketchbookPanels(isLoadingSketch: true); - PointerManager.m_Instance.EnablePointerStrokeGeneration(true); - if (SaveLoadScript.m_Instance.Load(fileInfo, true)) - { - SketchMemoryScript.m_Instance.SetPlaybackMode(m_SketchPlaybackMode, m_DefaultSketchLoadSpeed); - SketchMemoryScript.m_Instance.BeginDrawingFromMemory(bDrawFromStart: true, false, false); - // the order of these two lines are important as ExitIntroSketch is setting the - // color of the pointer and we need the color to be set before we go to the Loading - // state. App script's ShouldTintControllers allow the controller to be tinted only - // when the app is in the standard mode. That was there to prevent the controller color - // from flickering while in the intro mode. - App.Instance.ExitIntroSketch(); - App.Instance.SetDesiredState(App.AppState.QuickLoad); - } - } - - public void LoadSketch(SceneFileInfo fileInfo, bool quickload = false, bool additive = false) - { - LightsControlScript.m_Instance.DiscoMode = false; - m_WidgetManager.FollowingPath = false; - m_WidgetManager.CameraPathsVisible = false; - m_WidgetManager.DestroyAllWidgets(); - m_PanelManager.ToggleSketchbookPanels(isLoadingSketch: true); - ResetGrabbedPose(everything: true); - PointerManager.m_Instance.EnablePointerStrokeGeneration(true); - if (SaveLoadScript.m_Instance.Load(fileInfo, additive)) - { - SketchMemoryScript.m_Instance.SetPlaybackMode(m_SketchPlaybackMode, m_DefaultSketchLoadSpeed); - SketchMemoryScript.m_Instance.BeginDrawingFromMemory(bDrawFromStart: true); - // the order of these two lines are important as ExitIntroSketch is setting the - // color of the pointer and we need the color to be set before we go to the Loading - // state. App script's ShouldTintControllers allow the controller to be tinted only - // when the app is in the standard mode. That was there to prevent the controller color - // from flickering while in the intro mode. - App.Instance.ExitIntroSketch(); - App.Instance.SetDesiredState(quickload ? App.AppState.QuickLoad : App.AppState.Loading); - } - QualityControls.m_Instance.ResetAutoQuality(); - m_WidgetManager.ValidateCurrentCameraPath(); - } - - public void IssueGlobalCommand(GlobalCommands rEnum, int iParam1 = -1, - int iParam2 = -1, string sParam = null) - { - switch (rEnum) - { - - // Keyboard command, for debugging and emergency use. - case GlobalCommands.Save: - { - if (!FileUtils.CheckDiskSpaceWithError(App.UserSketchPath())) - { - return; - } - // Disable active selection before saving. - // This looks fishy, here's what's going on: When an object is selected and it has moved, the - // the user is observing the selection canvas in the HMD, but we will be saving the main canvas. - // Because they haven't deselected yet, the selection canvas and the main canvas are out of sync - // so the strokes that will be saved will not match what the user sees. - // - // Here we deselect to force the main canvas to sync with the selection canvas, which is more - // correct from the user's perspective. Push the deselect operation onto the stack so the user - // can undo it after save, if desired. - SelectionManager.m_Instance.ClearActiveSelection(); - GenerateReplacementSaveIcon(); - if (iParam1 == -1) - { - if (iParam2 == 1) - { - // Do a save in Tiltasaurus mode, which creates a new filename prefixed with - // "Tiltasaurus_" and the current prompt. Also, don't eat gaze input so that the - // Tiltasaurus prompt stays open. - StartCoroutine(SaveLoadScript.m_Instance.SaveOverwrite(tiltasaurusMode: true)); - } - else - { - StartCoroutine(SaveLoadScript.m_Instance.SaveOverwrite()); - EatGazeObjectInput(); - } - } - else - { - StartCoroutine(SaveLoadScript.m_Instance.SaveMonoscopic(iParam1)); - } - break; - } - case GlobalCommands.SaveNew: - { - if (!FileUtils.CheckDiskSpaceWithError(App.UserSketchPath())) - { - return; - } - if (iParam1 == 1) - { - GenerateBoundingBoxSaveIcon(); - } - StartCoroutine(SaveLoadScript.m_Instance.SaveNewName()); - EatGazeObjectInput(); - break; - } - case GlobalCommands.SaveAndUpload: - { - if (!FileUtils.CheckDiskSpaceWithError(App.UserSketchPath())) - { - Debug.LogError("SaveAndUpload: Disk space error"); - return; - } - SelectionManager.m_Instance.ClearActiveSelection(); - m_PanelManager.GetPanel(m_CurrentGazeObject).CreatePopUp( - GlobalCommands.UploadToGenericCloud, (int)Cloud.None, -1); - EatGazeObjectInput(); - break; - } - case GlobalCommands.ExportAll: - { - StartCoroutine(LoadAndExportAll()); - break; - } - // Glen Keane request: a way to draw guidelines that can be toggled on and off - // at runtime. - case GlobalCommands.DraftingVisibility: - { - if (!Enum.IsDefined(typeof(DraftingVisibilityOption), iParam1)) - { - Debug.LogError("Unknown draft visibility value: " + iParam1); - return; - } - DraftingVisibilityOption option = (DraftingVisibilityOption)iParam1; - if (option != m_DraftingVisibility) - { - m_DraftingVisibility = option; - UpdateDraftingVisibility(); - } - break; - } - case GlobalCommands.MergeBrushStrokes: - { - // TODO Refactor with Load below - var index = iParam1; - var sketchSetType = (SketchSetType)iParam2; - SketchSet sketchSet = SketchCatalog.m_Instance.GetSet(sketchSetType); - SceneFileInfo rInfo = sketchSet.GetSketchSceneFileInfo(index); - if (rInfo != null) - { - MergeBrushStrokes(rInfo); - if (m_ControlsType != ControlsType.ViewingOnly) - { - EatGazeObjectInput(); - } - } - break; - } - case GlobalCommands.Load: - { - var index = iParam1; - var sketchSetType = (SketchSetType)iParam2; - SketchSet sketchSet = SketchCatalog.m_Instance.GetSet(sketchSetType); - SceneFileInfo rInfo = sketchSet.GetSketchSceneFileInfo(index); - if (rInfo != null) - { - LoadSketch(rInfo); - if (m_ControlsType != ControlsType.ViewingOnly) - { - EatGazeObjectInput(); - } - } - break; - } - case GlobalCommands.LoadNamedFile: - LoadNamed(sParam, iParam1 == (int)LoadSpeed.Quick, iParam2 != -1); - break; - case GlobalCommands.NewSketch: - NewSketch(fade: true); - Vector3 vTrashSoundPos = m_CurrentGazeRay.origin; - if (App.VrSdk.GetControllerDof() == VrSdk.DoF.Six) - { - vTrashSoundPos = InputManager.m_Instance.GetControllerPosition( - InputManager.ControllerName.Wand); - } - AudioManager.m_Instance.PlayTrashSound(vTrashSoundPos); - PromoManager.m_Instance.RequestAdvancedPanelsPromo(); - break; - case GlobalCommands.SymmetryPlane: - if (PointerManager.m_Instance.CurrentSymmetryMode != SymmetryMode.SinglePlane) - { - PointerManager.m_Instance.SetSymmetryMode(SymmetryMode.SinglePlane); - ControllerConsoleScript.m_Instance.AddNewLine("Mirror Enabled"); - } - else - { - PointerManager.m_Instance.SetSymmetryMode(SymmetryMode.None); - ControllerConsoleScript.m_Instance.AddNewLine("Mirror Off"); - } - break; - case GlobalCommands.MultiMirror: - if (PointerManager.m_Instance.CurrentSymmetryMode != SymmetryMode.MultiMirror) - { - PointerManager.m_Instance.SetSymmetryMode(SymmetryMode.MultiMirror); - ControllerConsoleScript.m_Instance.AddNewLine("Symmetry Enabled"); - } - else - { - PointerManager.m_Instance.SetSymmetryMode(SymmetryMode.None); - ControllerConsoleScript.m_Instance.AddNewLine("Symmetry Off"); - } - InputManager.m_Instance.TriggerHaptics(InputManager.ControllerName.Brush, 0.1f); - break; - case GlobalCommands.SymmetryTwoHanded: - if (PointerManager.m_Instance.CurrentSymmetryMode != SymmetryMode.TwoHanded) - { - PointerManager.m_Instance.SetSymmetryMode(SymmetryMode.TwoHanded); - ControllerConsoleScript.m_Instance.AddNewLine("Symmetry Enabled"); - } - else - { - PointerManager.m_Instance.SetSymmetryMode(SymmetryMode.None); - ControllerConsoleScript.m_Instance.AddNewLine("Symmetry Off"); - } - InputManager.m_Instance.TriggerHaptics(InputManager.ControllerName.Brush, 0.1f); - break; - case GlobalCommands.StraightEdge: - PointerManager.m_Instance.StraightEdgeModeEnabled = !PointerManager.m_Instance.StraightEdgeModeEnabled; - if (PointerManager.m_Instance.StraightEdgeModeEnabled) - { - ControllerConsoleScript.m_Instance.AddNewLine("Straight Edge On"); - } - else - { - ControllerConsoleScript.m_Instance.AddNewLine("Straight Edge Off"); - } - break; - case GlobalCommands.AutoOrient: - m_AutoOrientAfterRotation = !m_AutoOrientAfterRotation; - if (m_AutoOrientAfterRotation) - { - ControllerConsoleScript.m_Instance.AddNewLine("Auto-Orient On"); - } - else - { - ControllerConsoleScript.m_Instance.AddNewLine("Auto-Orient Off"); - } - break; - case GlobalCommands.Undo: - SketchMemoryScript.m_Instance.StepBack(); - break; - case GlobalCommands.Redo: - SketchMemoryScript.m_Instance.StepForward(); - break; - case GlobalCommands.AudioVisualization: // Intentionally blank. - break; - case GlobalCommands.ResetAllPanels: - m_PanelManager.ResetWandPanelsConfiguration(); - EatGazeObjectInput(); - break; - case GlobalCommands.SketchOrigin: - m_SketchSurfacePanel.EnableSpecificTool(BaseTool.ToolType.SketchOrigin); - EatGazeObjectInput(); - break; - case GlobalCommands.ViewOnly: - m_ViewOnly = !m_ViewOnly; - RequestPanelsVisibility(!m_ViewOnly); - PointerManager.m_Instance.RequestPointerRendering(!m_ViewOnly); - // TODO - decide if this is a permanent change - // With this line, you can't set a tool such as fly or teleport - // and switch to View Only mode as the mode change disables all tools - //m_SketchSurface.SetActive(!m_ViewOnly); - m_Decor.SetActive(!m_ViewOnly); - break; - case GlobalCommands.SaveGallery: - m_SketchSurfacePanel.EnableSpecificTool(BaseTool.ToolType.SaveIconTool); - break; - case GlobalCommands.DropCam: - // Want to enable this if in monoscopic or VR modes. - // TODO: seems odd to tie this switch to the controller type, should be based on some - // other build-time configuration setting. - if (App.VrSdk.GetControllerDof() != VrSdk.DoF.None) - { - m_DropCam.Show(!m_DropCam.gameObject.activeSelf); - } - break; - case GlobalCommands.AnalyticsEnabled_Deprecated: - break; - case GlobalCommands.ToggleAutosimplification: - QualityControls.AutosimplifyEnabled = !QualityControls.AutosimplifyEnabled; - break; - case GlobalCommands.Credits: - LoadSketch(new DiskSceneFileInfo(m_CreditsSketchFilename, embedded: true, readOnly: true)); - EatGazeObjectInput(); - break; - case GlobalCommands.AshleysSketch: - LoadSketch(new DiskSceneFileInfo(m_AshleysSketchFilename, embedded: true, readOnly: true)); - EatGazeObjectInput(); - break; - case GlobalCommands.FAQ: - OpenURLAndInformUser(m_HelpCenterURL); - break; - case GlobalCommands.ReleaseNotes: - OpenURLAndInformUser(m_ReleaseNotesURL); - break; - case GlobalCommands.ExportRaw: - if (!FileUtils.CheckDiskSpaceWithError(App.UserExportPath())) - { - return; - } - EatGazeObjectInput(); - StartCoroutine(ExportCoroutine()); - break; - case GlobalCommands.IRC: - if (m_IRCChatWidget == null) - { - GameObject widgetobject = (GameObject)Instantiate(m_IRCChatPrefab); - widgetobject.transform.parent = App.Instance.m_RoomTransform; - m_IRCChatWidget = widgetobject.GetComponent(); - m_IRCChatWidget.Show(true); - } - else - { - m_IRCChatWidget.Show(false); - m_IRCChatWidget = null; - } - break; - case GlobalCommands.YouTubeChat: - if (m_YouTubeChatWidget == null) - { - GameObject widgetobject = (GameObject)Instantiate(m_YouTubeChatPrefab); - widgetobject.transform.parent = App.Instance.m_RoomTransform; - m_YouTubeChatWidget = widgetobject.GetComponent(); - m_YouTubeChatWidget.Show(true); - } - else - { - m_YouTubeChatWidget.Show(false); - m_YouTubeChatWidget = null; - } - break; - case GlobalCommands.CameraOptions: - // If we're switching in to Camera mode, make sure Multicam is selected. - if (!m_PanelManager.CameraActive()) - { - SketchSurfacePanel.m_Instance.EnableSpecificTool(BaseTool.ToolType.MultiCamTool); - } - m_PanelManager.ToggleCameraPanels(); - PointerManager.m_Instance.EatLineEnabledInput(); - SketchSurfacePanel.m_Instance.EatToolsInput(); - break; - case GlobalCommands.ShowSketchFolder: - { - var index = iParam1; - var sketchSetType = (SketchSetType)iParam2; - SketchSet sketchSet = SketchCatalog.m_Instance.GetSet(sketchSetType); - SceneFileInfo rInfo = sketchSet.GetSketchSceneFileInfo(index); - EatGazeObjectInput(); - //launch external window and tell the user we did so - //this call is windows only - if ((Application.platform == RuntimePlatform.WindowsPlayer) || - (Application.platform == RuntimePlatform.WindowsEditor)) - { - OutputWindowScript.m_Instance.CreateInfoCardAtController( - InputManager.ControllerName.Brush, - kRemoveHeadsetFyi, fPopScalar: 0.5f); - System.Diagnostics.Process.Start("explorer.exe", - "/select," + rInfo.FullPath); - } - break; - } - case GlobalCommands.About: - OpenURLAndInformUser(m_ThirdPartyNoticesURL); - break; - case GlobalCommands.StencilsDisabled: - SketchMemoryScript.m_Instance.PerformAndRecordCommand(new StencilsVisibleCommand()); - break; - case GlobalCommands.StraightEdgeMeterDisplay: - PointerManager.m_Instance.StraightEdgeGuide.FlipMeter(); - break; - case GlobalCommands.Sketchbook: - m_PanelManager.ToggleSketchbookPanels(); - PointerManager.m_Instance.EatLineEnabledInput(); - SketchSurfacePanel.m_Instance.EatToolsInput(); - break; - case GlobalCommands.StraightEdgeShape: - // Previously experimental mode only. - // Untested and currently untriggerable. - PointerManager.m_Instance.StraightEdgeGuide.SetTempShape( - (StraightEdgeGuideScript.Shape)iParam1); - break; - case GlobalCommands.DeleteSketch: - { - var sketchSetType = (SketchSetType)iParam2; - SketchSet sketchSet = SketchCatalog.m_Instance.GetSet(sketchSetType); - sketchSet.DeleteSketch(iParam1); - DismissPopupOnCurrentGazeObject(false); - break; - } - case GlobalCommands.RenameSketch: - { - var sketchSetType = (SketchSetType)iParam2; - SketchSet sketchSet = SketchCatalog.m_Instance.GetSet(sketchSetType); - if (sketchSetType == SketchSetType.User) - { - sketchSet.RenameSketch(iParam1, KeyboardPopUpWindow.m_LastInput); - } - DismissPopupOnCurrentGazeObject(false); - break; - } - case GlobalCommands.RenameLayer: - { - var layer = App.Scene.GetCanvasByLayerIndex(iParam1); - App.Scene.RenameLayer(layer, KeyboardPopUpWindow.m_LastInput); - DismissPopupOnCurrentGazeObject(false); - break; - } - case GlobalCommands.EditMultiplayerRoomName: - { - var panel = (MultiplayerPanel)m_PanelManager.GetActivePanelByType(BasePanel.PanelType.Multiplayer); - panel.RoomName = KeyboardPopUpWindow.m_LastInput; - DismissPopupOnCurrentGazeObject(false); - break; +// Copyright 2020 The Tilt 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 OpenBrush.Multiplayer; +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using TiltBrush.Layers; +using UnityEngine; +using UnityEngine.InputSystem; +using SymmetryMode = TiltBrush.PointerManager.SymmetryMode; + +namespace TiltBrush +{ + + public class SketchControlsScript : MonoBehaviour + { + public const string kRemoveHeadsetFyi = "Remove headset to view."; + const string kTiltBrushGalleryUrl = "https://poly.google.com/tiltbrush"; + const string kBlocksGalleryUrl = "https://poly.google.com/blocks"; + const string kPolyMainPageUri = "https://poly.google.com"; + + static public SketchControlsScript m_Instance; + static bool sm_enableGrabHaptics = true; + + // ------------------------------------------------------------ + // Constants and types + // ------------------------------------------------------------ + + public enum GlobalCommands + { + Null, + Save, + SaveNew, + Load, + NewSketch, + StraightEdge, + AutoOrient, + Undo, + Redo, + Tiltasaurus, + LightingHdr, + AudioVisualization, + ResetAllPanels, + SketchOrigin, + SymmetryPlane, + MultiMirror, + ViewOnly, + SaveGallery, + LightingLdr, + ShowSketchFolder, + About, + LoadNamedFile, // iParam1 : (optional) - send through a LoadSpeed as int + DropCam, + CuratedGallery, + Unused_UploadToCloud, + AnalyticsEnabled_Deprecated, + Credits, + LogOutOfGenericCloud, + DraftingVisibility, + DeleteSketch, + ShowWindowGUI, + MorePanels, + Cameras, + FAQ, + ExportRaw, + IRC, + YouTubeChat, + CameraOptions, + StencilsDisabled, + AdvancedTools, + FloatingPanelsMode, + StraightEdgeMeterDisplay, + Sketchbook, + ExportAll, + Lights, + SaveAndUpload, + StraightEdgeShape, + SaveOptions, + SketchbookMenu, + Disco, + ViewOnlineGallery, + CancelUpload, + AdvancedPanelsToggle, + Music, + Duplicate, + ToggleGroupStrokesAndWidgets, + SaveModel, + ViewPolyPage, + ViewPolyGallery, + ExportListed, + RenderCameraPath, + ToggleProfiling, + DoAutoProfile, + DoAutoProfileAndQuit, + ToggleSettings, + SummonMirror, + InvertSelection, + SelectAll, + FlipSelection, + ToggleBrushLab, + ReleaseNotes, + ToggleCameraPostEffects, + ToggleWatermark, + AccountInfo, + // LoadConfirmUnsaved -> LoadWaitOnDownload -> LoadConfirmComplex -> LoadComplexHigh -> Load + LoadConfirmUnsaved, + LoadConfirmComplex, + MemoryWarning, + MemoryExceeded, + ViewLastUpload, + LoadConfirmComplexHigh, + ShowTos, + ShowPrivacy, + ShowQuestSideLoading, + AshleysSketch, + UnloadReferenceImageCatalog, + SaveOnLocalChanges, + ToggleCameraPathVisuals, + ToggleCameraPathPreview, + DeleteCameraPath, + RecordCameraPath, + SelectCameraPath, + ToggleAutosimplification, + ShowGoogleDrive, + GoogleDriveSync_Folder, // iParam1: folder id as DriveSync.SyncedFolderType + GoogleDriveSync, + LoginToGenericCloud, // iParam1: Cloud enum + UploadToGenericCloud, // iParam1: Cloud enum + LoadWaitOnDownload, + SignOutConfirm, + ReadOnlyNotice, + ShowContribution, + + // Open Brush Reserved Enums 1000-1999 + LanguagePopup = 1000, + MultiplayerTogglePanel = 1001, + MultiplayerPanelOptions = 1002, // iParam1: Popup options + MultiplayerJoinRoom = 1004, + EditMultiplayerRoomName = 1005, + MultiplayerLeaveRoom = 1006, + MultiplayerConnect = 1007, + MultiplayerDisconnect = 1008, + EditMultiplayerNickName = 1009, + + RenameSketch = 5200, + OpenLayerOptionsPopup = 5201, + RenameLayer = 5202, + OpenDirectorChooserPopup = 5800, + OpenScriptsCommandsList = 6000, + OpenScriptsList = 6001, + OpenExampleScriptsList = 6002, + SymmetryTwoHanded = 6003, + OpenColorOptionsPopup = 7000, + ChangeSnapAngle = 8000, + MergeBrushStrokes = 10000, + RepaintOptions = 11500, + OpenNumericInputPopup = 12000 + } + + public enum ControlsType + { + KeyboardMouse, + SixDofControllers, + ViewingOnly + } + + public enum DraftingVisibilityOption + { + Visible, + Transparent, + Hidden + } + + public enum InputState + { + Standard, + Pan, + Rotation, + HeadLock, + ControllerLock, + PushPull, + BrushSize, + Save, + Load, + Num + } + + public enum LoadSpeed + { + Normal = -1, + Quick = 1, + } + + const float kControlPointHistoryMaxTime = 0.1f; + + class GazeResult + { + public bool m_HitWithGaze; + public bool m_HitWithController; + // ReSharper disable once NotAccessedField.Local + public bool m_WithinView; + public float m_ControllerDistance; + public Vector3 m_GazePosition; + public Vector3 m_ControllerPosition; + public InputManager.ControllerName m_ControllerName; + } + + class GrabWidgetControllerInfo + { + public InputManager.ControllerName m_Name; + /// Transform of controller at the time the grab started + public TrTransform m_BaseControllerXf; + /// "local" transform of widget (relative to controller), at the time the grab started. + /// The widget isn't parented to the controller, but if it were, this would be its transform. + public TrTransform m_BaseWidgetXf_LS; + } + + struct GrabWidgetHoldPoint + { + // ReSharper disable once NotAccessedField.Local + public InputManager.ControllerName m_Name; + public float m_BirthTime; + public Vector3 m_Pos; // where controller is holding the widget + public Quaternion m_Rot; + } + + class InputStateConfig + { + public bool m_AllowDrawing; + public bool m_AllowMovement; + public bool m_ShowGizmo; + } + + enum FadeState + { + None, + FadeOn, + FadeOff + } + + enum GrabWidgetState + { + None, + OneHand, + TwoHands + } + + enum GrabWorldState + { + Normal, + ResettingTransform, + ResetDone + } + + private enum WorldTransformResetState + { + Default, + Requested, + FadingToBlack, + FadingToScene, + } + + enum RotationType + { + All, + RollOnly + } + + enum GrabIntersectionState + { + RequestIntersections, + ReadBrush, + ReadWand + } + + // ------------------------------------------------------------ + // Inspector data (read-only even if public) + // ------------------------------------------------------------ + + public GameObject m_SketchSurface; + public SketchMemoryScript.PlaybackMode m_DefaultSketchPlaybackMode; + public float m_GazeMaxAngleFromPointing = 85.0f; + public float m_GazeMaxAngleFacingToForward = 80.0f; + + [SerializeField] bool m_AtlasIconTextures; + + [SerializeField] SaveIconTool m_SaveIconTool; + [SerializeField] DropCamWidget m_DropCam; + [SerializeField] string m_CreditsSketchFilename; + [SerializeField] string m_AshleysSketchFilename; + [SerializeField] float m_DefaultSketchLoadSpeed; + [SerializeField] GameObject m_TransformGizmoPrefab; + + [SerializeField] GameObject m_RotationIconPrefab; + [SerializeField] float m_GazeMaxAngleFromFacing = 70.0f; + [SerializeField] float m_GazeMaxDistance = 10.0f; + [SerializeField] float m_GazeControllerPointingDistance; + [SerializeField] float m_GazePanelDectivationDelay = 0.25f; + + [SerializeField] GameObject m_UIReticle; + [SerializeField] GameObject m_UIReticleMobile; + [SerializeField] GameObject m_UIReticleSixDofController; + + [SerializeField] float m_DoubleTapWindow; + [SerializeField] float m_PushPullScale; + [SerializeField] RotationCursorScript m_RotationCursor; + [SerializeField] float m_RotationMaxAngle; + + [SerializeField] float m_RotationScalar; + [SerializeField] float m_RotationRollScalar; + [SerializeField] float m_PanScalar; + + [SerializeField] float m_AdjustToolSizeScalar; + + [SerializeField] GameObject m_IRCChatPrefab; + [SerializeField] GameObject m_YouTubeChatPrefab; + [SerializeField] GameObject m_Decor; + [SerializeField] BaseTool.ToolType m_InitialTool = BaseTool.ToolType.SketchSurface; + [SerializeField] string m_ReleaseNotesURL; + [SerializeField] string m_HelpCenterURL; + [SerializeField] string m_ThirdPartyNoticesURL; + [SerializeField] string m_TosURL; + [SerializeField] string m_PrivacyURL; + [SerializeField] string m_QuestSideLoadingHowToURL; + + [Multiline] + [SerializeField] string m_ContributionPromoText; + [SerializeField] string m_ContributionURL; + + [SerializeField] float m_WorldTransformMinScale = .1f; + [SerializeField] float m_WorldTransformMaxScale = 10.0f; + + [Header("Undo/Redo Hold")] + [SerializeField] float m_UndoRedoHold_DurationBeforeStart; + [SerializeField] float m_UndoRedoHold_RepeatInterval; + + [Header("Pin Cushion")] + [SerializeField] GameObject m_PinCushionPrefab; + + [Header("Grabbing and tossing")] + [SerializeField] float m_GrabWorldFadeSpeed = 8.0f; + [SerializeField] Color m_GrabWorldGridColor = new Color(0.0f, 1.0f, 1.0f, 0.2f); + [SerializeField] ControllerGrabVisuals m_ControllerGrabVisuals; + [SerializeField] float m_WidgetGpuIntersectionRadius; + + [Header("Saving")] + [SerializeField] int m_NumStrokesForSaveIcon = 50; + + [NonSerialized] public Color m_GrabHighlightActiveColor; + [NonSerialized] public bool m_DisableWorldGrabbing = false; + + /// Throwing an object faster than this means it's a "toss". Units are m/s. + public float m_TossThresholdMeters = 3f; + /// Angular motion contributes more towards the toss velocity the larger the object is; + /// or rather, the larger the distance between the grab point and the object's center. + /// To prevent large objects from being too-easily-tossed, bound that distance. + public float m_TossMaxPivotDistMeters = 0.33f; + + // ------------------------------------------------------------ + // Internal data + // ------------------------------------------------------------ + + private SketchSurfacePanel m_SketchSurfacePanel; + private SketchMemoryScript.PlaybackMode m_SketchPlaybackMode; + private GameObject m_TransformGizmo; + private TransformGizmoScript m_TransformGizmoScript; + private GameObject m_RotationIcon; + private float m_MouseDeltaX; + private float m_MouseDeltaY; + private float m_MouseDeltaXScaled; + private float m_MouseDeltaYScaled; + private float m_PositionOffsetResetTapTime; + private bool m_EatToolScaleInput; + + private PanelManager m_PanelManager; + private WidgetManager m_WidgetManager; + private PinCushion m_PinCushion; + private bool m_EatPinCushionInput; + + // This is the gaze that was used to compute m_CurrentGazeHitPoint. + // It is not a general substitute for ViewpointScript.Gaze. + private Ray m_CurrentGazeRay; + private Quaternion m_CurrentHeadOrientation; + private GazeResult[] m_GazeResults; + private int m_CurrentGazeObject; + private bool m_EatInputGazeObject; + private Vector3 m_CurrentGazeHitPoint; + private Ray m_GazeControllerRay; + private Ray m_GazeControllerRayActivePanel; + private bool m_ForcePanelActivation = false; + private float m_GazePanelDectivationCountdown; + private bool m_PanelsVisibilityRequested; + + // Previously Experimental-Model only + private bool m_HeadOffset; + + float m_UndoHold_Timer; + float m_RedoHold_Timer; + + // Grab world member variables. + struct GrabState + { + public InputManager.ControllerName name; + public TrTransform grabTransform; + public bool grabbingWorld; + public bool grabbingGroup; + public bool startedGrabInsideWidget; + public bool eatInput; + private GrabWidget lastWidgetIntersect; + + public void SetHadBestGrabAndTriggerHaptics(GrabWidgetData data) + { + bool dormant = WidgetManager.m_Instance.WidgetsDormant; + if (data != null && !data.m_WidgetScript.AllowDormancy) + { + dormant = false; + } + GrabWidget newInsideWidget = (data != null && !dormant) ? data.m_WidgetScript : null; + if (sm_enableGrabHaptics && newInsideWidget != lastWidgetIntersect) + { + // state changed + if (newInsideWidget != null) + { + // transitioning in + InputManager.m_Instance.TriggerHaptics(name, data.m_WidgetScript.HapticDuration); } - case GlobalCommands.EditMultiplayerNickName: - { - var panel = (MultiplayerPanel)m_PanelManager.GetActivePanelByType(BasePanel.PanelType.Multiplayer); - panel.NickName = KeyboardPopUpWindow.m_LastInput; - DismissPopupOnCurrentGazeObject(false); - break; - } - case GlobalCommands.ShowWindowGUI: - break; - case GlobalCommands.Disco: - LightsControlScript.m_Instance.DiscoMode = !LightsControlScript.m_Instance.DiscoMode; - break; - case GlobalCommands.AccountInfo: break; // Intentionally blank. - case GlobalCommands.LoginToGenericCloud: - { - var ident = App.GetIdentity((Cloud)iParam1); - if (!ident.LoggedIn) { ident.LoginAsync(); } - // iParam2 is being used as a UX flag. If not set to the default, it will cause the UI - // to lose focus. - if (iParam2 != -1) { EatGazeObjectInput(); } - break; - } - case GlobalCommands.LogOutOfGenericCloud: - { - var ident = App.GetIdentity((Cloud)iParam1); - if (ident.LoggedIn) { ident.Logout(); } - break; - } - case GlobalCommands.UploadToGenericCloud: - { - Cloud cloud = (Cloud)iParam1; - var ident = App.GetIdentity(cloud); - if (!ident.LoggedIn) - { - ident.LoginAsync(); - break; - } - SelectionManager.m_Instance.ClearActiveSelection(); - VrAssetService.m_Instance.UploadCurrentSketchAsync(cloud, isDemoUpload: false).AsAsyncVoid(); - EatGazeObjectInput(); - break; - } - case GlobalCommands.ViewOnlineGallery: - OpenURLAndInformUser(kTiltBrushGalleryUrl); - break; - case GlobalCommands.CancelUpload: - VrAssetService.m_Instance.CancelUpload(); - break; - case GlobalCommands.ViewLastUpload: - if (VrAssetService.m_Instance.LastUploadCompleteUrl != null) - { - var url = VrAssetService.m_Instance.LastUploadCompleteUrl; - App.OpenURL(url); - - // The upload flow is different on mobile and requires the user to manually accept - // that they'll go to the browser for publishing. In that case, we want to reset - // state when the leave to publish. This is automatically part of the - // UploadPopUpWindow state flow on PC. - if (App.Config.IsMobileHardware) - { - DismissPopupOnCurrentGazeObject(true); - } - } - break; - case GlobalCommands.ShowGoogleDrive: - string baseDriveUrl = "https://drive.google.com"; - string driveURL = !App.GoogleIdentity.LoggedIn ? baseDriveUrl : - string.Format( - "http://accounts.google.com/AccountChooser?Email={0}&continue={1}", - App.GoogleIdentity.Profile.email, baseDriveUrl); - OpenURLAndInformUser(driveURL); - break; - case GlobalCommands.GoogleDriveSync: - App.DriveSync.SyncEnabled = !App.DriveSync.SyncEnabled; - break; - case GlobalCommands.GoogleDriveSync_Folder: - App.DriveSync.ToggleSyncOnFolderOfType((DriveSync.SyncedFolderType)iParam1); - break; - case GlobalCommands.Duplicate: - { - int selectedVerts = SelectionManager.m_Instance.NumVertsInSelection; - - // TODO - this code has never taken imported models etc into account - if (PointerManager.m_Instance.CurrentSymmetryMode == SymmetryMode.MultiMirror) - { - selectedVerts *= PointerManager.m_Instance.CustomMirrorMatrices.Count; - } - - if (!SketchMemoryScript.m_Instance.MemoryWarningAccepted && - SketchMemoryScript.m_Instance.WillVertCountPutUsOverTheMemoryLimit(selectedVerts)) - { - AudioManager.m_Instance.PlayUploadCanceledSound(InputManager.Wand.Transform.position); - if (!m_PanelManager.MemoryWarningActive()) - { - m_PanelManager.ToggleMemoryWarningMode(); - } - } - else - { - ClipboardManager.Instance.DuplicateSelection( - stampMode: IsUserInteractingWithSelectionWidget()); - } - EatToolScaleInput(); - break; - } - case GlobalCommands.AdvancedPanelsToggle: - m_PanelManager.ToggleAdvancedPanels(); - // If we're now in basic mode, ensure we don't have advanced abilities. - if (!m_PanelManager.AdvancedModeActive()) - { - m_WidgetManager.StencilsDisabled = true; - m_WidgetManager.CameraPathsVisible = false; - App.Switchboard.TriggerStencilModeChanged(); - m_SketchSurfacePanel.EnsureUserHasBasicToolEnabled(); - if (PointerManager.m_Instance.CurrentSymmetryMode != SymmetryMode.None) - { - PointerManager.m_Instance.SetSymmetryMode(SymmetryMode.None, false); - } - } - PromoManager.m_Instance.RecordCompletion(PromoType.AdvancedPanels); - EatGazeObjectInput(); - break; - case GlobalCommands.Music: break; // Intentionally blank. - case GlobalCommands.ToggleGroupStrokesAndWidgets: - SelectionManager.m_Instance.ToggleGroupSelectedStrokesAndWidgets(); - EatToolScaleInput(); - break; - case GlobalCommands.SaveModel: - SaveModel(); - break; - case GlobalCommands.ViewPolyPage: - OpenURLAndInformUser(kPolyMainPageUri); - break; - case GlobalCommands.ViewPolyGallery: - OpenURLAndInformUser(kBlocksGalleryUrl); - break; - case GlobalCommands.ExportListed: - StartCoroutine(ExportListAndQuit()); - break; - case GlobalCommands.RenderCameraPath: - StartCoroutine(RenderPathAndQuit()); - break; - case GlobalCommands.ToggleProfiling: - ToggleProfiling(); - break; - case GlobalCommands.DoAutoProfile: - DoAutoProfile(); - break; - case GlobalCommands.DoAutoProfileAndQuit: - DoAutoProfileAndQuit(); - break; - case GlobalCommands.ToggleSettings: - m_PanelManager.ToggleSettingsPanels(); - PointerManager.m_Instance.EatLineEnabledInput(); - SketchSurfacePanel.m_Instance.EatToolsInput(); - break; - case GlobalCommands.SummonMirror: - PointerManager.m_Instance.BringSymmetryToUser(); - break; - case GlobalCommands.InvertSelection: - SelectionManager.m_Instance.InvertSelection(); - break; - case GlobalCommands.SelectAll: - SketchSurfacePanel.m_Instance.EnableSpecificTool(BaseTool.ToolType.SelectionTool); - SelectionManager.m_Instance.SelectAll(); - EatGazeObjectInput(); - break; - case GlobalCommands.FlipSelection: - SelectionManager.m_Instance.FlipSelection(); - break; - case GlobalCommands.ToggleBrushLab: - m_PanelManager.ToggleBrushLabPanels(); - PointerManager.m_Instance.EatLineEnabledInput(); - SketchSurfacePanel.m_Instance.EatToolsInput(); - break; - case GlobalCommands.ToggleCameraPostEffects: - CameraConfig.PostEffects = !CameraConfig.PostEffects; - break; - case GlobalCommands.ToggleWatermark: - if (PlayerPrefs.GetInt("Promo_Contribution", 0) == 0) - { - OutputWindowScript.m_Instance.CreateInfoCardAtController( - InputManager.ControllerName.Wand, - m_ContributionPromoText, fPopScalar: 1.0f); - PlayerPrefs.SetInt("Promo_Contribution", 1); - } - CameraConfig.Watermark = !CameraConfig.Watermark; - break; - case GlobalCommands.LoadConfirmComplexHigh: - IssueGlobalCommand(GlobalCommands.Load, iParam1, iParam2, null); - break; - case GlobalCommands.LoadConfirmComplex: - { - var index = iParam1; - var sketchSetType = (SketchSetType)iParam2; - bool loadSketch = true; - - // If the sketchbook is active, we may want to show a popup instead of load. - if (m_PanelManager.SketchbookActive()) - { - BasePanel sketchBook = m_PanelManager.GetSketchBookPanel(); - if (sketchBook != null) - { - // Get triangle count from cloud scene file info. - SketchSet sketchSet = SketchCatalog.m_Instance.GetSet(sketchSetType); - SceneFileInfo sfi = sketchSet.GetSketchSceneFileInfo(index); - int tris = sfi.TriangleCount ?? -1; - - // Show "this is bad" popup if we're over the triangle limit. - if (tris > QualityControls.m_Instance.AppQualityLevels.MaxPolySketchTriangles) - { - loadSketch = false; - sketchBook.CreatePopUp(GlobalCommands.LoadConfirmComplexHigh, iParam1, iParam2); - } - else if (tris > - QualityControls.m_Instance.AppQualityLevels.WarningPolySketchTriangles) - { - // Show, "this could be bad" popup if we're over the warning limit. - loadSketch = false; - sketchBook.CreatePopUp(GlobalCommands.Load, iParam1, iParam2); - } - } - } - - if (loadSketch) - { - IssueGlobalCommand(GlobalCommands.Load, iParam1, iParam2, null); - } - } - break; - case GlobalCommands.LoadConfirmUnsaved: - { - BasePanel sketchBook = m_PanelManager.GetSketchBookPanel(); - if ((sketchBook != null) && SketchMemoryScript.m_Instance.IsMemoryDirty()) - { - sketchBook.CreatePopUp(GlobalCommands.LoadWaitOnDownload, iParam1, iParam2, null); - } - else - { - IssueGlobalCommand(GlobalCommands.LoadWaitOnDownload, iParam1, iParam2, null); - } - } - break; - case GlobalCommands.LoadWaitOnDownload: - { - bool download = false; - if (iParam2 == (int)SketchSetType.Drive) - { - BasePanel sketchBook = m_PanelManager.GetSketchBookPanel(); - var googleSketchSet = SketchCatalog.m_Instance.GetSet(SketchSetType.Drive); - if (sketchBook != null - && googleSketchSet != null - && googleSketchSet.IsSketchIndexValid(iParam1) - && !googleSketchSet.GetSketchSceneFileInfo(iParam1).Available) - { - sketchBook.CreatePopUp(GlobalCommands.LoadConfirmComplex, iParam1, iParam2, null); - download = true; - } - } - if (!download) - { - IssueGlobalCommand(GlobalCommands.LoadConfirmComplex, iParam1, iParam2, null); - } - } - break; - case GlobalCommands.MemoryWarning: - if (iParam1 > 0) - { - SketchMemoryScript.m_Instance.MemoryWarningAccepted = true; - } - m_PanelManager.ToggleMemoryWarningMode(); - break; - case GlobalCommands.MemoryExceeded: - // If we're in the memory exceeded app state, exit. - if (App.CurrentState == App.AppState.MemoryExceeded) - { - App.Instance.SetDesiredState(App.AppState.Standard); - } - else - { - // If we're not in the full app state, just switch our panel mode. - m_PanelManager.ToggleMemoryWarningMode(); - } - break; - case GlobalCommands.ShowTos: - OpenURLAndInformUser(m_TosURL); - break; - case GlobalCommands.ShowPrivacy: - OpenURLAndInformUser(m_PrivacyURL); - break; - case GlobalCommands.ShowQuestSideLoading: - OpenURLAndInformUser(m_QuestSideLoadingHowToURL); - break; - case GlobalCommands.ShowContribution: - OpenURLAndInformUser(m_ContributionURL); - break; - case GlobalCommands.UnloadReferenceImageCatalog: - ReferenceImageCatalog.m_Instance.UnloadAllImages(); - break; - case GlobalCommands.ToggleCameraPathVisuals: - m_WidgetManager.CameraPathsVisible = !m_WidgetManager.CameraPathsVisible; - break; - case GlobalCommands.ToggleCameraPathPreview: - m_WidgetManager.FollowingPath = !m_WidgetManager.FollowingPath; - break; - case GlobalCommands.DeleteCameraPath: - { - var cameraPath = m_WidgetManager.GetCurrentCameraPath(); - GrabWidget cameraPathWidget = cameraPath == null ? null : cameraPath.m_WidgetScript; - m_WidgetManager.DeleteCameraPath(cameraPathWidget); - } - break; - case GlobalCommands.RecordCameraPath: - // Turn off MultiCam if we're going to record the camera path. - if (m_SketchSurfacePanel.GetCurrentToolType() == BaseTool.ToolType.MultiCamTool) - { - m_SketchSurfacePanel.EnableDefaultTool(); - } - CameraPathCaptureRig.RecordPath(); - EatGazeObjectInput(); - break; - case GlobalCommands.OpenScriptsCommandsList: - OpenURLAndInformUser($"http://localhost:{App.HttpServer.HttpPort}/help/commands"); - break; - case GlobalCommands.OpenScriptsList: - OpenURLAndInformUser($"http://localhost:{App.HttpServer.HttpPort}/scripts"); - break; - case GlobalCommands.OpenExampleScriptsList: - OpenURLAndInformUser($"http://localhost:{App.HttpServer.HttpPort}/examplescripts"); - break; - case GlobalCommands.MultiplayerTogglePanel: - m_PanelManager.ToggleMultiplayerPanels(); - PointerManager.m_Instance.EatLineEnabledInput(); - SketchSurfacePanel.m_Instance.EatToolsInput(); - break; - case GlobalCommands.RepaintOptions: break; // Intentionally blank. - case GlobalCommands.Null: break; // Intentionally blank. - case GlobalCommands.MultiplayerPanelOptions: break; // Intentionally blank. - case GlobalCommands.MultiplayerJoinRoom: break; // Intentionally blank. + else + { + // transitioning out + InputManager.m_Instance.TriggerHaptics(name, 0.03f); + } + } + lastWidgetIntersect = newInsideWidget; + } + + public void ClearInsideWidget() + { + lastWidgetIntersect = null; + } + } + private GrabState m_GrabBrush = new GrabState { name = InputManager.ControllerName.Brush }; + private GrabState m_GrabWand = new GrabState { name = InputManager.ControllerName.Wand }; + + private WorldTransformResetState m_WorldTransformResetState = WorldTransformResetState.Default; + private TrTransform m_WorldTransformResetXf = TrTransform.identity; // set when reset requested + private GrabWorldState m_GrabWorldState = GrabWorldState.Normal; + private float m_WorldTransformFadeAmount; + private bool m_AllowWorldTransformLastFrame = false; + private bool m_WorldBeingGrabbed; + private TrTransform m_xfDropCamReset_RS; + + struct GpuIntersectionResult + { + public GpuIntersector.FutureModelResult result; + public List resultList; + } + private Queue m_BrushResults; + private Queue m_WandResults; + private int m_WidgetGpuIntersectionLayer; + + private GrabWidget m_CurrentGrabWidget; + private GrabWidget m_MaybeDriftingGrabWidget; // use only to clear drift + + // References to widgets, cached in the UpdateGrab_None, to be used by helper functions + // for the remainder of the frame. + private GrabWidget m_PotentialGrabWidgetBrush; + private GrabWidget m_PotentialGrabWidgetWand; + + // Flags for the explaining if the m_PotentialGrabWidget_x widgets are able to be interacted with. + // Cached in the UpdateGrab_None, used for the remainder of the frame. + private bool m_PotentialGrabWidgetBrushValid; + private bool m_PotentialGrabWidgetWandValid; + + // References to widget metadata, cached in UpdateGrab_None, to be re-used on "off frames" + // when the GPU intersector is not refreshing the nearest widget to the respective controller. + private GrabWidgetData m_BackupBrushGrabData; + private GrabWidgetData m_BackupWandGrabData; + + private GrabWidgetState m_GrabWidgetState; + private GrabWidgetControllerInfo m_GrabWidgetOneHandInfo; + private TrTransform m_GrabWidgetTwoHandBrushPrev; + private TrTransform m_GrabWidgetTwoHandWandPrev; + private Queue m_GrabWidgetHoldHistory; + + private Quaternion m_RotationOrigin; + private Vector2 m_RotationCursorOffset; + + private bool m_RotationRollActive; + private float m_RotationResetTapTime; + + private RotationType m_CurrentRotationType; + private bool m_AutoOrientAfterRotation; + + private Vector3 m_SurfaceForward; + private Vector3 m_SurfaceRight; + private Vector3 m_SurfaceUp; + + private Vector3 m_SurfaceLockOffset; + private Vector3 m_SurfaceLockBaseSurfacePosition; + private Vector3 m_SurfaceLockBaseControllerPosition; + private Quaternion m_SurfaceLockBaseHeadRotation; + private Quaternion m_SurfaceLockBaseControllerRotation; + private Quaternion m_SurfaceLockBaseSurfaceRotation; + private InputManager.ControllerName m_SurfaceLockActingController; + private float m_SurfaceLockControllerBaseScalar; + private float m_SurfaceLockControllerScalar; + + private bool m_PositioningPanelWithHead; + private Quaternion m_PositioningPanelBaseHeadRotation; + private Vector3 m_PositioningPanelOffset; + private float m_PositioningTimer; + private float m_PositioningSpeed; + + private DraftingVisibilityOption m_DraftingVisibility = DraftingVisibilityOption.Visible; + + private Vector3 m_SketchOrigin; + + private ControlsType m_ControlsType; + private GrabWidget m_IRCChatWidget; + private GrabWidget m_YouTubeChatWidget; + private MultiCamCaptureRig m_MultiCamCaptureRig; + private CameraPathCaptureRig m_CameraPathCaptureRig; + + private bool m_ViewOnly = false; + + private InputState m_CurrentInputState; + private InputStateConfig[] m_InputStateConfigs; + + private GrabIntersectionState m_CurrentGrabIntersectionState; + + private float m_WorldTransformSpeedSmoothed; + + // ------------------------------------------------------------ + // Properties and events + // ------------------------------------------------------------ + + public MultiCamCaptureRig MultiCamCaptureRig + { + get { return m_MultiCamCaptureRig; } + } + + public CameraPathCaptureRig CameraPathCaptureRig + { + get { return m_CameraPathCaptureRig; } + } + + public ControllerGrabVisuals ControllerGrabVisuals + { + get { return m_ControllerGrabVisuals; } + } + + public SketchMemoryScript.PlaybackMode SketchPlaybackMode + { + get { return m_SketchPlaybackMode; } + set { m_SketchPlaybackMode = value; } + } + + public Transform m_Canvas + { + get { return App.Instance.m_CanvasTransform; } + } + + public ControlsType ActiveControlsType + { + get { return m_ControlsType; } + set { m_ControlsType = value; } + } + + public float WorldTransformMinScale + { + get + { + return App.UserConfig.Flags.UnlockScale ? m_WorldTransformMinScale * 0.01f : + m_WorldTransformMinScale; + } + } + + public float WorldTransformMaxScale + { + get + { + return App.UserConfig.Flags.UnlockScale ? m_WorldTransformMaxScale * 10.0f : + m_WorldTransformMaxScale; + } + } + + public void SetInitialTool(BaseTool.ToolType rType) + { + m_InitialTool = rType; + } + + public void SetInFreePaintMode(bool bFreePaint) + { + m_SketchSurfacePanel.SetInFreePaintMode(bFreePaint); + } + + public float GazeMaxDistance + { + get { return m_GazeMaxDistance; } + } + + public InputManager.ControllerName OneHandGrabController + { + get + { + return m_CurrentGrabWidget != null ? + m_GrabWidgetOneHandInfo.m_Name : + InputManager.ControllerName.None; + } + } + + public InputManager.ControllerName PotentialOneHandGrabController(GrabWidget widget) + { + if (m_PotentialGrabWidgetBrush == widget) + { + return InputManager.ControllerName.Brush; + } + else if (m_PotentialGrabWidgetWand == widget) + { + return InputManager.ControllerName.Wand; + } + return OneHandGrabController; + } + + public Vector3 GetSurfaceForward() { return m_SurfaceForward; } + public Vector3 GetSurfaceUp() { return m_SurfaceUp; } + public Vector3 GetSurfaceRight() { return m_SurfaceRight; } + public Vector3 GetSketchOrigin() { return m_SketchOrigin; } + public float GetDefaultSketchLoadSpeed() { return m_DefaultSketchLoadSpeed; } + public Quaternion GetCurrentHeadOrientation() { return m_CurrentHeadOrientation; } + public Vector3 GetUIReticlePos() { return m_UIReticle.transform.position; } + public Vector3 GetSweetSpotPos() { return m_PanelManager.m_SweetSpot.transform.position; } + public void SetSketchOrigin(Vector3 vOrigin) { m_SketchOrigin = vOrigin; } + + public void EatGazeObjectInput() + { + m_EatInputGazeObject = true; + m_GazePanelDectivationCountdown = 0.0f; + PointerManager.m_Instance.EatLineEnabledInput(); + SketchSurfacePanel.m_Instance.EatToolsInput(); + } + public void EatToolScaleInput() { m_EatToolScaleInput = true; } + public void EatGrabInput() + { + m_GrabWand.eatInput = true; + m_GrabBrush.eatInput = true; + } + + public bool ShouldRespondToPadInput(InputManager.ControllerName name) + { + if (name == InputManager.ControllerName.Brush && m_CurrentGazeObject != -1) + { + return m_PanelManager.GetPanel(m_CurrentGazeObject).BrushPadAnimatesOnHover(); + } + return !m_EatToolScaleInput && SketchSurfacePanel.m_Instance.CanAdjustToolSize(); + } + public void ForcePanelActivation(bool bForce) + { + m_ForcePanelActivation = bForce; + if (m_ForcePanelActivation) + { + m_GazePanelDectivationCountdown = m_GazePanelDectivationDelay; + } + } + public bool IsUserInteractingWithUI() + { + return (m_CurrentGazeObject != -1) || (m_GazePanelDectivationCountdown > 0.0f); + } + public bool IsUIBlockingUndoRedo() + { + if (m_CurrentGazeObject != -1) + { + return m_PanelManager.GetPanel(m_CurrentGazeObject).UndoRedoBlocked(); + } + return false; + } + public bool IsUserAbleToInteractWithAnyWidget() + { + return IsUserInteractingWithAnyWidget() || + (m_PotentialGrabWidgetBrush != null && m_PotentialGrabWidgetBrushValid) || + (m_PotentialGrabWidgetWand != null && m_PotentialGrabWidgetWandValid); + } + public bool IsUserInteractingWithAnyWidget() { return m_CurrentGrabWidget != null; } + public bool IsUserGrabbingAnyPanel() + { + return (m_CurrentGrabWidget != null && m_CurrentGrabWidget is PanelWidget); + } + public bool IsUsersBrushIntersectingWithSelectionWidget() + { + return (m_PotentialGrabWidgetBrush != null && + m_PotentialGrabWidgetBrushValid && + m_PotentialGrabWidgetBrush is SelectionWidget); + } + public bool IsUserIntersectingWithSelectionWidget() + { + return IsUsersBrushIntersectingWithSelectionWidget() || + (m_PotentialGrabWidgetWand != null && + m_PotentialGrabWidgetWandValid && + m_PotentialGrabWidgetWand is SelectionWidget); + } + public bool IsUserInteractingWithSelectionWidget() + { + return (m_CurrentGrabWidget != null && m_CurrentGrabWidget is SelectionWidget); + } + + public bool IsUserGrabbingWorld() { return m_GrabWand.grabbingWorld || m_GrabBrush.grabbingWorld; } + public bool IsUserGrabbingWorldWithBrushHand() { return m_GrabBrush.grabbingWorld; } + public bool IsUserTransformingWorld() { return m_GrabWand.grabbingWorld && m_GrabBrush.grabbingWorld; } + public float GetGazePanelActivationRatio() { return m_GazePanelDectivationCountdown / m_GazePanelDectivationDelay; } + public bool IsCurrentGrabWidgetPinned() { return IsUserInteractingWithAnyWidget() && m_CurrentGrabWidget.Pinned; } + public bool CanCurrentGrabWidgetBePinned() { return IsUserInteractingWithAnyWidget() && m_CurrentGrabWidget.AllowPinning; } + public bool DidUserGrabWithBothInside() { return m_GrabBrush.startedGrabInsideWidget && m_GrabWand.startedGrabInsideWidget; } + public bool IsUserGrabbingWidget(GrabWidget widget) { return widget == m_CurrentGrabWidget; } + public bool IsUserTwoHandGrabbingWidget() { return m_GrabWidgetState == GrabWidgetState.TwoHands; } + public bool IsPinCushionShowing() { return m_PinCushion.IsShowing(); } + public bool IsUserLookingAtPanel(BasePanel panel) + { + return m_CurrentGazeObject > -1 && + m_PanelManager.GetAllPanels()[m_CurrentGazeObject].m_Panel == panel; + } + + public SaveIconTool GetSaveIconTool() + { + return m_SaveIconTool; + } + + public DropCamWidget GetDropCampWidget() + { + return m_DropCam; + } + + public bool IsGrabWorldStateStable() + { + return m_GrabWorldState == GrabWorldState.Normal; + } + + // Internal: modify Coords.ScenePose or Coords.CanvasPose depending on the + // state of m_InTransformCanvasMode + TrTransform GrabbedPose + { + get + { + return App.Scene.Pose; + } + set + { + App.Scene.Pose = value; + } + } + + public Transform GazeObjectTransform() + { + if (m_CurrentGazeObject != -1) + { + return m_PanelManager.GetPanel(m_CurrentGazeObject).transform; + } + return null; + } + + public void ForceShowUIReticle(bool bVisible) + { + m_UIReticle.SetActive(bVisible); + } + + public void SetUIReticleTransform(Vector3 vPos, Vector3 vForward) + { + m_UIReticle.transform.position = vPos; + m_UIReticle.transform.forward = vForward; + } + + public bool AtlasIconTextures + { + get { return m_AtlasIconTextures; } + } + + public IconTextureAtlas IconTextureAtlas + { + get { return GetComponent(); } + } + public GrabWidget CurrentGrabWidget => m_CurrentGrabWidget; + + void DismissPopupOnCurrentGazeObject(bool force) + { + if (m_CurrentGazeObject != -1) + { + m_PanelManager.GetPanel(m_CurrentGazeObject).CloseActivePopUp(force); + } + } + + void Awake() + { + m_Instance = this; + + BrushController.m_Instance.BrushSetToDefault += OnBrushSetToDefault; + + IconTextureAtlas.Init(); + + m_MultiCamCaptureRig = GetComponentInChildren(true); + m_MultiCamCaptureRig.Init(); + + m_CameraPathCaptureRig = GetComponentInChildren(true); + m_CameraPathCaptureRig.Init(); + + m_SketchSurfacePanel = m_SketchSurface.GetComponent(); + m_PanelManager = GetComponent(); + m_PanelManager.Init(); + InitGazePanels(); + + m_WidgetManager = GetComponent(); + m_WidgetManager.Init(); + + m_InputStateConfigs = new InputStateConfig[(int)InputState.Num]; + for (int i = 0; i < (int)InputState.Num; ++i) + { + m_InputStateConfigs[i] = new InputStateConfig(); + m_InputStateConfigs[i].m_AllowDrawing = false; + m_InputStateConfigs[i].m_AllowMovement = true; + m_InputStateConfigs[i].m_ShowGizmo = false; + } + + m_InputStateConfigs[(int)InputState.Standard].m_AllowDrawing = true; + m_InputStateConfigs[(int)InputState.Pan].m_AllowDrawing = true; + m_InputStateConfigs[(int)InputState.HeadLock].m_AllowDrawing = true; + m_InputStateConfigs[(int)InputState.ControllerLock].m_AllowDrawing = true; + m_InputStateConfigs[(int)InputState.PushPull].m_AllowDrawing = true; + + m_InputStateConfigs[(int)InputState.Pan].m_AllowMovement = false; + m_InputStateConfigs[(int)InputState.Rotation].m_AllowMovement = false; + m_InputStateConfigs[(int)InputState.ControllerLock].m_AllowMovement = false; + m_InputStateConfigs[(int)InputState.PushPull].m_AllowMovement = false; + m_InputStateConfigs[(int)InputState.BrushSize].m_AllowMovement = false; + + m_InputStateConfigs[(int)InputState.Pan].m_ShowGizmo = true; + m_InputStateConfigs[(int)InputState.Rotation].m_ShowGizmo = true; + m_InputStateConfigs[(int)InputState.HeadLock].m_ShowGizmo = true; + m_InputStateConfigs[(int)InputState.PushPull].m_ShowGizmo = true; + + m_CurrentGazeRay = new Ray(Vector3.zero, Vector3.forward); + m_GazeControllerRay = new Ray(Vector3.zero, Vector3.forward); + m_GazeControllerRayActivePanel = new Ray(Vector3.zero, Vector3.forward); + + m_GrabWidgetHoldHistory = new Queue(); + m_GrabWidgetOneHandInfo = new GrabWidgetControllerInfo(); + + // Initialize world grip members. + m_GrabBrush.grabTransform = TrTransform.identity; + m_GrabWand.grabTransform = TrTransform.identity; + + m_BrushResults = new Queue(); + m_WandResults = new Queue(); + m_WidgetGpuIntersectionLayer = LayerMask.NameToLayer("GpuIntersection"); + m_CurrentGrabIntersectionState = GrabIntersectionState.RequestIntersections; + } + + public void InitGazePanels() + { + // Find all gaze panels. + int iNumGazePanels = m_PanelManager.GetAllPanels().Count; + m_GazeResults = new GazeResult[iNumGazePanels]; + for (int i = 0; i < iNumGazePanels; ++i) + { + m_GazeResults[i] = new GazeResult(); + m_GazeResults[i].m_HitWithGaze = false; + m_GazeResults[i].m_HitWithController = false; + m_GazeResults[i].m_WithinView = false; + m_GazeResults[i].m_GazePosition = new Vector3(); + } + } + + public void OnEnable() + { + // This needs to run before other tools initialize, which is why it's running in OnEnable. + // The sequence is Awake(), OnEnable(), Start(). + if (App.VrSdk.GetControllerDof() == VrSdk.DoF.Six) + { + SetInFreePaintMode(true); + SetInitialTool(BaseTool.ToolType.FreePaintTool); + } + } + + void Start() + { + m_TransformGizmo = (GameObject)Instantiate(m_TransformGizmoPrefab); + m_TransformGizmo.transform.parent = transform; + m_TransformGizmoScript = m_TransformGizmo.GetComponent(); + m_TransformGizmo.SetActive(false); + + m_RotationIcon = (GameObject)Instantiate(m_RotationIconPrefab); + m_RotationIcon.transform.position = m_SketchSurface.transform.position; + m_RotationIcon.transform.parent = m_SketchSurface.transform; + m_RotationIcon.SetActive(false); + + GameObject pinCushionObj = (GameObject)Instantiate(m_PinCushionPrefab); + m_PinCushion = pinCushionObj.GetComponent(); + + m_PositionOffsetResetTapTime = 0.0f; + + m_UndoHold_Timer = m_UndoRedoHold_DurationBeforeStart; + m_RedoHold_Timer = m_UndoRedoHold_DurationBeforeStart; + + m_AutoOrientAfterRotation = true; + m_RotationCursor.gameObject.SetActive(false); + + ResetGrabbedPose(); + m_SketchOrigin = m_SketchSurface.transform.position; + + m_PanelManager.InitPanels(m_ControlsType == ControlsType.SixDofControllers); + + m_UIReticleMobile.SetActive(m_ControlsType == ControlsType.ViewingOnly); + m_UIReticleSixDofController.SetActive(m_ControlsType != ControlsType.ViewingOnly); + + m_PositioningPanelWithHead = false; + m_PositioningSpeed = 16.0f; + + m_CurrentRotationType = RotationType.All; + m_RotationResetTapTime = 0.0f; + + m_CurrentInputState = InputState.Standard; + + m_SketchSurfacePanel.EnableSpecificTool(m_InitialTool); + m_SurfaceLockControllerBaseScalar = m_SketchSurfacePanel.m_PanelSensitivity; + + //after initializing, start with gaze objects hidden + m_CurrentGazeObject = -1; + m_EatInputGazeObject = false; + + // Previously set to 0 in experimental builds + int hidePanelsDelay = 1; + + StartCoroutine(DelayedHidePanels(hidePanelsDelay)); + + m_DropCam.Show(false); + + m_GrabWidgetState = GrabWidgetState.None; + + UpdateDraftingVisibility(); + + m_DisableWorldGrabbing = false; + } + + private IEnumerator DelayedHidePanels(int frames) + { + int stall = frames; + while (stall-- > 0) + { + yield return null; + } + + m_PanelManager.HidePanelsForStartup(); + RequestPanelsVisibility(false); + } + + void Update() + { + // TODO: we need to figure out what transform to pass in here! + // Maybe best _just for now_ to use the scene transform? + TrTransform scenePose = App.Scene.Pose; + Shader.SetGlobalMatrix("xf_CS", scenePose.ToMatrix4x4()); + Shader.SetGlobalMatrix("xf_I_CS", scenePose.inverse.ToMatrix4x4()); + } + + void LateUpdate() + { + // Gracefully exits if we're not recording a video. + VideoRecorderUtils.SerializerNewUsdFrame(); + } + + public bool IsFreepaintToolReady() + { + return + !m_PinCushion.IsShowing() && + !PointerManager.m_Instance.IsStraightEdgeProxyActive() && + !InputManager.m_Instance.ControllersAreSwapping() && + (m_SketchSurfacePanel.IsSketchSurfaceToolActive() || + (m_SketchSurfacePanel.GetCurrentToolType() == BaseTool.ToolType.FreePaintTool)) + ; + } + + public void UpdateControls() + { + UnityEngine.Profiling.Profiler.BeginSample("SketchControlsScript.UpdateControls"); + m_SketchSurfacePanel.m_UpdatedToolThisFrame = false; + + // Verify controllers are available and prune state if they're not. + if ((App.VrSdk.GetControllerDof() == VrSdk.DoF.Six && + App.VrSdk.IsInitializingUnityXR) && App.VrSdk.IsHmdInitialized()) + { + m_PanelManager.SetVisible(false); + PointerManager.m_Instance.RequestPointerRendering(false); + return; + } + + //mouse movement + Vector2 mv = InputManager.m_Instance.GetMouseMoveDelta(); + m_MouseDeltaX = mv.x; + m_MouseDeltaY = mv.y; + + UpdateGazeObjectsAnimationState(); + UpdateCurrentGazeRay(); + m_SketchSurfacePanel.SetBacksideActive(m_CurrentGazeRay.origin); + m_PanelManager.UpdatePanels(); + + m_MouseDeltaXScaled = m_MouseDeltaX * GetAppropriateMovementScalar(); + m_MouseDeltaYScaled = m_MouseDeltaY * GetAppropriateMovementScalar(); + + //this is used for one-shot inputs that don't require state and do not change state + UpdateBaseInput(); + + UpdatePinCushionVisibility(); + + //if the pointer manager is processing, we don't want to respond to input + if (!PointerManager.m_Instance.IsMainPointerProcessingLine()) + { + + //see if we're grabbing a widget + UpdateGrab(); + + //see if we're looking at a gaze object + RefreshCurrentGazeObject(); + + // Tools allowed when widgets aren't grabbed. + bool bWidgetGrabOK = m_GrabWidgetState == GrabWidgetState.None; + + // If we don't have a widget held and we're not grabbing the world with the brush controller, + // update tools. + if (bWidgetGrabOK && !m_GrabBrush.grabbingWorld) + { + if (m_CurrentGazeObject != -1 && !m_WorldBeingGrabbed) + { + UpdateActiveGazeObject(); + + // Allow for standard input (like Undo / Redo) even when gazing at a panel. + if (m_CurrentInputState == InputState.Standard) + { + UpdateStandardInput(); + } + } + else + { + //standard input, no gaze object + if (m_InputStateConfigs[(int)m_CurrentInputState].m_AllowMovement) + { + m_SketchSurfacePanel.UpdateReticleOffset(m_MouseDeltaX, m_MouseDeltaY); + } + + switch (m_CurrentInputState) + { + case InputState.Standard: + UpdateStandardInput(); + break; + case InputState.Pan: + UpdatePanInput(); + break; + case InputState.Rotation: + UpdateRotationInput(); + break; + case InputState.HeadLock: + UpdateHeadLockInput(); + break; + case InputState.ControllerLock: + UpdateControllerLock(); + break; + case InputState.PushPull: + UpdatePushPullInput(); + break; + case InputState.Save: + UpdateSaveInput(); + break; + case InputState.Load: + UpdateLoadInput(); + break; + } + + //keep pointer locked in the right spot, even if it's hidden + if (m_SketchSurfacePanel.ActiveTool.LockPointerToSketchSurface()) + { + Vector3 vPointerPos = Vector3.zero; + Vector3 vPointerForward = Vector3.zero; + m_SketchSurfacePanel.GetReticleTransform(out vPointerPos, out vPointerForward, + (m_ControlsType == ControlsType.ViewingOnly)); + PointerManager.m_Instance.SetMainPointerPosition(vPointerPos); + PointerManager.m_Instance.SetMainPointerForward(vPointerForward); + } + + m_SketchSurfacePanel.AllowDrawing(m_InputStateConfigs[(int)m_CurrentInputState].m_AllowDrawing); + m_SketchSurfacePanel.UpdateCurrentTool(); + + PointerManager.m_Instance.AllowPointerPreviewLine(IsFreepaintToolReady()); + //keep transform gizmo at sketch surface pos + m_TransformGizmo.transform.position = m_SketchSurface.transform.position; + bool bGizmoActive = m_InputStateConfigs[(int)m_CurrentInputState].m_ShowGizmo && m_SketchSurfacePanel.ShouldShowTransformGizmo(); + m_TransformGizmo.SetActive(bGizmoActive); + } + } + } + + // Update any transition to a scene transform reset. + UpdateWorldTransformReset(); + + //update our line after all input and tools have chimed in on the state of it + PointerManager.m_Instance.UpdateLine(); + UnityEngine.Profiling.Profiler.EndSample(); + } + + public void UpdateControlsPostIntro() + { + m_PanelManager.UpdatePanels(); + UpdateCurrentGazeRay(); + UpdateGazeObjectsAnimationState(); + RefreshCurrentGazeObject(); + UpdateSwapControllers(); + if (m_CurrentGazeObject > -1) + { + UpdateActiveGazeObject(); + } + } + + public void UpdateControlsForLoading() + { + UpdateCurrentGazeRay(); + m_PanelManager.UpdatePanels(); + UpdateGazeObjectsAnimationState(); + UpdateGrab(); + UpdateWorldTransformReset(); + + if (m_GrabWidgetState == GrabWidgetState.None && m_CurrentGazeObject == -1 && + m_SketchSurfacePanel.ActiveTool.AvailableDuringLoading() && + !m_GrabBrush.grabbingWorld) + { + m_SketchSurfacePanel.UpdateCurrentTool(); + } + } + + public void UpdateControlsForReset() + { + UpdateGrab(); + UpdateCurrentGazeRay(); + UpdatePinCushionVisibility(); + m_PanelManager.UpdatePanels(); + UpdateGazeObjectsAnimationState(); + PointerManager.m_Instance.UpdateLine(); + } + + public void UpdateControlsForUploading() + { + UpdateCurrentGazeRay(); + UpdatePinCushionVisibility(); + m_PanelManager.UpdatePanels(); + UpdateGazeObjectsAnimationState(); + } + + public void UpdateControlsForMemoryExceeded() + { + UpdateGrab(); + m_SketchSurfacePanel.m_UpdatedToolThisFrame = false; + m_PanelManager.UpdatePanels(); + UpdateCurrentGazeRay(); + UpdateGazeObjectsAnimationState(); + RefreshCurrentGazeObject(); + if (m_CurrentGazeObject > -1) + { + UpdateActiveGazeObject(); + } + } + + void UpdatePinCushionVisibility() + { + // If the pin cushion is showing and the user cancels, eat the input. + // if (m_PinCushion.IsShowing()) + // { + // if (InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Activate) || + // InputManager.Brush.GetControllerGrip() || + // InputManager.Wand.GetControllerGrip() || + // IsUserInteractingWithAnyWidget() || + // IsUserInteractingWithUI()) + // { + // m_EatPinCushionInput = true; + // } + // } + + // If our tool wants the input blocked, maintain the input eat state until + // after the user has let off input. + if (m_SketchSurfacePanel.ActiveTool.BlockPinCushion() || !CanUsePinCushion()) + { + m_EatPinCushionInput = true; + } + + bool show = + InputManager.m_Instance.GetCommand(InputManager.SketchCommands.ShowPinCushion); + m_PinCushion.ShowPinCushion(show && !m_EatPinCushionInput); + m_EatPinCushionInput = m_EatPinCushionInput && show; + } + + bool CanUsePinCushion() + { + return (m_ControlsType == ControlsType.SixDofControllers) && + m_PanelManager.AdvancedModeActive() && + !InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Activate) && + !InputManager.Brush.GetControllerGrip() && + !InputManager.Wand.GetControllerGrip() && + !IsUserInteractingWithAnyWidget() && + !IsUserInteractingWithUI() && + !m_SketchSurfacePanel.ActiveTool.BlockPinCushion() && + App.Instance.IsInStateThatAllowsPainting(); + } + + void UpdateCurrentGazeRay() + { + var head = ViewpointScript.Head; + m_CurrentGazeRay = new Ray(head.position, head.forward); + m_CurrentHeadOrientation = head.rotation; + + // We use the gaze ray for certain shader effects - like edge falloff. + Shader.SetGlobalVector("_WorldSpaceRootCameraPosition", m_CurrentGazeRay.origin); + bool hasController = m_ControlsType == ControlsType.SixDofControllers; + if (hasController) + { + if (InputManager.Brush.IsTrackedObjectValid) + { + Transform rAttachPoint = InputManager.m_Instance.GetBrushControllerAttachPoint(); + m_GazeControllerRay.direction = rAttachPoint.forward; + m_GazeControllerRay.origin = rAttachPoint.position; + } + else + { + // If the brush controller isn't tracked, put our controller ray out of the way. + float fBig = 9999999.0f; + m_GazeControllerRay.direction = Vector3.one; + m_GazeControllerRay.origin = new Vector3(fBig, fBig, fBig); + } + + m_GazeControllerRayActivePanel.direction = m_GazeControllerRay.direction; + m_GazeControllerRayActivePanel.origin = m_GazeControllerRay.origin; + m_GazeControllerRayActivePanel.origin -= (m_GazeControllerRayActivePanel.direction * 0.5f); + } + } + + public void UpdateGazeObjectsAnimationState() + { + // Are the panels allowed to be visible? + bool isSixDof = m_ControlsType == ControlsType.SixDofControllers; + if ((!isSixDof) || + (InputManager.Wand.IsTrackedObjectValid && + !m_SketchSurfacePanel.ActiveTool.HidePanels() && + !App.Instance.IsLoading())) + { + // Transition panels according to requested visibility. + m_PanelManager.SetVisible(m_PanelsVisibilityRequested); + } + else + { + // Transition out. + m_PanelManager.SetVisible(false); + } + } + + void UpdateBaseInput() + { + UnityEngine.Profiling.Profiler.BeginSample("SketchControlScript.UpdateBaseInput"); + if (m_ControlsType == ControlsType.SixDofControllers) + { + m_PanelManager.UpdateWandOrientationControls(); + } + + //allow tool scaling if we're not drawing and our input device is active + bool bScaleInputActive = InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Scale); + bool bScaleCommandActive = + bScaleInputActive + && !InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Activate) + && m_GrabBrush.grabbingWorld == false + && m_CurrentGazeObject == -1 // free up swipe for use by gaze object + && (m_ControlsType != ControlsType.SixDofControllers || InputManager.Brush.IsTrackedObjectValid) // TODO:Mikesky - very hacky + && SketchSurfacePanel.m_Instance.ActiveTool.m_Type != BaseTool.ToolType.MultiCamTool; + + if (m_EatToolScaleInput) + { + m_EatToolScaleInput = bScaleInputActive; + } + + if (bScaleCommandActive && !m_EatToolScaleInput) + { + if (m_GrabWidgetState == GrabWidgetState.None) + { + //send scale command down to current tool + m_SketchSurfacePanel.UpdateToolSize( + m_AdjustToolSizeScalar * InputManager.m_Instance.GetAdjustedBrushScrollAmount()); + } + + //ugly, but brush size is becoming not an input state + m_MouseDeltaX = 0.0f; + m_MouseDeltaY = 0.0f; + } + + UpdateSwapControllers(); + UnityEngine.Profiling.Profiler.EndSample(); + } + + void UpdateSwapControllers() + { + // Don't allow controller swap in first run intro. + // Don't allow controller swap if we're grabbing a widget. + // Don't allow controller swap if a Logitech pen is present. + if (!TutorialManager.m_Instance.TutorialActive() && + m_GrabWidgetState == GrabWidgetState.None && + !App.VrSdk.VrControls.LogitechPenIsPresent()) + { + if (InputManager.m_Instance.GetCommandDown(InputManager.SketchCommands.SwapControls)) + { + DoSwapControls(); + } + } + } + + public static void DoSwapControls() + { + InputManager.m_Instance.WandOnRight = !InputManager.m_Instance.WandOnRight; + InputManager.m_Instance.GetControllerBehavior(InputManager.ControllerName.Brush) + .DisplayControllerSwapAnimation(); + InputManager.m_Instance.GetControllerBehavior(InputManager.ControllerName.Wand) + .DisplayControllerSwapAnimation(); + AudioManager.m_Instance.PlayControllerSwapSound( + InputManager.m_Instance.GetControllerPosition(InputManager.ControllerName.Brush)); + } + + void UpdateStandardInput() + { + UnityEngine.Profiling.Profiler.BeginSample("SketchControlScript.UpdateStandardInput"); + //debug keys + if (App.UserConfig.Flags.AdvancedKeyboardShortcuts) + { + var camTool = SketchSurfacePanel.m_Instance.ActiveTool as MultiCamTool; + + if (InputManager.m_Instance.GetKeyboardShortcutDown(InputManager.KeyboardShortcut.SaveNew)) + { + IssueGlobalCommand(GlobalCommands.SaveNew, 1); + } + else if (InputManager.m_Instance.GetKeyboardShortcutDown( + InputManager.KeyboardShortcut.SwitchCamera) && camTool != null) + { + camTool.ExternalObjectNextCameraStyle(); // For monoscopic mode + } + else if (InputManager.m_Instance.GetKeyboardShortcutDown( + InputManager.KeyboardShortcut.ViewOnly)) + { + IssueGlobalCommand(GlobalCommands.ViewOnly); + } + else if (InputManager.m_Instance.GetKeyboardShortcutDown( + InputManager.KeyboardShortcut.ToggleScreenMirroring)) + { + ViewpointScript.m_Instance.ToggleScreenMirroring(); + } + else if (InputManager.m_Instance.GetKeyboardShortcutDown( + InputManager.KeyboardShortcut.PreviousTool)) + { + m_SketchSurfacePanel.PreviousTool(); + } + else if (InputManager.m_Instance.GetKeyboardShortcutDown( + InputManager.KeyboardShortcut.NextTool)) + { + m_SketchSurfacePanel.NextTool(); + } + else if (InputManager.m_Instance.GetKeyboardShortcutDown( + InputManager.KeyboardShortcut.CycleSymmetryMode)) + { + var cur = PointerManager.m_Instance.CurrentSymmetryMode; + var next = (cur == SymmetryMode.None) ? SymmetryMode.SinglePlane + : (cur == SymmetryMode.SinglePlane) ? SymmetryMode.DebugMultiple + : (cur == SymmetryMode.DebugMultiple) ? SymmetryMode.MultiMirror + : (cur == SymmetryMode.MultiMirror) ? SymmetryMode.TwoHanded + : SymmetryMode.None; + PointerManager.m_Instance.CurrentSymmetryMode = next; + } + else if (InputManager.m_Instance.GetKeyboardShortcutDown( + InputManager.KeyboardShortcut.Export)) + { + StartCoroutine(ExportCoroutine()); + } + else if (InputManager.m_Instance.GetKeyboardShortcutDown( + InputManager.KeyboardShortcut.StoreHeadTransform) && + InputManager.m_Instance.GetAnyShift()) + { + Transform head = ViewpointScript.Head; + PlayerPrefs.SetFloat("HeadOffset_localPositionX", head.localPosition.x); + PlayerPrefs.SetFloat("HeadOffset_localPositionY", head.localPosition.y); + PlayerPrefs.SetFloat("HeadOffset_localPositionZ", head.localPosition.z); + PlayerPrefs.SetFloat("HeadOffset_localRotationX", head.localRotation.x); + PlayerPrefs.SetFloat("HeadOffset_localRotationY", head.localRotation.y); + PlayerPrefs.SetFloat("HeadOffset_localRotationZ", head.localRotation.z); + PlayerPrefs.SetFloat("HeadOffset_localRotationW", head.localRotation.w); + } + else if (InputManager.m_Instance.GetKeyboardShortcutDown( + InputManager.KeyboardShortcut.RecallHeadTransform)) + { + Transform head = ViewpointScript.Head; + // Toggle the head offset. + if (m_HeadOffset) + { + // Remove the offset. + Transform originalParent = head.parent; + head.SetParent(head.parent.parent); + GameObject.DestroyImmediate(originalParent.gameObject); + m_HeadOffset = false; + } + else + { + // Add the offset. + GameObject newParent = new GameObject(); + newParent.transform.SetParent(head.parent); + newParent.transform.localPosition = Vector3.zero; + newParent.transform.localRotation = Quaternion.identity; + newParent.transform.localScale = Vector3.one; + head.SetParent(newParent.transform); + TrTransform offsetTransform = TrTransform.TR( + new Vector3( + PlayerPrefs.GetFloat("HeadOffset_localPositionX", 0), + PlayerPrefs.GetFloat("HeadOffset_localPositionY", 1.5f), + PlayerPrefs.GetFloat("HeadOffset_localPositionZ", 0)), + new Quaternion( + PlayerPrefs.GetFloat("HeadOffset_localRotationX", 0), + PlayerPrefs.GetFloat("HeadOffset_localRotationY", 0), + PlayerPrefs.GetFloat("HeadOffset_localRotationZ", 0), + PlayerPrefs.GetFloat("HeadOffset_localRotationW", 1))); + TrTransform originalTransformInverse = TrTransform.FromLocalTransform(head).inverse; + TrTransform newParentTransform = offsetTransform * originalTransformInverse; + newParent.transform.localPosition = newParentTransform.translation; + newParent.transform.localRotation = newParentTransform.rotation; + m_HeadOffset = true; + } + } + else if (InputManager.m_Instance.GetKeyboardShortcutDown( + InputManager.KeyboardShortcut.ToggleLightType)) + { + // Toggle between per-pixel & SH lighting on the secondary directional light + Light secondaryLight = App.Scene.GetLight((1)); + if (LightRenderMode.ForceVertex == secondaryLight.renderMode) + { + secondaryLight.renderMode = LightRenderMode.ForcePixel; + } + else + { + secondaryLight.renderMode = LightRenderMode.ForceVertex; + } + } + else if (InputManager.m_Instance.GetKeyboardShortcutDown( + InputManager.KeyboardShortcut.TossWidget)) + { + m_WidgetManager.TossNearestWidget(); + } + else if (InputManager.m_Instance.GetKeyboardShortcutDown( + InputManager.KeyboardShortcut.Reset)) + { + App.Instance.SetDesiredState(App.AppState.LoadingBrushesAndLighting); + } + else if (InputManager.m_Instance.GetKeyboardShortcutDown( + InputManager.KeyboardShortcut.FlyMode)) + { + SketchSurfacePanel.m_Instance.EnableSpecificTool(BaseTool.ToolType.FlyTool); + } + else if (App.Config.m_ToggleProfileOnAppButton && + (InputManager.Wand.GetVrInputDown(VrInput.Button03) || + InputManager.m_Instance.GetKeyboardShortcutDown( + InputManager.KeyboardShortcut.ToggleProfile))) + { + IssueGlobalCommand(GlobalCommands.ToggleProfiling); + } + } + +#if DEBUG + if (InputManager.m_Instance.GetKeyboardShortcutDown( + InputManager.KeyboardShortcut.CheckStrokes)) + { + bool value = !SketchMemoryScript.m_Instance.m_SanityCheckStrokes; + string feature = "Stroke determinism checking"; + SketchMemoryScript.m_Instance.m_SanityCheckStrokes = value; + OutputWindowScript.m_Instance.CreateInfoCardAtController( + InputManager.ControllerName.Brush, + feature + (value ? ": On" : ": Off")); + } +#endif + + bool hasController = m_ControlsType == ControlsType.SixDofControllers; + var mouse = Mouse.current; + + // Toggle default tool. + if (!m_PanelManager.AdvancedModeActive() && + InputManager.m_Instance.GetCommandDown(InputManager.SketchCommands.ToggleDefaultTool) && + !m_SketchSurfacePanel.IsDefaultToolEnabled() && + m_SketchSurfacePanel.ActiveTool.AllowDefaultToolToggle() && m_CurrentGazeObject == -1)// don't allow tool to change while pointing at panel because there is no visual indication + { + m_SketchSurfacePanel.EnableDefaultTool(); + AudioManager.m_Instance.PlayPinCushionSound(true); + } + // Pan. + else if (!hasController && mouse.rightButton.isPressed) + { + SwitchState(InputState.Pan); + } + // Controller lock (this must be before rotate/head lock!). + else if (!hasController && + InputManager.m_Instance.GetCommand(InputManager.SketchCommands.LockToController)) + { + SwitchState(InputState.ControllerLock); + } + // Rotate. + else if (!hasController && + InputManager.m_Instance.GetCommand(InputManager.SketchCommands.PivotRotation)) + { + SwitchState(InputState.Rotation); + } + // Head lock. + else if (!hasController && + InputManager.m_Instance.GetCommand(InputManager.SketchCommands.LockToHead)) + { + SwitchState(InputState.HeadLock); + } + // Push pull. + else if (!hasController && + InputManager.m_Instance.GetCommand(InputManager.SketchCommands.AltActivate)) + { + SwitchState(InputState.PushPull); + } + else if (!PointerManager.m_Instance.IsMainPointerCreatingStroke()) + { + // Reset surface. + if (!hasController && + InputManager.m_Instance.GetCommandDown(InputManager.SketchCommands.Reset)) + { + ResetGrabbedPose(); + } + // Undo. + else if (InputManager.m_Instance.GetCommandDown(InputManager.SketchCommands.Undo) && + CanUndo()) + { + IssueGlobalCommand(GlobalCommands.Undo); + } + else if (InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Undo) && + CanUndo() && ShouldRepeatUndo()) + { + m_UndoHold_Timer = m_UndoRedoHold_RepeatInterval; + IssueGlobalCommand(GlobalCommands.Undo); + } + // Redo. + else if (InputManager.m_Instance.GetCommandDown(InputManager.SketchCommands.Redo) && + CanRedo()) + { + IssueGlobalCommand(GlobalCommands.Redo); + } + else if (InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Redo) && + CanRedo() && ShouldRepeatRedo()) + { + m_RedoHold_Timer = m_UndoRedoHold_RepeatInterval; + IssueGlobalCommand(GlobalCommands.Redo); + } + // Reset scene. + else if (!hasController && + InputManager.m_Instance.GetKeyboardShortcutDown( + InputManager.KeyboardShortcut.ResetScene)) + { + // TODO: Should thsi go away? Seems like the "sweetspot" may no longer be used. + if (App.VrSdk.GetControllerDof() == VrSdk.DoF.Two) + { + m_PanelManager.SetSweetSpotPosition(m_CurrentGazeRay.origin); + ResetGrabbedPose(); + } + } + // Straight edge. + else if (!hasController && + InputManager.m_Instance.GetKeyboardShortcutDown( + InputManager.KeyboardShortcut.StraightEdge)) + { + IssueGlobalCommand(GlobalCommands.StraightEdge); + } + // Always fall back on switching tools. + else + { + m_SketchSurfacePanel.CheckForToolSelection(); + } + } + + // Reset undo/redo hold timers. + if (!InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Undo)) + { + m_UndoHold_Timer = m_UndoRedoHold_DurationBeforeStart; + } + if (!InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Redo)) + { + m_RedoHold_Timer = m_UndoRedoHold_DurationBeforeStart; + } + UnityEngine.Profiling.Profiler.EndSample(); + } + + bool CanUndo() + { + return SketchMemoryScript.m_Instance.CanUndo() && + !IsUIBlockingUndoRedo() && + m_PanelManager.GazePanelsAreVisible() && + !m_GrabWand.grabbingWorld && + !InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Activate) && + !SelectionManager.m_Instance.IsAnimatingTossFromGrabbingGroup; + } + + bool CanRedo() + { + return SketchMemoryScript.m_Instance.CanRedo() && + !IsUIBlockingUndoRedo() && + m_PanelManager.GazePanelsAreVisible() && + !m_GrabBrush.grabbingWorld && + !InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Activate) && + !SelectionManager.m_Instance.IsAnimatingTossFromGrabbingGroup; + } + + bool ShouldRepeatUndo() + { + m_UndoHold_Timer -= Time.deltaTime; + return (m_UndoHold_Timer <= 0.0f); + } + + bool ShouldRepeatRedo() + { + m_RedoHold_Timer -= Time.deltaTime; + return (m_RedoHold_Timer <= 0.0f); + } + + // Updates the global state: + // m_CurrentGrabWidget + void UpdateGrab() + { + UnityEngine.Profiling.Profiler.BeginSample("SketchControlScript.UpdateGrab"); + if (m_ControlsType != ControlsType.SixDofControllers) + { + UnityEngine.Profiling.Profiler.EndSample(); + return; + } + + GrabWidget rPrevGrabWidget = m_CurrentGrabWidget; + GrabWidget rPrevPotentialBrush = m_PotentialGrabWidgetBrush; + GrabWidget rPrevPotentialWand = m_PotentialGrabWidgetWand; + if (m_CurrentGrabWidget) + { + m_CurrentGrabWidget.Activate(false); + } + if (m_PotentialGrabWidgetBrush) + { + m_PotentialGrabWidgetBrush.Activate(false); + } + if (m_PotentialGrabWidgetWand) + { + m_PotentialGrabWidgetWand.Activate(false); + } + m_CurrentGrabWidget = null; + m_PotentialGrabWidgetBrush = null; + m_PotentialGrabWidgetWand = null; + m_PotentialGrabWidgetBrushValid = false; + m_PotentialGrabWidgetWandValid = false; + + m_WidgetManager.RefreshNearestWidgetLists(m_CurrentGazeRay, m_CurrentGazeObject); + + if (m_GrabWidgetState == GrabWidgetState.None) + { + UpdateGrab_WasNone(rPrevPotentialBrush, rPrevPotentialWand); + } + else if (m_GrabWidgetState == GrabWidgetState.OneHand) + { + UpdateGrab_WasOneHand(rPrevGrabWidget); + } + else if (m_GrabWidgetState == GrabWidgetState.TwoHands) + { + UpdateGrab_WasTwoHands(rPrevGrabWidget); + } + + // Update grab intersection state. + switch (m_CurrentGrabIntersectionState) + { + case GrabIntersectionState.RequestIntersections: + m_CurrentGrabIntersectionState = GrabIntersectionState.ReadBrush; + break; + case GrabIntersectionState.ReadBrush: + m_CurrentGrabIntersectionState = GrabIntersectionState.ReadWand; + break; + case GrabIntersectionState.ReadWand: + m_CurrentGrabIntersectionState = GrabIntersectionState.RequestIntersections; + break; + } + + if (!TutorialManager.m_Instance.TutorialActive() && m_CurrentGrabWidget == null) + { + UpdateGrab_World(); + } + + App.Instance.SelectionEffect.HighlightForGrab( + m_GrabWidgetState != GrabWidgetState.None || + (m_PotentialGrabWidgetBrush != null && m_PotentialGrabWidgetBrushValid) || + (m_PotentialGrabWidgetWand != null && m_PotentialGrabWidgetWandValid)); + UnityEngine.Profiling.Profiler.EndSample(); + } + + void UpdateGrab_WasNone(GrabWidget rPrevPotentialBrush, GrabWidget rPrevPotentialWand) + { + // if a panel isn't in focus, allow for widget grab + // We can grab a widget as long as we aren't trying to draw with that hand. + bool bActiveInput = + (InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Activate) && + App.Instance.IsInStateThatAllowsPainting()); + + //certain tools don't allow us to mess with widgets + bool bWidgetManipOK = m_SketchSurfacePanel.DoesCurrentToolAllowWidgetManipulation() && + !m_GrabWand.grabbingWorld && !m_GrabBrush.grabbingWorld && IsGrabWorldStateStable() && + App.Instance.IsInStateThatAllowsAnyGrabbing(); + + // Update EatInput flags if they're valid. + if (m_GrabBrush.eatInput) + { + m_GrabBrush.eatInput = InputManager.Brush.GetControllerGrip(); + } + if (m_GrabWand.eatInput) + { + m_GrabWand.eatInput = InputManager.Wand.GetControllerGrip(); + } + + bool bShouldClearWandInside = false; + if (m_CurrentInputState == InputState.Standard && bWidgetManipOK) + { + // If we're in the intersection request state, fire off a new intersection request. If + // we're in the read brush state, update our brush grab data structure. + List brushBests = m_WidgetManager.WidgetsNearBrush; + if (m_CurrentGrabIntersectionState == GrabIntersectionState.RequestIntersections) + { + RequestWidgetIntersection(brushBests, InputManager.ControllerName.Brush); + } + else if (m_CurrentGrabIntersectionState == GrabIntersectionState.ReadBrush) + { + m_BackupBrushGrabData = GetBestWidget(brushBests, m_BrushResults); + } + + if (m_BackupBrushGrabData != null) + { + m_PotentialGrabWidgetBrush = m_BackupBrushGrabData.m_WidgetScript; + + // Allow widget grab if we're not painting. + if (!bActiveInput) + { + m_PotentialGrabWidgetBrush.Activate(true); + m_PotentialGrabWidgetBrushValid = true; + m_PotentialGrabWidgetBrush.VisualizePinState(); + + if (!m_GrabBrush.eatInput && InputManager.Brush.GetControllerGrip()) + { + m_CurrentGrabWidget = m_PotentialGrabWidgetBrush; + if (m_CurrentGrabWidget.Group != SketchGroupTag.None) + { + m_GrabBrush.grabbingGroup = true; + m_CurrentGrabWidget = + SelectionManager.m_Instance.StartGrabbingGroupWithWidget(m_CurrentGrabWidget); + } + UpdateGrab_NoneToOne(InputManager.ControllerName.Brush); + bShouldClearWandInside = true; + m_GrabBrush.startedGrabInsideWidget = true; + } + } + } + m_GrabBrush.SetHadBestGrabAndTriggerHaptics(m_BackupBrushGrabData); + m_ControllerGrabVisuals.BrushInWidgetRange = m_BackupBrushGrabData != null; + + // If we're in the intersection request state, fire off a new intersection request. If + // we're in the read wand state, update our wand grab data structure. + List wandBests = m_WidgetManager.WidgetsNearWand; + if (m_CurrentGrabIntersectionState == GrabIntersectionState.RequestIntersections) + { + RequestWidgetIntersection(wandBests, InputManager.ControllerName.Wand); + } + else if (m_CurrentGrabIntersectionState == GrabIntersectionState.ReadWand) + { + m_BackupWandGrabData = GetBestWidget(wandBests, m_WandResults); + } + + if (m_BackupWandGrabData != null) + { + m_PotentialGrabWidgetWand = m_BackupWandGrabData.m_WidgetScript; + // Allow wand widget grab if brush grab failed. + bool bGrabAllowed = (m_GrabWidgetState == GrabWidgetState.None) && !bActiveInput; + if (bGrabAllowed) + { + m_PotentialGrabWidgetWand.Activate(true); + m_PotentialGrabWidgetWandValid = true; + m_PotentialGrabWidgetWand.VisualizePinState(); + + if (!m_GrabWand.eatInput && InputManager.Wand.GetControllerGrip()) + { + m_CurrentGrabWidget = m_PotentialGrabWidgetWand; + if (m_CurrentGrabWidget.Group != SketchGroupTag.None) + { + m_GrabWand.grabbingGroup = true; + m_CurrentGrabWidget = + SelectionManager.m_Instance.StartGrabbingGroupWithWidget(m_CurrentGrabWidget); + } + UpdateGrab_NoneToOne(InputManager.ControllerName.Wand); + m_GrabBrush.ClearInsideWidget(); + m_GrabWand.startedGrabInsideWidget = true; + } + } + } + m_GrabWand.SetHadBestGrabAndTriggerHaptics(m_BackupWandGrabData); + m_ControllerGrabVisuals.WandInWidgetRange = m_BackupWandGrabData != null; + + // Account for asymmetry in controller processing by clearing after wand has updated + // GrabState.insideWidget according to bestWandGrab. + if (bShouldClearWandInside) + { + m_GrabWand.ClearInsideWidget(); + } + } + + // Update widget collisions if we've got a drifter. + if (m_GrabWidgetState == GrabWidgetState.None) + { + if (m_WidgetManager.ShouldUpdateCollisions()) + { + m_PanelManager.DoCollisionSimulationForWidgetPanels(); + } + } + } + + void UpdateGrab_WasOneHand(GrabWidget rPrevGrabWidget) + { + var controller = InputManager.Controllers[(int)m_GrabWidgetOneHandInfo.m_Name]; + bool shouldRelease = !App.Instance.IsInStateThatAllowsAnyGrabbing(); + if (!InputManager.Controllers[(int)m_GrabWidgetOneHandInfo.m_Name].GetControllerGrip() || + shouldRelease) + { + if (shouldRelease) + { + EatGrabInput(); + } + + Vector3 vLinearVelocity; + Vector3 vAngularVelocity; + if (GetGrabWidgetHoldHistory(out vLinearVelocity, out vAngularVelocity)) + { + rPrevGrabWidget.SetVelocities( + vLinearVelocity, vAngularVelocity, + controller.Transform.position); + } + // One -> None + UpdateGrab_ToNone(rPrevGrabWidget); + } + else + { + // Keep holding on to our widget. + m_CurrentGrabWidget = rPrevGrabWidget; + m_CurrentGrabWidget.Activate(true); + m_CurrentGrabWidget.UserInteracting(true, m_GrabWidgetOneHandInfo.m_Name); + + if (!m_CurrentGrabWidget.Pinned) + { + var info = InputManager.Controllers[(int)m_GrabWidgetOneHandInfo.m_Name]; + var controllerXf = Coords.AsGlobal[info.Transform]; + var newWidgetXf = controllerXf * m_GrabWidgetOneHandInfo.m_BaseWidgetXf_LS; + m_CurrentGrabWidget.RecordAndSetPosRot(newWidgetXf); + + UpdateGrabWidgetHoldHistory(m_GrabWidgetOneHandInfo.m_Name); + } + + m_PanelManager.DoCollisionSimulationForWidgetPanels(); + + // Check for widget pinning. + if (m_CurrentGrabWidget.AllowPinning) + { + if (InputManager.Controllers[(int)m_GrabWidgetOneHandInfo.m_Name].GetCommandDown( + InputManager.SketchCommands.PinWidget)) + { + // If the user initiates a pin action, buzz a bit. + if (!m_CurrentGrabWidget.Pinned) + { + InputManager.m_Instance.TriggerHapticsPulse( + m_GrabWidgetOneHandInfo.m_Name, 3, 0.10f, 0.07f); + } + m_CurrentGrabWidget.Pin(!m_CurrentGrabWidget.Pinned); + SketchSurfacePanel.m_Instance.EatToolsInput(); + m_WidgetManager.RefreshPinAndUnpinLists(); + } + } + + if (m_CurrentGrabWidget is SelectionWidget) + { + if (InputManager.m_Instance.GetCommandDown( + InputManager.SketchCommands.DuplicateSelection)) + { + controller.LastHeldInput = + controller.GetCommandHoldInput(InputManager.SketchCommands.DuplicateSelection); + } + + if (controller.LastHeldInput != null && + InputManager.m_Instance.GetCommandHeld(InputManager.SketchCommands.DuplicateSelection)) + { + SketchControlsScript.m_Instance.IssueGlobalCommand( + SketchControlsScript.GlobalCommands.Duplicate); + } + } + + InputManager.ControllerName otherName = + (m_GrabWidgetOneHandInfo.m_Name == InputManager.ControllerName.Brush) ? + InputManager.ControllerName.Wand : InputManager.ControllerName.Brush; + bool otherInputEaten = + (m_GrabWidgetOneHandInfo.m_Name == InputManager.ControllerName.Brush) ? + m_GrabWand.eatInput : m_GrabBrush.eatInput; + + // See if the other controller decides to grab the widget (unless we're pinned). + if (!m_CurrentGrabWidget.Pinned) + { + if (m_CurrentGrabWidget.AllowTwoHandGrab) + { + if (InputManager.Controllers[(int)otherName].GetControllerGrip()) + { + RequestPanelsVisibility(false); + m_GrabWidgetState = GrabWidgetState.TwoHands; + // Figure out if the new grab starts inside the widget. + Vector3 vOtherGrabPos = TrTransform.FromTransform( + InputManager.m_Instance.GetController(otherName)).translation; + bool bOtherGrabInBounds = m_CurrentGrabWidget.GetActivationScore( + vOtherGrabPos, otherName) >= 0; + m_CurrentGrabWidget.SetUserTwoHandGrabbing( + true, m_GrabWidgetOneHandInfo.m_Name, otherName, bOtherGrabInBounds); + + if (otherName == InputManager.ControllerName.Brush) + { + m_GrabBrush.startedGrabInsideWidget = bOtherGrabInBounds; + } + else + { + m_GrabWand.startedGrabInsideWidget = bOtherGrabInBounds; + } + + m_GrabWidgetTwoHandBrushPrev = TrTransform.FromTransform( + InputManager.m_Instance.GetController(InputManager.ControllerName.Brush)); + m_GrabWidgetTwoHandWandPrev = TrTransform.FromTransform( + InputManager.m_Instance.GetController(InputManager.ControllerName.Wand)); + } + } + } + else if (!otherInputEaten && InputManager.Controllers[(int)otherName].GetControllerGrip()) + { + // If it's a two hand grab but the current grab widget is pinned, grab the world. + UpdateGrab_ToNone(m_CurrentGrabWidget); + m_CurrentGrabWidget = null; + m_ControllerGrabVisuals.SetDesiredVisualState(ControllerGrabVisuals.VisualState.Off); + } + } + } + + // Previous frame was a two-handed grab. + // Handles all the cases where this frame's grab is zero, one, or two hands. + void UpdateGrab_WasTwoHands(GrabWidget rPrevGrabWidget) + { + //keep holding on to our widget + m_CurrentGrabWidget = rPrevGrabWidget; + m_CurrentGrabWidget.Activate(true); + m_CurrentGrabWidget.UserInteracting(true, m_GrabWidgetOneHandInfo.m_Name); + + if (!App.Instance.IsInStateThatAllowsAnyGrabbing()) + { + m_CurrentGrabWidget.SetUserTwoHandGrabbing(false); + UpdateGrab_ToNone(rPrevGrabWidget); + } + else if (!InputManager.Wand.GetControllerGrip()) + { // Look for button release. + m_CurrentGrabWidget.SetUserTwoHandGrabbing(false); + // See if our Brush hand is still within grab range of the widget. + if (m_GrabBrush.startedGrabInsideWidget || + IsControllerNearWidget(InputManager.ControllerName.Brush, m_CurrentGrabWidget)) + { + m_GrabWidgetOneHandInfo.m_Name = InputManager.ControllerName.Brush; + RequestPanelsVisibility(true); + InitializeGrabWidgetControllerInfo(m_GrabWidgetOneHandInfo); + m_GrabWidgetState = GrabWidgetState.OneHand; + } + else + { + // If the Brush hand is beyond the widget, we're not holding it anymore. + UpdateGrab_ToNone(rPrevGrabWidget); + + // Eat input on the brush grip until we release the button. + m_GrabBrush.eatInput = true; + } + } + else if (!InputManager.Brush.GetControllerGrip()) + { + m_CurrentGrabWidget.SetUserTwoHandGrabbing(false); + if (m_GrabWand.startedGrabInsideWidget || + IsControllerNearWidget(InputManager.ControllerName.Wand, m_CurrentGrabWidget)) + { + m_GrabWidgetOneHandInfo.m_Name = InputManager.ControllerName.Wand; + InitializeGrabWidgetControllerInfo(m_GrabWidgetOneHandInfo); + m_GrabWidgetState = GrabWidgetState.OneHand; + } + else + { + UpdateGrab_ToNone(rPrevGrabWidget); + m_GrabWand.eatInput = true; + } + } + else + { + // Both hands still grabbing. + // Check for pin, which forcibly releases one of the hands. + if (m_CurrentGrabWidget.AllowPinning && + InputManager.Controllers[(int)m_GrabWidgetOneHandInfo.m_Name].GetCommandDown( + InputManager.SketchCommands.PinWidget)) + { + // If the user initiates a pin action, buzz a bit. + if (!m_CurrentGrabWidget.Pinned) + { + InputManager.m_Instance.TriggerHapticsPulse( + m_GrabWidgetOneHandInfo.m_Name, 3, 0.10f, 0.07f); + } + + m_CurrentGrabWidget.Pin(!m_CurrentGrabWidget.Pinned); + SketchSurfacePanel.m_Instance.EatToolsInput(); + m_WidgetManager.RefreshPinAndUnpinLists(); + + InitializeGrabWidgetControllerInfo(m_GrabWidgetOneHandInfo); + m_GrabWidgetState = GrabWidgetState.OneHand; + m_CurrentGrabWidget.SetUserTwoHandGrabbing(false); + + // Eat input on the off hand so we don't immediately jump in to world transform. + if (m_GrabWidgetOneHandInfo.m_Name == InputManager.ControllerName.Brush) + { + RequestPanelsVisibility(true); + m_GrabWand.eatInput = true; + } + else + { + m_GrabBrush.eatInput = true; + } + } + + if (!m_CurrentGrabWidget.Pinned) + { + UpdateGrab_ContinuesTwoHands(); + } + } + ClearGrabWidgetHoldHistory(); + m_PanelManager.DoCollisionSimulationForWidgetPanels(); + } + + // Common case for two-handed grab: both the previous and current frames are two-handed. + private void UpdateGrab_ContinuesTwoHands() + { + //holding with two hands, transform accordingly + TrTransform xfBrush = TrTransform.FromTransform(InputManager.Brush.Transform); + TrTransform xfWand = TrTransform.FromTransform(InputManager.Wand.Transform); + Vector2 vSizeRange = m_CurrentGrabWidget.GetWidgetSizeRange(); + + GrabWidget.Axis axis = m_CurrentGrabWidget.GetScaleAxis( + xfWand.translation, xfBrush.translation, + out Vector3 axisDirection, out float axisExtent); + + TrTransform newWidgetXf; + if (axis != GrabWidget.Axis.Invalid) + { + // Scale along a single axis + float deltaScale; + if (App.Config.m_AxisManipulationIsResize) + { + newWidgetXf = MathUtils.TwoPointObjectTransformationAxisResize( + axisDirection, axisExtent, + m_GrabWidgetTwoHandWandPrev, m_GrabWidgetTwoHandBrushPrev, + xfWand, xfBrush, + GetWorkingTransform(m_CurrentGrabWidget), + out deltaScale, + deltaScaleMin: vSizeRange.x / axisExtent, + deltaScaleMax: vSizeRange.y / axisExtent); + } + else + { + newWidgetXf = MathUtils.TwoPointObjectTransformationNonUniformScale( + axisDirection, + m_GrabWidgetTwoHandWandPrev, m_GrabWidgetTwoHandBrushPrev, + xfWand, xfBrush, + GetWorkingTransform(m_CurrentGrabWidget), + out deltaScale, + finalScaleMin: vSizeRange.x, + deltaScaleMin: vSizeRange.x / axisExtent, + deltaScaleMax: vSizeRange.y / axisExtent); + } + + // The above functions return undefined values in newWidgetXf.scale; but that's + // okay because RecordAndSetPosRot ignores xf.scale. + // TODO: do this more cleanly + m_CurrentGrabWidget.RecordAndApplyScaleToAxis(deltaScale, axis); + } + else + { + // Uniform scaling + TrTransform xfObject = GetWorkingTransform(m_CurrentGrabWidget); + Vector3 extents = (m_CurrentGrabWidget is StencilWidget) + ? (m_CurrentGrabWidget as StencilWidget).Extents + : Vector3.one * Mathf.Abs(m_CurrentGrabWidget.GetSignedWidgetSize()); + + // Delta-scale bounds should be based on the smallest/largest extent. + // Irritatingly, the API wants absolute rather than relative scale bounds, + // so they need even more conversion. + float deltaScaleMin = vSizeRange.x / extents.Min(); + float deltaScaleMax = vSizeRange.y / extents.Max(); + if (m_GrabWand.startedGrabInsideWidget && m_GrabBrush.startedGrabInsideWidget) + { + newWidgetXf = MathUtils.TwoPointObjectTransformation( + m_GrabWidgetTwoHandWandPrev, m_GrabWidgetTwoHandBrushPrev, + xfWand, xfBrush, + xfObject, + deltaScaleMin: deltaScaleMin, deltaScaleMax: deltaScaleMax); + } + else if (m_GrabWand.startedGrabInsideWidget) + { + // keep the wand inside the object + newWidgetXf = MathUtils.TwoPointObjectTransformation( + m_GrabWidgetTwoHandWandPrev, m_GrabWidgetTwoHandBrushPrev, + xfWand, xfBrush, + xfObject, + deltaScaleMin: deltaScaleMin, deltaScaleMax: deltaScaleMax, + bUseLeftAsPivot: true); + } + else + { + // keep the brush inside the object (note the brush is the left hand) + newWidgetXf = MathUtils.TwoPointObjectTransformation( + m_GrabWidgetTwoHandBrushPrev, m_GrabWidgetTwoHandWandPrev, + xfBrush, xfWand, + xfObject, + deltaScaleMin: deltaScaleMin, deltaScaleMax: deltaScaleMax, + bUseLeftAsPivot: true); + } + + // Must do separately becvause RecordAndSetPosRot ignores newWidgetXf.scale + m_CurrentGrabWidget.RecordAndSetSize(newWidgetXf.scale); + + float currentSize = Mathf.Abs(m_CurrentGrabWidget.GetSignedWidgetSize()); + if (currentSize == vSizeRange.x || currentSize == vSizeRange.y) + { + InputManager.m_Instance.TriggerHaptics(InputManager.ControllerName.Brush, 0.05f); + InputManager.m_Instance.TriggerHaptics(InputManager.ControllerName.Wand, 0.05f); + } + } + + // Ignores TrTransform.scale + m_CurrentGrabWidget.RecordAndSetPosRot(newWidgetXf); + + m_GrabWidgetTwoHandBrushPrev = xfBrush; + m_GrabWidgetTwoHandWandPrev = xfWand; + } + + void UpdateGrab_NoneToOne(InputManager.ControllerName controllerName) + { + if (m_MaybeDriftingGrabWidget != null && + m_MaybeDriftingGrabWidget.IsMoving() && + !m_MaybeDriftingGrabWidget.IsSpinningFreely) + { + // If a new widget is grabbed but the previous one is still drifting, end the drift. + // TODO: Simplify in the widget animation cleanup. + if (m_MaybeDriftingGrabWidget == m_CurrentGrabWidget) + { + SketchMemoryScript.m_Instance.PerformAndRecordCommand( + new MoveWidgetCommand(m_MaybeDriftingGrabWidget, + m_MaybeDriftingGrabWidget.LocalTransform, m_MaybeDriftingGrabWidget.CustomDimension, + final: true), + discardIfNotMerged: true); + } + m_MaybeDriftingGrabWidget.ClearVelocities(); + } + + // UserInteracting should be the first thing that happens here so OnUserBeginInteracting can + // be called before everything else. + m_CurrentGrabWidget.UserInteracting(true, controllerName); + m_CurrentGrabWidget.ClearVelocities(); + ClearGrabWidgetHoldHistory(); + + //set our info names according to this controller's name + m_GrabWidgetOneHandInfo.m_Name = controllerName; + InitializeGrabWidgetControllerInfo(m_GrabWidgetOneHandInfo); + + PointerManager.m_Instance.AllowPointerPreviewLine(false); + PointerManager.m_Instance.RequestPointerRendering(false); + m_SketchSurfacePanel.RequestHideActiveTool(true); + if (m_GrabWidgetOneHandInfo.m_Name == InputManager.ControllerName.Wand) + { + RequestPanelsVisibility(false); + } + + // Notify visuals. + ControllerGrabVisuals.VisualState visualState = + m_GrabWidgetOneHandInfo.m_Name == InputManager.ControllerName.Brush ? + ControllerGrabVisuals.VisualState.WidgetBrushGrip : + ControllerGrabVisuals.VisualState.WidgetWandGrip; + m_ControllerGrabVisuals.SetDesiredVisualState(visualState); + m_ControllerGrabVisuals.SetHeldWidget(m_CurrentGrabWidget.transform); + + //if a gaze object had focus when we grabbed this widget, take focus off the object + ResetActivePanel(); + m_UIReticle.SetActive(false); + + // Prep all other grab widgets for collision. + m_PanelManager.PrimeCollisionSimForWidgets(m_CurrentGrabWidget); + + m_GrabWidgetState = GrabWidgetState.OneHand; + m_WidgetManager.WidgetsDormant = false; + PointerManager.m_Instance.EatLineEnabledInput(); + + m_BackupWandGrabData = null; + m_BackupBrushGrabData = null; + } + + void UpdateGrab_ToNone(GrabWidget rPrevGrabWidget) + { + m_MaybeDriftingGrabWidget = rPrevGrabWidget; + + m_GrabWidgetState = GrabWidgetState.None; + PointerManager.m_Instance.RequestPointerRendering(!App.Instance.IsLoading() && + m_SketchSurfacePanel.ShouldShowPointer()); + RequestPanelsVisibility(true); + m_SketchSurfacePanel.RequestHideActiveTool(false); + rPrevGrabWidget.UserInteracting(false); + + // Disable grab visuals. + m_ControllerGrabVisuals.SetDesiredVisualState(ControllerGrabVisuals.VisualState.Off); + m_ControllerGrabVisuals.SetHeldWidget(null); + + if (m_GrabBrush.grabbingGroup || m_GrabWand.grabbingGroup) + { + SelectionManager.m_Instance.EndGrabbingGroupWithWidget(); + m_GrabBrush.grabbingGroup = false; + m_GrabWand.grabbingGroup = false; + } + } + + void RequestWidgetIntersection(List candidates, + InputManager.ControllerName controllerName) + { + // Get locals based off what controller we're using. + Queue resultQueue = null; + Vector3 controllerPos = Vector3.zero; + if (controllerName == InputManager.ControllerName.Brush) + { + resultQueue = m_BrushResults; + controllerPos = InputManager.m_Instance.GetBrushControllerAttachPoint().position; + } + else + { + resultQueue = m_WandResults; + controllerPos = InputManager.m_Instance.GetWandControllerAttachPoint().position; + } + + // If we don't have a candidate that has a GPU object, don't bother firing off a GPU request. + bool requestGpuIntersection = false; + + // Fire off a new GPU intersection with all widgets that can use it. + for (int i = 0; i < candidates.Count; ++i) + { + if (candidates[i].m_WidgetScript.HasGPUIntersectionObject()) + { + candidates[i].m_WidgetScript.SetGPUIntersectionObjectLayer(m_WidgetGpuIntersectionLayer); + requestGpuIntersection = true; + } + } + + if (requestGpuIntersection) + { + GpuIntersectionResult newRequest = new GpuIntersectionResult(); + newRequest.resultList = new List(); + newRequest.result = App.Instance.GpuIntersector.RequestModelIntersections( + controllerPos, m_WidgetGpuIntersectionRadius, newRequest.resultList, 8, + (1 << m_WidgetGpuIntersectionLayer)); + + // The new result will only be null when the intersector is disabled. + if (newRequest.result != null) + { + resultQueue.Enqueue(newRequest); + } + + for (int i = 0; i < candidates.Count; ++i) + { + if (candidates[i].m_WidgetScript.HasGPUIntersectionObject()) + { + candidates[i].m_WidgetScript.RestoreGPUIntersectionObjectLayer(); + } + } + } + } + + GrabWidgetData GetBestWidget(List candidates, + Queue resultQueue) + { + // Discard futures that are too old. + while (resultQueue.Count > 0) + { + if (Time.frameCount - resultQueue.Peek().result.StartFrame < 5) + { + break; + } + resultQueue.Dequeue(); + } + + // If the oldest future is ready, use its intersection result to update the candidates. + GpuIntersectionResult finishedResult; + if (resultQueue.Count > 0 && resultQueue.Peek().result.IsReady) + { + finishedResult = resultQueue.Dequeue(); + } + else + { + finishedResult.resultList = new List(); + } + + // TODO: Speed this up. + for (int i = 0; i < candidates.Count; ++i) + { + if (candidates[i].m_WidgetScript.HasGPUIntersectionObject()) + { + // If a candidate can't find itself in the finished results list, it's not eligible. + bool candidateValid = false; + for (int j = 0; j < finishedResult.resultList.Count; ++j) + { + if (candidates[i].m_WidgetScript.Equals(finishedResult.resultList[j].widget)) + { + candidateValid = true; + break; + } + } + + if (candidateValid) + { + // If a candidate has a GPU intersection object and we found it in this list, + // not only is it valid, but it's as valid as it can be. + candidates[i].m_ControllerScore = 1.0f; + } + else + { + candidates[i].m_NearController = false; + } + } + } + + // Run through the candidates and pick + GrabWidgetData best = null; + for (int i = 0; i < candidates.Count; ++i) + { + var candidate = candidates[i]; + if (!candidate.m_NearController) continue; + + // For media widgets - only select from the active layer + if (candidate.m_WidgetScript is MediaWidget + && candidate.m_WidgetScript.Canvas != App.Scene.ActiveCanvas) continue; + + if (best == null || candidate.m_ControllerScore > best.m_ControllerScore) + { + best = candidate; + } + } + return best; + } + + void InitializeGrabWidgetControllerInfo(GrabWidgetControllerInfo info) + { + Transform controller = InputManager.Controllers[(int)info.m_Name].Transform; + Transform widget = m_CurrentGrabWidget.GrabTransform_GS; + TrTransform newWidgetXf = Coords.AsGlobal[widget]; + + info.m_BaseControllerXf = Coords.AsGlobal[controller]; + info.m_BaseWidgetXf_LS = info.m_BaseControllerXf.inverse * newWidgetXf; + } + + // returns the transform of the true widget (not the snapped one for those that can be) + private TrTransform GetWorkingTransform(GrabWidget w) + { + TrTransform ret = w.GetGrabbedTrTransform(); + ret.scale = w.GetSignedWidgetSize(); + return ret; + } + + // Initiate the world transform reset animation. + public void RequestWorldTransformReset(bool toSavedXf = false) + { + if (WorldIsReset(toSavedXf)) + { + return; + } + + m_WorldTransformResetXf = + toSavedXf ? SketchMemoryScript.m_Instance.InitialSketchTransform : TrTransform.identity; + m_WorldTransformResetState = WorldTransformResetState.Requested; + + App.Scene.disableTiltProtection = false; + } + + void UpdateWorldTransformReset() + { + switch (m_WorldTransformResetState) + { + case WorldTransformResetState.Requested: + ViewpointScript.m_Instance.FadeToColor(Color.black, m_GrabWorldFadeSpeed); + m_WorldTransformResetState = WorldTransformResetState.FadingToBlack; + m_xfDropCamReset_RS = Coords.AsRoom[m_DropCam.transform]; + PointerManager.m_Instance.EatLineEnabledInput(); + PointerManager.m_Instance.AllowPointerPreviewLine(false); + break; + case WorldTransformResetState.FadingToBlack: + m_WorldTransformFadeAmount += m_GrabWorldFadeSpeed * Time.deltaTime; + if (m_WorldTransformFadeAmount >= 1.0f) + { + App.Scene.Pose = m_WorldTransformResetXf; + m_WorldTransformFadeAmount = 1.0f; + m_WorldTransformResetState = WorldTransformResetState.FadingToScene; + ViewpointScript.m_Instance.FadeToScene(m_GrabWorldFadeSpeed); + m_DropCam.transform.position = m_xfDropCamReset_RS.translation; + m_DropCam.transform.rotation = m_xfDropCamReset_RS.rotation; + PointerManager.m_Instance.AllowPointerPreviewLine(true); + } + break; + case WorldTransformResetState.FadingToScene: + m_WorldTransformFadeAmount -= m_GrabWorldFadeSpeed * Time.deltaTime; + if (m_WorldTransformFadeAmount <= 0.0f) + { + m_WorldTransformFadeAmount = 0.0f; + m_WorldTransformResetState = WorldTransformResetState.Default; + } + break; + } + } + + bool CheckToggleTiltProtection() + { + if ( + InputManager.Wand.GetCommandDown(InputManager.SketchCommands.Redo) || + InputManager.Brush.GetCommandDown(InputManager.SketchCommands.Redo) + ) + { + App.Scene.disableTiltProtection = !App.Scene.disableTiltProtection; + + return !App.Scene.disableTiltProtection; + } + + return false; + + } + + void UpdateGrab_World() + { + bool bAllowWorldTransform = m_SketchSurfacePanel.ActiveTool.AllowWorldTransformation() && + (m_GrabWorldState != GrabWorldState.ResetDone) && + (!PointerManager.m_Instance.IsMainPointerCreatingStroke() || App.Instance.IsLoading()) && + App.Instance.IsInStateThatAllowsAnyGrabbing() && + !m_DisableWorldGrabbing; + + bool bWorldGrabWandPrev = m_GrabWand.grabbingWorld; + bool bWorldGrabBrushPrev = m_GrabBrush.grabbingWorld; + m_GrabWand.grabbingWorld = bAllowWorldTransform && !m_GrabWand.eatInput && + InputManager.Wand.GetControllerGrip(); + m_GrabBrush.grabbingWorld = bAllowWorldTransform && !m_GrabBrush.eatInput && + InputManager.Brush.GetControllerGrip() && + (m_CurrentGazeObject == -1); + + bool grabsChanged = (bWorldGrabWandPrev != m_GrabWand.grabbingWorld) || + (bWorldGrabBrushPrev != m_GrabBrush.grabbingWorld); + bool bAllowWorldTransformChanged = + bAllowWorldTransform != m_AllowWorldTransformLastFrame; + int nGrabs = m_GrabWand.grabbingWorld ? 1 : 0; + nGrabs += m_GrabBrush.grabbingWorld ? 1 : 0; + + // Allow grabbing again if grabs have changed and we're done resetting. + if (m_GrabWorldState == GrabWorldState.ResetDone && grabsChanged) + { + m_GrabWorldState = GrabWorldState.Normal; + } + + // Update panels visibility if brush grip has changed. + if (bWorldGrabWandPrev != m_GrabWand.grabbingWorld) + { + RequestPanelsVisibility(!m_GrabWand.grabbingWorld); + } + + // Update tool visibility if brush grip has changed. + if (bWorldGrabBrushPrev != m_GrabBrush.grabbingWorld) + { + m_SketchSurfacePanel.RequestHideActiveTool(m_GrabBrush.grabbingWorld); + PointerManager.m_Instance.AllowPointerPreviewLine(!m_GrabBrush.grabbingWorld); + PointerManager.m_Instance.RequestPointerRendering(!m_GrabBrush.grabbingWorld + && m_SketchSurfacePanel.ShouldShowPointer() && !App.Instance.IsLoading()); + } + + // Reset m_WorldBeingGrabbed and only set it when world is actually being grabbed. + bool bWorldBeingGrabbedPrev = m_WorldBeingGrabbed; + m_WorldBeingGrabbed = false; + + // Move the world if it has been grabbed. + if (m_GrabWorldState == GrabWorldState.Normal && bAllowWorldTransform) + { + if (nGrabs == 2) + { + // Two-handed world movement. + m_WorldBeingGrabbed = true; + TrTransform grabXfWand = TrTransform.FromTransform( + InputManager.m_Instance.GetController(InputManager.ControllerName.Wand)); + TrTransform grabXfBrush = TrTransform.FromTransform( + InputManager.m_Instance.GetController(InputManager.ControllerName.Brush)); + + // Offset the controller positions so that they're centered on the grips. + Vector3 gripPos = InputManager.Controllers[(int)InputManager.ControllerName.Brush].Geometry.GripAttachPoint.localPosition; + gripPos.x = 0.0f; + grabXfWand.translation += grabXfWand.MultiplyVector(gripPos); + grabXfBrush.translation += grabXfBrush.MultiplyVector(gripPos); + + // Are we initiating two hand transform this frame? + if (!bWorldGrabWandPrev || !bWorldGrabBrushPrev) + { + PointerManager.m_Instance.EnableLine(false); + PointerManager.m_Instance.AllowPointerPreviewLine(false); + PointerManager.m_Instance.RequestPointerRendering(false); + // Initiate audio loop + m_WorldTransformSpeedSmoothed = 0.0f; + AudioManager.m_Instance.WorldGrabLoop(true); + } + else + { + TrTransform xfOld = GrabbedPose; + TrTransform xfNew; + float deltaScaleMin = WorldTransformMinScale / xfOld.scale; + float deltaScaleMax = WorldTransformMaxScale / xfOld.scale; + bool fixOffset = false; + fixOffset = CheckToggleTiltProtection(); + xfNew = MathUtils.TwoPointObjectTransformation( + m_GrabBrush.grabTransform, m_GrabWand.grabTransform, + grabXfBrush, grabXfWand, + xfOld, + rotationAxisConstraint: (App.Scene.disableTiltProtection ? default(Vector3) : Vector3.up), + deltaScaleMin: deltaScaleMin, deltaScaleMax: deltaScaleMax); + float fCurrentWorldTransformSpeed = + Mathf.Abs((xfNew.scale - xfOld.scale) / Time.deltaTime); + m_WorldTransformSpeedSmoothed = + Mathf.Lerp(m_WorldTransformSpeedSmoothed, fCurrentWorldTransformSpeed, + AudioManager.m_Instance.m_WorldGrabLoopSmoothSpeed * Time.deltaTime); + AudioManager.m_Instance.ChangeLoopVolume("WorldGrab", + Mathf.Clamp(m_WorldTransformSpeedSmoothed / + AudioManager.m_Instance.m_WorldGrabLoopAttenuation, 0f, + AudioManager.m_Instance.m_WorldGrabLoopMaxVolume)); + + if (fixOffset) + { + Vector3 midPoint = Vector3.Lerp(grabXfBrush.translation, grabXfWand.translation, 0.5f); + + Vector3 localMidPointOldXF = xfOld.inverse * midPoint; + + // assign this to force the axial protection + GrabbedPose = xfNew; + xfNew = GrabbedPose; + + Vector3 midPointXFNew = xfNew * localMidPointOldXF; + + TrTransform xfDelta1 = TrTransform.T(midPoint - midPointXFNew); + xfNew = xfDelta1 * xfNew; + } + GrabbedPose = xfNew; + } + + // Update last states. + m_GrabBrush.grabTransform = grabXfBrush; + m_GrabWand.grabTransform = grabXfWand; + } + } + else if (m_GrabWorldState == GrabWorldState.ResettingTransform) + { + if (m_WorldTransformResetState == WorldTransformResetState.FadingToScene) + { + ResetGrabbedPose(); + PanelManager.m_Instance.ExecuteOnPanel(x => x.OnPanelMoved()); + + // World can't be transformed right after a reset until grab states have changed. + if (bAllowWorldTransform) + { + bAllowWorldTransform = false; + bAllowWorldTransformChanged = + bAllowWorldTransform != m_AllowWorldTransformLastFrame; + } + + // Set the grab world state on exit. + if (nGrabs == 0) + { + m_GrabWorldState = GrabWorldState.Normal; + } + else + { + m_GrabWorldState = GrabWorldState.ResetDone; + } + } + } + + if (grabsChanged || bAllowWorldTransformChanged) + { + // Fade in grid when doing two handed spin. + if (nGrabs == 2 && !bAllowWorldTransformChanged) + { + ViewpointScript.m_Instance.FadeGroundPlaneIn(m_GrabWorldGridColor, m_GrabWorldFadeSpeed); + } + else + { + ViewpointScript.m_Instance.FadeGroundPlaneOut(m_GrabWorldFadeSpeed); + } + } + + // Update visuals for world transform + if (grabsChanged) + { + bool bDoubleGrip = m_GrabBrush.grabbingWorld && m_GrabWand.grabbingWorld; + bool bSingleGrip = m_GrabBrush.grabbingWorld || m_GrabWand.grabbingWorld; + Vector3 vControllersMidpoint = + (InputManager.m_Instance.GetControllerPosition(InputManager.ControllerName.Brush) + + InputManager.m_Instance.GetControllerPosition(InputManager.ControllerName.Wand)) * 0.5f; + + // Update transform line visuals + if (bDoubleGrip) + { + m_ControllerGrabVisuals.SetDesiredVisualState(ControllerGrabVisuals.VisualState.WorldDoubleGrip); + AudioManager.m_Instance.WorldGrabbed(vControllersMidpoint); + } + else if (bSingleGrip) + { + if (m_GrabWand.grabbingWorld) + { + m_ControllerGrabVisuals.SetDesiredVisualState(ControllerGrabVisuals.VisualState.WorldWandGrip); + } + else + { + m_ControllerGrabVisuals.SetDesiredVisualState(ControllerGrabVisuals.VisualState.WorldBrushGrip); + } + + if (!bWorldGrabWandPrev && !bWorldGrabBrushPrev) + { + AudioManager.m_Instance.WorldGrabbed(vControllersMidpoint); + } + else + { + AudioManager.m_Instance.WorldGrabLoop(false); + } + } + else + { + m_ControllerGrabVisuals.SetDesiredVisualState(ControllerGrabVisuals.VisualState.Off); + AudioManager.m_Instance.WorldGrabLoop(false); + } + + if (m_GrabWand.grabbingWorld || m_GrabBrush.grabbingWorld) + { + m_WidgetManager.WidgetsDormant = false; + PointerManager.m_Instance.EatLineEnabledInput(); + } + } + + // Reset scene transform if we're gripping and press the track pad. + bool wandReset = m_GrabWand.grabbingWorld && + InputManager.Wand.GetCommandDown(InputManager.SketchCommands.WorldTransformReset); + bool brushReset = m_GrabBrush.grabbingWorld && + InputManager.Brush.GetCommandDown(InputManager.SketchCommands.WorldTransformReset); + if ((wandReset || brushReset) && !WorldIsReset(toSavedXf: false)) + { + m_GrabBrush.eatInput = true; + m_GrabWand.eatInput = true; + m_EatToolScaleInput = true; + m_GrabWorldState = GrabWorldState.ResettingTransform; + RequestWorldTransformReset(); + AudioManager.m_Instance.PlayTransformResetSound(); + } + + // Update the skybox rotation with the new scene rotation. + if (RenderSettings.skybox) + { + Quaternion sceneQuaternion = App.Instance.m_SceneTransform.rotation; + RenderSettings.skybox.SetVector( + "_SkyboxRotation", + new Vector4(sceneQuaternion.x, sceneQuaternion.y, sceneQuaternion.z, sceneQuaternion.w)); + } + + // Update last frame members. + m_AllowWorldTransformLastFrame = bAllowWorldTransform; + } + + /// If lhs and rhs are overlapping, return the smallest vector that would + /// cause rhs to stop overlapping; otherwise, return 0. + /// lhs: an antisphere (solid outside, empty inside) + /// rhs: a sphere (empty outside, solid inside) + private static Vector3 GetOverlap_Antisphere_Sphere( + Vector3 lhsCenter, float lhsRadius, + Vector3 rhsCenter, float rhsRadius) + { + // If anyone passes negative values, they are a bad person + lhsRadius = Mathf.Abs(lhsRadius); + rhsRadius = Mathf.Abs(rhsRadius); + // Without loss of generality, can recenter on lhs + rhsCenter -= lhsCenter; + lhsCenter -= lhsCenter; + + float maxDistance = lhsRadius - rhsRadius; + + // Edge case: sphere does not fit in antisphere + if (maxDistance <= 0) + { + return -rhsCenter; + } + + float penetrationDistance = Mathf.Max(0, rhsCenter.magnitude - maxDistance); + return -penetrationDistance * rhsCenter.normalized; + } + + public static bool IsValidScenePose(TrTransform xf, float radialBounds) + { + // Simple and dumb implementation for now. + return xf == MakeValidScenePose(xf, radialBounds); + } + + /// This is like MakeValidScenePose, but it guarantees that: + /// - The return value is a valid result of Lerp(scene0, scene1, t), + /// for some handwavy definition of "lerp" + /// - The lerp "t" is in [0, 1] + /// - IsValidScenePose(return value) is true, subject to the previous constraints. + /// + /// Think of it as doing a cast from scene0 to scene1. + public static TrTransform MakeValidSceneMove( + TrTransform scene0, TrTransform scene1, float radialBounds) + { + if (IsValidScenePose(scene1, radialBounds)) + { + return scene1; + } + if (!IsValidScenePose(scene0, radialBounds)) + { + Debug.LogError("Invalid scene cast start"); + return scene0; + } + + // We don't support lerping either of these + Debug.Assert(scene0.rotation == scene1.rotation); + Debug.Assert(scene0.scale == scene1.scale); + + Vector3 vRoom0 = -scene0.translation; + Vector3 vRoom1 = -scene1.translation; + float radius = (scene0.scale + * radialBounds + * App.METERS_TO_UNITS) - App.Instance.RoomRadius; + + float t0, t1; + bool success = MathUtils.RaySphereIntersection( + vRoom0, vRoom1 - vRoom0, + Vector3.zero, radius, out t0, out t1); + if (!success) + { + // If this were more important, we could solve for the t of the closest approach + return scene0; + } + + // t0 is expected to be < 0 (room starts inside the fence) + // t1 is expected to be in [0, 1] (room ends outside the fence) + + // Constraints: + // - Lerp t must be in [0, 1]. (Do not move past the requested endpoint) + // - Lerp t should be as high as possible but < t1. (Do not exit the sphere) + float t = Mathf.Clamp(t1, 0, 1); + + TrTransform sceneT = TrTransform.TRS( + Vector3.Lerp(scene0.translation, scene1.translation, t), + scene0.rotation, + scene0.scale); + return MakeValidScenePose(sceneT, radialBounds); + } + + /// Returns a new ScenePose TrTransform that does not cause the room + /// to violate the hard scene bounds. + /// + /// scenePose - The current, possibly invalid scene pose + public static TrTransform MakeValidScenePose(TrTransform scenePose, float radialBounds) + { + scenePose.scale = Mathf.Clamp( + scenePose.scale, + SketchControlsScript.m_Instance.WorldTransformMinScale, + SketchControlsScript.m_Instance.WorldTransformMaxScale); + + // Anything not explicitly qualified is in room space. + + float roomRadius = App.Instance.RoomRadius; + Vector3 roomCenter = Vector3.zero; + + float fenceRadius = scenePose.scale * radialBounds + * App.METERS_TO_UNITS; + Vector3 fenceCenter = scenePose.translation; + + Vector3 moveRoom = GetOverlap_Antisphere_Sphere( + fenceCenter, fenceRadius, roomCenter, roomRadius); + Vector3 moveFence = -moveRoom; + + scenePose.translation += moveFence; + return scenePose; + } + + /// Clears data used by GetGrabWidgetHoldHistory() + /// Should be called any time m_GrabWidgetOneHandInfo changes + void ClearGrabWidgetHoldHistory() + { + m_GrabWidgetHoldHistory.Clear(); + } + + /// Collects data for use with GetGrabWidgetHoldHistory() + void UpdateGrabWidgetHoldHistory(InputManager.ControllerName name) + { + float t = Time.realtimeSinceStartup; + var info = InputManager.Controllers[(int)name]; + m_GrabWidgetHoldHistory.Enqueue(new GrabWidgetHoldPoint + { + m_Name = name, + m_BirthTime = t, + m_Pos = info.Transform.position, + m_Rot = info.Transform.rotation + }); + + // Trim the fat off our widget history + while (m_GrabWidgetHoldHistory.Count > 0 && + t - m_GrabWidgetHoldHistory.Peek().m_BirthTime >= kControlPointHistoryMaxTime) + { + m_GrabWidgetHoldHistory.Dequeue(); + } + } + + /// Returns possibly-smoothed linear and angular velocities. May fail. + /// Angular velocity is returned as an axial vector whose length() is degrees/second + bool GetGrabWidgetHoldHistory(out Vector3 vLinearVelocity, out Vector3 vAngularVelocity) + { + vLinearVelocity = vAngularVelocity = Vector3.zero; + if (m_GrabWidgetHoldHistory.Count < 2) + { + return false; + } + + // We need pairs of elements, so a simple foreach() won't quite work. + // Maybe using linq .First() and .Skip() would be okay. + using (IEnumerator enumerator = m_GrabWidgetHoldHistory.GetEnumerator()) + { + if (!enumerator.MoveNext()) + { + return false; + } + + // Infinitesimal rotations commute, and scaled-axis-angle rotations commute + // "better" than other rotation formats. + Vector3 totalDeltaTheta = Vector3.zero; + + GrabWidgetHoldPoint first = enumerator.Current; + GrabWidgetHoldPoint prev = first; + GrabWidgetHoldPoint current = first; + while (enumerator.MoveNext()) + { + current = enumerator.Current; + + // For our quaternion, find the difference, convert it to angle/axis, and sum it + // Find delta such that delta * prev = cur + // left-multiply because we want it in world-space. + // multiply vs prev since we want the delta that takes us forward in time + // rather than backward in time. + Quaternion dtheta = current.m_Rot * Quaternion.Inverse(prev.m_Rot); + // Assume the rotation took the shorter path + if (dtheta.w < 0) + { + dtheta.Set(-dtheta.x, -dtheta.y, -dtheta.z, -dtheta.w); + } + + float degrees; + Vector3 axis; + dtheta.ToAngleAxis(out degrees, out axis); + totalDeltaTheta += (axis * degrees); + prev = current; + } + + // Linear velocity calculation doesn't need to look at intermediate points + Vector3 totalDeltaPosition = current.m_Pos - first.m_Pos; + float totalDeltaTime = current.m_BirthTime - first.m_BirthTime; + if (totalDeltaTime == 0) + { + return false; + } + + vLinearVelocity = totalDeltaPosition / totalDeltaTime; + vAngularVelocity = totalDeltaTheta / totalDeltaTime; + return true; + } + } + + bool IsControllerNearWidget(InputManager.ControllerName name, GrabWidget widget) + { + Vector3 vControllerPos = InputManager.m_Instance.GetControllerAttachPointPosition(name); + return widget.GetActivationScore(vControllerPos, name) >= 0.0f; + } + + void RefreshCurrentGazeObject() + { + UnityEngine.Profiling.Profiler.BeginSample("SketchControlScript.RefreshCurrentGazeObject"); + int iPrevGazeObject = m_CurrentGazeObject; + m_CurrentGazeObject = -1; + bool bGazeAllowed = (m_CurrentInputState == InputState.Standard) + && !InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Activate) + && !m_SketchSurfacePanel.ActiveTool.InputBlocked() + && (m_GrabWidgetState == GrabWidgetState.None) + && !m_GrabBrush.grabbingWorld + && !m_PinCushion.IsShowing() + && !PointerManager.MainPointerIsPainting() + ; + + bool bGazeDeactivationOverrideWithInput = false; + List aAllPanels = m_PanelManager.GetAllPanels(); + + bool hasController = m_ControlsType == ControlsType.SixDofControllers; + + //if we're re-positioning a panel, keep it active + if (m_PositioningPanelWithHead) + { + m_CurrentGazeObject = iPrevGazeObject; + } + // Only activate gaze objects if we're in standard input mode, and if we don't have the 'draw' + // button held. + else if ((bGazeAllowed || (iPrevGazeObject != -1))) + { + //reset hit flags + for (int i = 0; i < m_GazeResults.Length; ++i) + { + m_GazeResults[i].m_HitWithGaze = false; + m_GazeResults[i].m_HitWithController = false; + m_GazeResults[i].m_WithinView = false; + } + + // If we're in controller mode, find the nearest colliding widget that might get in our way. + float fNearestWidget = 99999.0f; + if (hasController) + { + fNearestWidget = m_WidgetManager.DistanceToNearestWidget(m_GazeControllerRay); + } + + //check all panels for gaze hit + bool bRequireVisibilityCheck = !hasController || (iPrevGazeObject == -1); + if (m_PanelManager.PanelsAreStable()) + { + RaycastHit rHitInfo; + bool bRayHit = false; + int panelsHit = 0; + for (int i = 0; i < aAllPanels.Count; ++i) + { + // Ignore fixed panels when they are not visible. + if (!m_PanelManager.GazePanelsAreVisible() && aAllPanels[i].m_Panel.m_Fixed) + { + continue; + } + + if (aAllPanels[i].m_Panel.gameObject.activeSelf && aAllPanels[i].m_Panel.IsAvailable()) + { + //make sure this b-snap is in view + Vector3 vToPanel = aAllPanels[i].m_Panel.transform.position - m_CurrentGazeRay.origin; + vToPanel.Normalize(); + if (!bRequireVisibilityCheck || Vector3.Angle(vToPanel, m_CurrentGazeRay.direction) < m_GazeMaxAngleFromFacing) + { + if (hasController) + { + if (aAllPanels[i].m_Panel.HasMeshCollider()) + { + //make sure the angle between the pointer and the panel forward is below our max angle + if (Vector3.Angle(aAllPanels[i].m_Panel.transform.forward, m_GazeControllerRay.direction) < m_GazeMaxAngleFromPointing) + { + //make sure the angle between the user-to-panel and the panel forward is reasonable + if (Vector3.Angle(aAllPanels[i].m_Panel.transform.forward, vToPanel) < m_GazeMaxAngleFacingToForward) + { + m_GazeResults[i].m_WithinView = true; + + bRayHit = false; + bRayHit = aAllPanels[i].m_Panel.RaycastAgainstMeshCollider( + m_GazeControllerRay, out rHitInfo, m_GazeControllerPointingDistance); + + if (bRayHit) + { + //if the ray starts inside the panel, we won't get a good hit point, it'll just be zero + if (rHitInfo.point.sqrMagnitude > 0.1f) + { + if (rHitInfo.distance < fNearestWidget) + { + m_GazeResults[i].m_ControllerDistance = rHitInfo.distance; + m_GazeResults[i].m_ControllerPosition = rHitInfo.point; + m_GazeResults[i].m_HitWithController = true; + panelsHit++; + } + } + } + } + } + } + } + else + { + m_GazeResults[i].m_WithinView = true; + if (aAllPanels[i].m_Panel.GetCollider().Raycast(m_CurrentGazeRay, out rHitInfo, m_GazeMaxDistance)) + { + m_GazeResults[i].m_GazePosition = rHitInfo.point; + m_GazeResults[i].m_HitWithGaze = true; + } + } + } + } + } + + // No panels hit within normal ray distance. + // Check if previous panel still pointed to. + if (panelsHit == 0) + { + if (iPrevGazeObject != -1) + { + // Don't allow any panel to hold focus if it's facing away from the user. + Vector3 vToPanel = aAllPanels[iPrevGazeObject].m_Panel.transform.position - + m_CurrentGazeRay.origin; + vToPanel.Normalize(); + if (Vector3.Angle(aAllPanels[iPrevGazeObject].m_Panel.transform.forward, vToPanel) < + m_GazeMaxAngleFacingToForward) + { + float fDist = m_GazeControllerPointingDistance * 1.5f; + bRayHit = aAllPanels[iPrevGazeObject].m_Panel.RaycastAgainstMeshCollider( + m_GazeControllerRayActivePanel, out rHitInfo, fDist); + if (bRayHit) + { + if (rHitInfo.point.sqrMagnitude > 0.1f) + { + if (rHitInfo.distance < fNearestWidget) + { + m_GazeResults[iPrevGazeObject].m_ControllerDistance = rHitInfo.distance; + m_GazeResults[iPrevGazeObject].m_ControllerPosition = rHitInfo.point; + m_GazeResults[iPrevGazeObject].m_HitWithController = true; + } + } + } + } + } + } + } + + //determine what panel we hit, take the one with the lowest controller distance + float fControllerDist = 999.0f; + int iControllerIndex = -1; + if (hasController) + { + for (int i = 0; i < m_GazeResults.Length; ++i) + { + if (m_GazeResults[i].m_HitWithController) + { + if (m_GazeResults[i].m_ControllerDistance < fControllerDist) + { + iControllerIndex = i; + fControllerDist = m_GazeResults[i].m_ControllerDistance; + } + } + } + } + + //if we found something near our controller, take it + if (iControllerIndex != -1) + { + m_CurrentGazeObject = iControllerIndex; + m_CurrentGazeHitPoint = m_GazeResults[iControllerIndex].m_ControllerPosition; + + // TODO: This should not be hardcoded once multiple pointers are allowed. + m_GazeResults[m_CurrentGazeObject].m_ControllerName = InputManager.ControllerName.Brush; + if (m_GazeResults[m_CurrentGazeObject].m_HitWithGaze) + { + //average with the gaze position if we hit that too + m_CurrentGazeHitPoint += m_GazeResults[m_CurrentGazeObject].m_GazePosition; + m_CurrentGazeHitPoint *= 0.5f; + } + } + else + { + //nothing near the controller, see if we're looking at the previous + if (iPrevGazeObject != -1 && m_GazeResults[iPrevGazeObject].m_HitWithGaze) + { + m_CurrentGazeObject = iPrevGazeObject; + m_CurrentGazeHitPoint = m_GazeResults[m_CurrentGazeObject].m_GazePosition; + } + else + { + //controller and gaze not near panel, pick the first panel we're looking at + for (int i = 0; i < m_GazeResults.Length; ++i) + { + if (m_GazeResults[i].m_HitWithGaze) + { + m_CurrentGazeObject = i; + m_CurrentGazeHitPoint = m_GazeResults[i].m_GazePosition; + break; + } + } + } + } + + //forcing users to look away from gaze panel + if (m_EatInputGazeObject && m_CurrentGazeObject != -1) + { + m_CurrentGazeObject = -1; + } + else if (m_CurrentGazeObject == -1) + { + m_EatInputGazeObject = false; + } + } + + //if we're staring at a panel, keep our countdown fresh + if (m_CurrentGazeObject != -1 || m_ForcePanelActivation) + { + m_GazePanelDectivationCountdown = m_GazePanelDectivationDelay; + } + else + { + if (InputManager.m_Instance.GetCommandDown(InputManager.SketchCommands.Activate)) + { + bGazeDeactivationOverrideWithInput = true; + m_GazePanelDectivationCountdown = 0.0f; + } + else + { + m_GazePanelDectivationCountdown -= Time.deltaTime; + } + if (m_GazePanelDectivationCountdown > 0.0f) + { + m_CurrentGazeObject = iPrevGazeObject; + } + } + + //update our positioning timer + if (m_PositioningPanelWithHead) + { + m_PositioningTimer += m_PositioningSpeed * Time.deltaTime; + m_PositioningTimer = Mathf.Min(m_PositioningTimer, 1.0f); + } + else + { + m_PositioningTimer -= m_PositioningSpeed * Time.deltaTime; + m_PositioningTimer = Mathf.Max(m_PositioningTimer, 0.0f); + } + + //prime objects if we change targets + if (iPrevGazeObject != m_CurrentGazeObject) + { + //if we're switching panels, make sure the pointer doesn't streak + PointerManager.m_Instance.DisablePointerPreviewLine(); + + if (iPrevGazeObject != -1) + { + aAllPanels[iPrevGazeObject].m_Panel.PanelGazeActive(false); + aAllPanels[iPrevGazeObject].m_Panel.SetPositioningPercent(0.0f); + } + if (m_CurrentGazeObject != -1) + { + //make sure our line is disabled + if (m_GazeResults[m_CurrentGazeObject].m_ControllerName == InputManager.ControllerName.Brush) + { + PointerManager.m_Instance.EnableLine(false); + PointerManager.m_Instance.AllowPointerPreviewLine(false); + } + + aAllPanels[m_CurrentGazeObject].m_Panel.PanelGazeActive(true); + aAllPanels[m_CurrentGazeObject].m_Panel.SetPositioningPercent(0.0f); + + if (m_GazeResults[m_CurrentGazeObject].m_ControllerName == InputManager.ControllerName.Brush) + { + m_SketchSurfacePanel.RequestHideActiveTool(true); + } + } + else + { + //if we don't have a panel, we need to enable the pointer according to the current tool + PointerManager.m_Instance.RefreshFreePaintPointerAngle(); + PointerManager.m_Instance.RequestPointerRendering(m_SketchSurfacePanel.ShouldShowPointer()); + m_UIReticle.SetActive(false); + m_SketchSurfacePanel.RequestHideActiveTool(false); + if (!bGazeDeactivationOverrideWithInput) + { + m_SketchSurfacePanel.EatToolsInput(); + } + } + + m_PositioningPanelWithHead = false; + } + UnityEngine.Profiling.Profiler.EndSample(); + } + + void UpdateActiveGazeObject() + { + BasePanel currentPanel = m_PanelManager.GetPanel(m_CurrentGazeObject); + currentPanel.SetPositioningPercent(m_PositioningTimer); + bool hasController = m_ControlsType == ControlsType.SixDofControllers; + // Update positioning behavior. + if (m_PositioningPanelWithHead) + { + if (!InputManager.m_Instance.GetCommand(InputManager.SketchCommands.LockToHead) && + !InputManager.m_Instance.GetCommand(InputManager.SketchCommands.LockToController)) + { + // No more positioning. + m_PositioningPanelWithHead = false; + m_PanelManager.m_SweetSpot.EnableBorderSphere(false, Vector3.zero, 0.0f); + currentPanel.PanelHasStoppedMoving(); + } + else + { + //lock the panel to the sweet spot bounds in the direction the user is looking + Quaternion qDiff = m_CurrentHeadOrientation * Quaternion.Inverse(m_PositioningPanelBaseHeadRotation); + Vector3 vAdjustedOffset = qDiff * m_PositioningPanelOffset; + + Vector3 vNewPos = m_PanelManager.m_SweetSpot.transform.position + vAdjustedOffset; + currentPanel.transform.position = vNewPos; + + vAdjustedOffset.Normalize(); + currentPanel.transform.forward = vAdjustedOffset; + + float fHighlightRadius = currentPanel.m_BorderSphereHighlightRadius; + m_PanelManager.m_SweetSpot.EnableBorderSphere(true, vNewPos, fHighlightRadius * m_PositioningTimer); + + //once we've moved this panel, run the simulation on the other panels to resolve collisions + m_PanelManager.DoCollisionSimulationForKeyboardMouse(currentPanel); + } + } + else + { + // It's possible that, on this frame, before this function was called, active gaze was pulled + // from this panel. In this case, we want to skip updating this frame. + // This happens when a panel has gaze and world grab dismisses all panels, for example. + if (currentPanel.IsActive()) + { + //orient to gaze + if (hasController) + { + currentPanel.UpdatePanel(m_GazeControllerRay.direction, m_CurrentGazeHitPoint); + } + else + { + currentPanel.UpdatePanel(m_CurrentGazeRay.direction, m_CurrentGazeHitPoint); + } + } + + if (!hasController) + { + //lock to head if we're holding a lock button.. + bool bLockToHead = InputManager.m_Instance.GetCommand(InputManager.SketchCommands.LockToHead) || + InputManager.m_Instance.GetCommand(InputManager.SketchCommands.LockToController); + + if (bLockToHead) + { + m_PositioningPanelWithHead = true; + m_PositioningPanelBaseHeadRotation = m_CurrentHeadOrientation; + m_PositioningPanelOffset = currentPanel.transform.position - + m_PanelManager.m_SweetSpot.transform.position; + + currentPanel.ResetPanelFlair(); + + //prime all other panels for movement + m_PanelManager.PrimeCollisionSimForKeyboardMouse(); + } + } + + PointerManager.m_Instance.RequestPointerRendering(false); + currentPanel.UpdateReticleOffset(m_MouseDeltaX, m_MouseDeltaY); + } + + // Keep reticle locked in the right spot. + Vector3 reticlePos = Vector3.zero; + Vector3 reticleForward = Vector3.zero; + if (hasController) + { + currentPanel.GetReticleTransformFromPosDir(m_CurrentGazeHitPoint, + m_GazeControllerRay.direction, out reticlePos, out reticleForward); + } + else + { + currentPanel.GetReticleTransform(out reticlePos, out reticleForward, + (m_ControlsType == ControlsType.ViewingOnly)); + } + + SetUIReticleTransform(reticlePos, -reticleForward); + m_UIReticle.SetActive(GetGazePanelActivationRatio() >= 1.0f); + } + + public void ResetActivePanel() + { + m_PanelManager.ResetPanel(m_CurrentGazeObject); + PointerManager.m_Instance.DisablePointerPreviewLine(); + m_PositioningPanelWithHead = false; + m_CurrentGazeObject = -1; + } + + void UpdatePanInput() + { + if (Mouse.current.rightButton.isPressed) + { + Vector3 vPanDiff = Vector3.zero; + vPanDiff += (Vector3.right * m_MouseDeltaXScaled); + vPanDiff += (Vector3.up * m_MouseDeltaYScaled); + Vector3 vSurfacePos = m_SketchSurface.transform.position; + m_SketchSurface.transform.position = vSurfacePos + vPanDiff; + } + else + { + float fCurrentTime = Time.realtimeSinceStartup; + if (fCurrentTime - m_PositionOffsetResetTapTime < m_DoubleTapWindow) + { + if (m_CurrentGazeObject == -1) + { + ResetGrabbedPose(); + } + } + m_PositionOffsetResetTapTime = fCurrentTime; + + SwitchState(InputState.Standard); + } + } + + void UpdateRotationInput() + { + if (InputManager.m_Instance.GetCommand(InputManager.SketchCommands.PivotRotation)) + { + bool bAltInputActive = InputManager.m_Instance.GetCommand(InputManager.SketchCommands.AltActivate); + bool bRollRotation = m_RotationRollActive || bAltInputActive || m_CurrentRotationType == RotationType.RollOnly; + m_RotationIcon.SetActive(bRollRotation); + if (bRollRotation) + { + m_RotationCursorOffset.x += m_MouseDeltaXScaled; + float fRotationAmount = m_RotationCursorOffset.x * -m_RotationRollScalar; + + Quaternion qOffsetRotation = Quaternion.AngleAxis(fRotationAmount, m_SurfaceForward); + Quaternion qNewRotation = qOffsetRotation * m_RotationOrigin; + m_SketchSurface.transform.rotation = qNewRotation; + + m_RotationRollActive = true; + m_RotationCursor.gameObject.SetActive(false); + } + else + { + //update offset with mouse movement + m_RotationCursorOffset.x += m_MouseDeltaXScaled; + m_RotationCursorOffset.y += m_MouseDeltaYScaled; + + //get offset in model space + Vector3 vSurfaceBounds = m_SketchSurface.transform.localScale * 0.5f; + m_RotationCursorOffset.x = Mathf.Clamp(m_RotationCursorOffset.x, -vSurfaceBounds.x, vSurfaceBounds.x); + m_RotationCursorOffset.y = Mathf.Clamp(m_RotationCursorOffset.y, -vSurfaceBounds.y, vSurfaceBounds.y); + float fCursorOffsetDist = m_RotationCursorOffset.magnitude; + float fMaxCursorOffsetDist = vSurfaceBounds.x; + + //transform offset in to world space + Vector3 vTransformedOffset = m_RotationOrigin * m_RotationCursorOffset; + vTransformedOffset.Normalize(); + + //get world space rotation axis + Vector3 vSketchSurfaceRotationAxis = Vector3.Cross(vTransformedOffset, m_SurfaceForward); + vSketchSurfaceRotationAxis.Normalize(); + + //amount to rotate is determined by offset distance from origin + float fSketchSurfaceRotationAngle = Mathf.Min(fCursorOffsetDist / fMaxCursorOffsetDist, 1.0f); + fSketchSurfaceRotationAngle *= m_RotationMaxAngle; + + //set new surface rotation by combining base rotation with angle/axis rotation + Quaternion qOffsetRotation = Quaternion.AngleAxis(fSketchSurfaceRotationAngle, vSketchSurfaceRotationAxis); + Quaternion qNewRotation = qOffsetRotation * m_RotationOrigin; + m_SketchSurface.transform.rotation = qNewRotation; + + //set position of rotation cursor + Vector3 vNewTransformedOffset = qNewRotation * m_RotationCursorOffset; + m_RotationCursor.transform.position = m_SketchSurface.transform.position + vNewTransformedOffset; + m_RotationCursor.transform.rotation = qNewRotation; + + //set position of guide lines + Vector2 vToCenter = m_RotationCursorOffset; + vToCenter.Normalize(); + float fOffsetAngle = Vector2.Angle(vToCenter, Vector2.up); + m_RotationCursor.PositionCursorLines(m_SketchSurface.transform.position, m_SketchSurface.transform.forward, fOffsetAngle, vSurfaceBounds.x * 2.0f); + } + } + else + { + float fCurrentTime = Time.realtimeSinceStartup; + if (fCurrentTime - m_RotationResetTapTime < m_DoubleTapWindow) + { + //reset drawing surface rotation + m_SketchSurface.transform.rotation = Quaternion.identity; + } + m_RotationResetTapTime = fCurrentTime; + + m_SurfaceForward = m_SketchSurface.transform.forward; + m_SurfaceRight = m_SketchSurface.transform.right; + m_SurfaceUp = m_SketchSurface.transform.up; + + if (!m_RotationRollActive && m_AutoOrientAfterRotation && m_SketchSurfacePanel.IsSketchSurfaceToolActive()) + { + //get possible auto rotations + Quaternion qQuatUp = OrientSketchSurfaceToUp(); + Quaternion qQuatForward = OrientSketchSurfaceToForward(); + + //get the angle between our current and desired auto-rotation + float toUpAngle = Quaternion.Angle(qQuatUp, m_SketchSurface.transform.rotation); + float toForwardAngle = Quaternion.Angle(qQuatForward, m_SketchSurface.transform.rotation); + + //set our new rotation to be whichever autorotation is closeset + Quaternion qNewRotation; + if (Mathf.Abs(toUpAngle) < Mathf.Abs(toForwardAngle)) + { + qNewRotation = qQuatUp; + } + else + { + qNewRotation = qQuatForward; + } + + //update the sketch surface + m_SketchSurface.transform.rotation = qNewRotation; + + m_SurfaceForward = m_SketchSurface.transform.forward; + m_SurfaceRight = m_SketchSurface.transform.right; + m_SurfaceUp = m_SketchSurface.transform.up; + } + + SwitchState(InputState.Standard); + } + } + + void UpdateHeadLockInput() + { + if (InputManager.m_Instance.GetCommand(InputManager.SketchCommands.LockToHead)) + { + //compute new position/orientation of sketch surface + Vector3 vTransformedOffset = m_CurrentHeadOrientation * m_SurfaceLockOffset; + Vector3 vSurfacePos = m_CurrentGazeRay.origin + vTransformedOffset; + + Quaternion qDiff = m_CurrentHeadOrientation * Quaternion.Inverse(m_SurfaceLockBaseHeadRotation); + Quaternion qNewSurfaceRot = qDiff * m_SurfaceLockBaseSurfaceRotation; + + m_SketchSurface.transform.position = vSurfacePos; + m_SketchSurface.transform.rotation = qNewSurfaceRot; + } + else + { + m_SurfaceForward = m_SketchSurface.transform.forward; + m_SurfaceRight = m_SketchSurface.transform.right; + m_SurfaceUp = m_SketchSurface.transform.up; + + SwitchState(InputState.Standard); + } + } + + void UpdateControllerLock() + { + if (InputManager.m_Instance.GetCommand(InputManager.SketchCommands.LockToController)) + { + //compute new position/orientation of sketch surface + Vector3 vControllerDiff = InputManager.m_Instance.GetControllerPosition(m_SurfaceLockActingController) - m_SurfaceLockBaseControllerPosition; + m_SketchSurface.transform.position = m_SurfaceLockBaseSurfacePosition + (vControllerDiff * m_SurfaceLockControllerScalar); + + Quaternion qDiff = InputManager.m_Instance.GetControllerRotation(m_SurfaceLockActingController) * Quaternion.Inverse(m_SurfaceLockBaseControllerRotation); + m_SketchSurface.transform.rotation = qDiff * m_SurfaceLockBaseSurfaceRotation; + } + else + { + m_SurfaceForward = m_SketchSurface.transform.forward; + m_SurfaceRight = m_SketchSurface.transform.right; + m_SurfaceUp = m_SketchSurface.transform.up; + + SwitchState(InputState.Standard); + } + } + + void UpdatePushPullInput() + { + bool bRotationActive = InputManager.m_Instance.GetCommand(InputManager.SketchCommands.PivotRotation); + bool bInputActive = InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Activate); + bool bAltInputActive = InputManager.m_Instance.GetCommand(InputManager.SketchCommands.AltActivate); + + if (bRotationActive && bInputActive) + { + SwitchState(InputState.Rotation); + } + else if (bAltInputActive) + { + Vector3 vPos = m_SketchSurface.transform.position; + float fBigDiff = Mathf.Abs(m_MouseDeltaXScaled) > Mathf.Abs(m_MouseDeltaYScaled) ? -m_MouseDeltaXScaled : m_MouseDeltaYScaled; + vPos += Vector3.forward * fBigDiff; + + m_SketchSurface.transform.position = vPos; + } + else + { + SwitchState(InputState.Standard); + } + } + + void UpdateSaveInput() + { + if (!InputManager.m_Instance.GetKeyboardShortcut(InputManager.KeyboardShortcut.Save)) + { + SwitchState(InputState.Standard); + } + } + + void UpdateLoadInput() + { + if (!InputManager.m_Instance.GetKeyboardShortcut(InputManager.KeyboardShortcut.Load)) + { + SwitchState(InputState.Standard); + } + } + + void OnBrushSetToDefault() + { + BrushDescriptor rDefaultBrush = BrushCatalog.m_Instance.DefaultBrush; + PointerManager.m_Instance.SetBrushForAllPointers(rDefaultBrush); + PointerManager.m_Instance.SetAllPointersBrushSize01(0.5f); + PointerManager.m_Instance.MarkAllBrushSizeUsed(); + } + + public void AssignControllerMaterials(InputManager.ControllerName controller) + { + ControllerGeometry geometry = InputManager.GetControllerGeometry(controller); + + // Start from a clean state + geometry.ResetAll(); + + // If the tutorial is enabled, override all materials. + if (TutorialManager.m_Instance.TutorialActive()) + { + InputManager.m_Instance + .GetControllerTutorial(controller) + ?.AssignControllerMaterials(controller); + return; + } + + // If we're grabbing the world, get the materials from the world transform panel. + if (m_GrabBrush.grabbingWorld && controller == InputManager.ControllerName.Brush) + { + TrTransform scenePose = App.Scene.Pose; + if (scenePose.scale != 1 || scenePose.translation != Vector3.zero + || scenePose.rotation != Quaternion.identity) + { + geometry.ShowWorldTransformReset(); + } + return; + } + else if (m_GrabWand.grabbingWorld && controller == InputManager.ControllerName.Wand) + { + TrTransform scenePose = App.Scene.Pose; + if (scenePose.scale != 1 || scenePose.translation != Vector3.zero + || scenePose.rotation != Quaternion.identity) + { + geometry.ShowWorldTransformReset(); + } + return; + } + + // Not grabbing the world, so see if we're grabbing a widget. + if (m_GrabWidgetState != GrabWidgetState.None) + { + m_CurrentGrabWidget.AssignControllerMaterials(controller); + return; + } + + // See if we're highlighting a widget and if that matters. + if (m_CurrentGrabWidget != null && m_CurrentGrabWidget.HasHoverInteractions()) + { + m_CurrentGrabWidget.AssignHoverControllerMaterials(controller); + return; + } + + // Not grabbing the world or a widget, see if we're interacting with a panel. + if (controller == InputManager.ControllerName.Brush && m_CurrentGazeObject != -1) + { + BasePanel panel = m_PanelManager.GetPanel(m_CurrentGazeObject); + panel.AssignControllerMaterials(controller); + return; + } + + // Defaults. + if (controller == InputManager.ControllerName.Wand) + { + if (App.CurrentState != App.AppState.Standard || m_PanelManager.IntroSketchbookMode) + { + // If app is not in standard mode, the actions represented by subsequent material + // assigments cannot be taken. + return; + } + bool creatingStroke = PointerManager.m_Instance.IsMainPointerCreatingStroke(); + bool allowPainting = App.Instance.IsInStateThatAllowsPainting(); + + InputManager.Wand.Geometry.ShowRotatePanels(); + InputManager.Wand.Geometry.ShowUndoRedo(CanUndo() && !creatingStroke && allowPainting, + CanRedo() && !creatingStroke && allowPainting); + } + + // Show the pin cushion icon on the button if it's available. + if (controller == InputManager.ControllerName.Brush && CanUsePinCushion()) + { + InputManager.Brush.Geometry.ShowPinCushion(); + } + + // Finally, override with tools. + m_SketchSurfacePanel.AssignControllerMaterials(controller); + } + + public float GetControllerPadShaderRatio( + InputManager.ControllerName controller, VrInput input) + { + // If we're interacting with a panel, get touch ratio from the panel. + if (controller == InputManager.ControllerName.Brush && m_CurrentGazeObject != -1) + { + BasePanel panel = m_PanelManager.GetPanel(m_CurrentGazeObject); + return panel.GetControllerPadShaderRatio(controller); + } + return SketchSurfacePanel.m_Instance.GetCurrentToolSizeRatio(controller, input); + } + + void SwitchState(InputState rDesiredState) + { + //exit current state + switch (m_CurrentInputState) + { + case InputState.Pan: + m_TransformGizmoScript.ResetTransform(); + break; + case InputState.PushPull: + m_TransformGizmoScript.ResetTransform(); + break; + case InputState.Rotation: + m_RotationRollActive = false; + m_RotationIcon.SetActive(false); + m_RotationCursor.gameObject.SetActive(false); + break; + } + + bool bSketchSurfaceToolActive = m_SketchSurfacePanel.IsSketchSurfaceToolActive(); + + //enter new state + switch (rDesiredState) + { + case InputState.Pan: + m_TransformGizmoScript.SetTransformForPan(); + break; + case InputState.PushPull: + m_TransformGizmoScript.SetTransformForPushPull(); + break; + case InputState.Rotation: + if (bSketchSurfaceToolActive) + { + m_SketchSurface.transform.position = PointerManager.m_Instance.MainPointer.transform.position; + m_SketchSurfacePanel.ResetReticleOffset(); + } + m_RotationOrigin = m_SketchSurface.transform.rotation; + m_RotationCursorOffset = Vector2.zero; + m_RotationCursor.transform.position = m_SketchSurface.transform.position; + m_RotationCursor.transform.rotation = m_SketchSurface.transform.rotation; + m_RotationCursor.ClearCursorLines(m_SketchSurface.transform.position); + m_RotationCursor.gameObject.SetActive(bSketchSurfaceToolActive); + break; + case InputState.HeadLock: + m_SurfaceLockBaseHeadRotation = m_CurrentHeadOrientation; + m_SurfaceLockBaseSurfaceRotation = m_SketchSurface.transform.rotation; + m_SurfaceLockOffset = m_SketchSurface.transform.position - m_CurrentGazeRay.origin; + m_SurfaceLockOffset = Quaternion.Inverse(m_SurfaceLockBaseHeadRotation) * m_SurfaceLockOffset; + break; + case InputState.ControllerLock: + if (bSketchSurfaceToolActive) + { + m_SketchSurface.transform.position = PointerManager.m_Instance.MainPointer.transform.position; + m_SketchSurfacePanel.ResetReticleOffset(); + } + m_SurfaceLockActingController = InputManager.m_Instance.GetDominantController(InputManager.SketchCommands.LockToController); + m_SurfaceLockBaseSurfaceRotation = m_SketchSurface.transform.rotation; + m_SurfaceLockBaseControllerRotation = InputManager.m_Instance.GetControllerRotation(m_SurfaceLockActingController); + m_SurfaceLockBaseSurfacePosition = m_SketchSurface.transform.position; + m_SurfaceLockBaseControllerPosition = InputManager.m_Instance.GetControllerPosition(m_SurfaceLockActingController); + m_SurfaceLockControllerScalar = m_SketchSurfacePanel.m_PanelSensitivity / m_SurfaceLockControllerBaseScalar; + break; + case InputState.Save: + IssueGlobalCommand(GlobalCommands.Save); + break; + case InputState.Load: + IssueGlobalCommand(GlobalCommands.Load); + break; + } + + m_CurrentInputState = rDesiredState; + } + + public void RequestPanelsVisibility(bool bVisible) + { + // Always false in viewonly mode + bVisible = m_ViewOnly ? false : bVisible; + m_PanelsVisibilityRequested = bVisible; + } + + Quaternion OrientSketchSurfaceToUp() + { + //project the world up vector on to the surface plane + Vector3 vUpOnSurfacePlane = Vector3.up - (Vector3.Dot(Vector3.up, m_SurfaceForward) * m_SurfaceForward); + vUpOnSurfacePlane.Normalize(); + + //get the angle between the surface up and the projected world up + float fUpOnSurfacePlaneAngle = Vector3.Angle(vUpOnSurfacePlane, m_SurfaceUp); + Vector3 vUpCross = Vector3.Cross(vUpOnSurfacePlane, m_SurfaceUp); + vUpCross.Normalize(); + if (Vector3.Dot(vUpCross, m_SurfaceForward) > 0.0f) + { + fUpOnSurfacePlaneAngle *= -1.0f; + } + + //rotate around the surface foward by the angle diff + Quaternion qOrientToUp = Quaternion.AngleAxis(fUpOnSurfacePlaneAngle, m_SurfaceForward); + Quaternion qNewRotation = qOrientToUp * m_SketchSurface.transform.rotation; + return qNewRotation; + } + + Quaternion OrientSketchSurfaceToForward() + { + //project the world forward vector on to the surface plane + Vector3 vForwardOnSurfacePlane = Vector3.forward - (Vector3.Dot(Vector3.forward, m_SurfaceForward) * m_SurfaceForward); + vForwardOnSurfacePlane.Normalize(); + + //get the angle between the surface up and the projected world forward + float fForwardOnSurfacePlaneAngle = Vector3.Angle(vForwardOnSurfacePlane, m_SurfaceUp); + Vector3 vUpCross = Vector3.Cross(vForwardOnSurfacePlane, m_SurfaceUp); + vUpCross.Normalize(); + if (Vector3.Dot(vUpCross, m_SurfaceForward) > 0.0f) + { + fForwardOnSurfacePlaneAngle *= -1.0f; + } + + //rotate around the surface foward by the angle diff + Quaternion qOrientToForward = Quaternion.AngleAxis(fForwardOnSurfacePlaneAngle, m_SurfaceForward); + Quaternion qNewRotation = qOrientToForward * m_SketchSurface.transform.rotation; + return qNewRotation; + } + + /// Reset the scene or the canvas, depending on the current mode + public void ResetGrabbedPose(bool everything = false) + { + //update sketch surface position with offset to sweet spot + m_SketchSurface.transform.position = m_PanelManager.GetSketchSurfaceResetPos(); + if (everything) + { + App.Scene.Pose = TrTransform.identity; + Coords.CanvasLocalPose = TrTransform.identity; + } + App.Scene.Pose = TrTransform.identity; + + //reset orientation and pointer + ResetSketchSurfaceOrientation(); + m_SketchSurfacePanel.ResetReticleOffset(); + PointerManager.m_Instance.DisablePointerPreviewLine(); + PointerManager.m_Instance.SetPointerPreviewLineDelayTimer(); + } + + public void ResetSketchSurfaceOrientation() + { + m_SketchSurface.transform.rotation = Quaternion.identity; + m_SurfaceForward = m_SketchSurface.transform.forward; + m_SurfaceRight = m_SketchSurface.transform.right; + m_SurfaceUp = m_SketchSurface.transform.up; + } + + float GetAppropriateMovementScalar() + { + switch (m_CurrentInputState) + { + case InputState.Pan: return m_PanScalar; + case InputState.Rotation: return m_RotationScalar; + case InputState.PushPull: return m_PushPullScale; + } + + return 1.0f; + } + + // TODO - it'd be great if we could disentangle this from the multicam. + IEnumerator RenderPathAndQuit() + { +#if USD_SUPPORTED + App.Instance.SetDesiredState(App.AppState.OfflineRendering); + SketchSurfacePanel.m_Instance.EnableSpecificTool(BaseTool.ToolType.MultiCamTool); + MultiCamTool multiCam = SketchSurfacePanel.m_Instance.ActiveTool as MultiCamTool; + Debug.Assert(multiCam != null); // Something's gone wrong if we've been unable to find multicam! + if (multiCam == null) + { + yield break; + } + multiCam.ExternalObjectForceCameraStyle(MultiCamStyle.Video); + MultiCamCaptureRig.ForceClippingPlanes(MultiCamStyle.Video); + // Give the video tool time to switch - TODO - be a little more graceful here + yield return new WaitForSeconds(2); + // Make sure the videos have had time to load, and set playing ones to start + while (VideoCatalog.Instance.IsScanning) + { + yield return null; + } + foreach (var widget in WidgetManager.m_Instance.VideoWidgets) + { + if (widget.VideoController.Playing) + { + widget.VideoController.Position = 0; + } + } + yield return null; + var ssMgr = MultiCamCaptureRig.ManagerFromStyle(MultiCamStyle.Video); + ssMgr.SetScreenshotResolution(App.UserConfig.Video.OfflineResolution); + multiCam.StartVideoCapture(MultiCamTool.GetSaveName(MultiCamStyle.Video), offlineRender: true); + App.Instance.FrameCountDisplay.gameObject.SetActive(true); + App.Instance.FrameCountDisplay.SetFramesTotal(VideoRecorderUtils.NumFramesInUsdSerializer); + while (VideoRecorderUtils.ActiveVideoRecording != null) + { + App.Instance.FrameCountDisplay.SetCurrentFrame( + VideoRecorderUtils.ActiveVideoRecording.FrameCount); + yield return null; + } + ssMgr.SetScreenshotResolution(App.UserConfig.Video.Resolution); +#else + Debug.LogError("Render path requires USD support"); + yield return null; +#endif + QuitApp(); + } + + IEnumerator ExportListAndQuit() + { + App.Config.m_ForceDeterministicBirthTimeForExport = true; + List filesToExport = new List(); + foreach (string filePattern in App.Config.m_FilePatternsToExport) + { + bool absolute = Path.IsPathRooted(filePattern); + string directory = absolute ? Path.GetDirectoryName(filePattern) : App.UserSketchPath(); + string filename = Path.GetFileName(filePattern); + var tiltFiles = Directory.GetFiles(directory, filename); + filesToExport.AddRange(tiltFiles); + // Also look at .tilt files which have been unzipped into directory format + var tiltDirs = Directory.GetDirectories(directory, filename) + .Where(n => n.EndsWith(".tilt")); + filesToExport.AddRange(tiltDirs); + } + + using (var coroutine = LoadAndExportList(filesToExport)) + { + while (coroutine.MoveNext()) + { + yield return coroutine.Current; + } + } + QuitApp(); + } + + void QuitApp() + { + // We're done! Quit! +#if UNITY_EDITOR + UnityEditor.EditorApplication.isPlaying = false; +#else + Application.Quit(); +#endif + } + + // This coroutine must be run to completion or disposed. + IEnumerator LoadAndExportList(List filenames) + { + foreach (var filename in filenames) + { + using (var coroutine = LoadAndExport(filename)) + { + while (coroutine.MoveNext()) + { + yield return coroutine.Current; + } + } + } + } + + // This coroutine must be run to completion or disposed. + IEnumerator LoadAndExportAll() + { + SketchSet sketchSet = SketchCatalog.m_Instance.GetSet(SketchSetType.User); + for (int i = 0; i < SketchCatalog.m_Instance.GetSet(SketchSetType.User).NumSketches; ++i) + { + SceneFileInfo rInfo = sketchSet.GetSketchSceneFileInfo(i); + using (var coroutine = LoadAndExport(rInfo.FullPath)) + { + while (coroutine.MoveNext()) + { + yield return coroutine.Current; + } + } + } + } + + /// Loads a .tilt file completely. + /// This may be slightly buggy; it's not currently used for production. + /// This coroutine must be run to completion or disposed. + public IEnumerable LoadTiltFile(string filename) + { + using (var unused = new SceneSettings.RequestInstantSceneSwitch()) + { + IssueGlobalCommand( + GlobalCommands.LoadNamedFile, + iParam1: (int)LoadSpeed.Quick, sParam: filename); + yield return null; + while (App.Instance.IsLoading()) + { + yield return null; + } + + // I don't know why App.Instance.IsLoading() doesn't cover this, but it doesn't. + while (m_WidgetManager.CreatingMediaWidgets) + { + yield return null; + } + while (WidgetManager.m_Instance.AreMediaWidgetsStillLoading()) + { + yield return null; + } + + // This is kind of a hack. + // Despite the RequestInstantSceneSwitch above, I think scene colors still require + // a few frames to settle; also, GrabWidgets need to register themselves on the + // first frame, etc. + for (int i = 0; i < 10; ++i) + { + yield return null; + } + } + } + + // This coroutine must be run to completion or disposed. + IEnumerator LoadAndExport(string filename) + { + foreach (var val in LoadTiltFile(filename)) + { + yield return val; + } + using (var coroutine = ExportCoroutine()) + { + while (coroutine.MoveNext()) + { + yield return coroutine.Current; + } + } + } + + IEnumerator ExportCoroutine() + { + return OverlayManager.m_Instance.RunInCompositor( + OverlayType.Export, () => + { + // Sort of a kludge: put stuff back into the main canvas + SelectionManager.m_Instance.ClearActiveSelection(); + Export.ExportScene(); + }, 0.25f, false, true); + } + + private void SaveModel() + { +#if USD_SUPPORTED + var current = SaveLoadScript.m_Instance.SceneFile; + string basename = (current.Valid) + ? Path.GetFileNameWithoutExtension(current.FullPath) + : "Untitled"; + string directoryName = FileUtils.GenerateNonexistentFilename( + App.ModelLibraryPath(), basename, ""); + + string usdname = Path.Combine(directoryName, basename + ".usd"); + // TODO: export selection only, though this is still only experimental. The blocking + // issue to implement this is that the export collector needs to expose this as an option. + // + // SelectionManager.m_Instance.HasSelection + // ? SelectionManager.m_Instance.SelectedStrokes + // : null + ExportUsd.ExportPayload(usdname); + OutputWindowScript.m_Instance.CreateInfoCardAtController( + InputManager.ControllerName.Brush, "Model created!"); +#endif + } + + /// Generates a view from the previous thumbnail viewpoint. + public void GenerateReplacementSaveIcon() + { + if (SaveLoadScript.m_Instance.LastThumbnail_SS.HasValue) + { + TrTransform thumbnailInGlobalSpace = App.Scene.Pose * + SaveLoadScript.m_Instance.LastThumbnail_SS.Value; + + m_SaveIconTool.ProgrammaticCaptureSaveIcon(thumbnailInGlobalSpace.translation, + thumbnailInGlobalSpace.rotation); + } + else + { + GenerateBestGuessSaveIcon(); + } + } + + public void GenerateBestGuessSaveIcon() + { + TrTransform camXform = GenerateBestGuessSaveIconTransform(); + m_SaveIconTool.ProgrammaticCaptureSaveIcon(camXform.translation, camXform.rotation); + } + + /// This positions the save icon camera at the user's head position, and faces it towards + /// the most recent strokes the user has created. + /// If there are no strokes, it faces towards the 'most recent' models. + /// Sadly we cannot really mix the two as we don't know when the models were instantiated. + public TrTransform GenerateBestGuessSaveIconTransform(int itemsToEnumerate = 0) + { + if (itemsToEnumerate == 0) + { + itemsToEnumerate = m_NumStrokesForSaveIcon; + } + int startIndex = Mathf.Max(0, SketchMemoryScript.AllStrokesCount() - itemsToEnumerate); + var lastFewStrokes = SketchMemoryScript.AllStrokes().Skip(startIndex).ToArray(); + + Bounds bounds; + if (lastFewStrokes.Length > 0) + { + bounds = new Bounds(lastFewStrokes.First().m_ControlPoints.First().m_Pos, Vector3.zero); + foreach (var stroke in lastFewStrokes.Skip(1)) + { + bounds.Encapsulate(stroke.m_ControlPoints.First().m_Pos); + bounds.Encapsulate(stroke.m_ControlPoints.Last().m_Pos); + } + } + else + { + // If we have no strokes, just use the aggregates bounding boxes of the blocks models. + var models = m_WidgetManager.ModelWidgets.ToArray(); + // we should always have models to get here, but just in case... + if (models.Length > 0) + { + startIndex = Mathf.Max(0, models.Length - itemsToEnumerate); + bounds = models[startIndex].WorldSpaceBounds; + for (int i = startIndex + 1; i < models.Length; ++i) + { + bounds.Encapsulate(models[i].WorldSpaceBounds); + } + } + else + { + bounds = new Bounds(new Vector3(0, 1, -100000), Vector3.one); // some point in the distance + } + } + + Vector3 camPos = ViewpointScript.Head.position; + Vector3 worldPos = App.Scene.Pose.MultiplyPoint(bounds.center); + Quaternion direction = Quaternion.LookRotation(worldPos - camPos); + return TrTransform.TR(camPos, direction); + } + + + public void GenerateBoundingBoxSaveIcon() + { + Vector3 vNewCamPos; + { + Bounds rCanvasBounds = App.Scene.AllCanvases + .Select(canvas => canvas.GetCanvasBoundingBox()) + .Aggregate((b1, b2) => + { + b1.Encapsulate(b2); + return b1; + }); + + //position the camera at the center of the canvas bounds + vNewCamPos = rCanvasBounds.center; + + //back the camera up, along -z until we can see the extent of the bounds + float fCanvasWidth = rCanvasBounds.max.x - rCanvasBounds.min.x; + float fCanvasHeight = rCanvasBounds.max.y - rCanvasBounds.min.y; + float fLargerExtent = Mathf.Max(fCanvasHeight, fCanvasWidth); + + //half fov for camera + float fHalfFOV = m_SaveIconTool.ScreenshotManager.LeftEye.fieldOfView * 0.5f; + + //TODO: find the real reason this isn't working as it should + float fMagicNumber = 1.375f; + + //set new cam position and zero out orientation + float fBackupDistance = (fLargerExtent * 0.5f) + * Mathf.Tan(Mathf.Deg2Rad * fHalfFOV) * fMagicNumber; + vNewCamPos.z = rCanvasBounds.min.z - fBackupDistance; + } + + m_SaveIconTool.ProgrammaticCaptureSaveIcon(vNewCamPos, Quaternion.identity); + } + + private void MergeBrushStrokes(SceneFileInfo fileInfo) + { + m_PanelManager.ToggleSketchbookPanels(isLoadingSketch: true); + PointerManager.m_Instance.EnablePointerStrokeGeneration(true); + if (SaveLoadScript.m_Instance.Load(fileInfo, true)) + { + SketchMemoryScript.m_Instance.SetPlaybackMode(m_SketchPlaybackMode, m_DefaultSketchLoadSpeed); + SketchMemoryScript.m_Instance.BeginDrawingFromMemory(bDrawFromStart: true, false, false); + // the order of these two lines are important as ExitIntroSketch is setting the + // color of the pointer and we need the color to be set before we go to the Loading + // state. App script's ShouldTintControllers allow the controller to be tinted only + // when the app is in the standard mode. That was there to prevent the controller color + // from flickering while in the intro mode. + App.Instance.ExitIntroSketch(); + App.Instance.SetDesiredState(App.AppState.QuickLoad); + } + } + + public void LoadSketch(SceneFileInfo fileInfo, bool quickload = false, bool additive = false) + { + LightsControlScript.m_Instance.DiscoMode = false; + m_WidgetManager.FollowingPath = false; + m_WidgetManager.CameraPathsVisible = false; + m_WidgetManager.DestroyAllWidgets(); + m_PanelManager.ToggleSketchbookPanels(isLoadingSketch: true); + ResetGrabbedPose(everything: true); + PointerManager.m_Instance.EnablePointerStrokeGeneration(true); + if (SaveLoadScript.m_Instance.Load(fileInfo, additive)) + { + SketchMemoryScript.m_Instance.SetPlaybackMode(m_SketchPlaybackMode, m_DefaultSketchLoadSpeed); + SketchMemoryScript.m_Instance.BeginDrawingFromMemory(bDrawFromStart: true); + // the order of these two lines are important as ExitIntroSketch is setting the + // color of the pointer and we need the color to be set before we go to the Loading + // state. App script's ShouldTintControllers allow the controller to be tinted only + // when the app is in the standard mode. That was there to prevent the controller color + // from flickering while in the intro mode. + App.Instance.ExitIntroSketch(); + App.Instance.SetDesiredState(quickload ? App.AppState.QuickLoad : App.AppState.Loading); + } + QualityControls.m_Instance.ResetAutoQuality(); + m_WidgetManager.ValidateCurrentCameraPath(); + } + + public void IssueGlobalCommand(GlobalCommands rEnum, int iParam1 = -1, + int iParam2 = -1, string sParam = null) + { + switch (rEnum) + { + + // Keyboard command, for debugging and emergency use. + case GlobalCommands.Save: + { + if (!FileUtils.CheckDiskSpaceWithError(App.UserSketchPath())) + { + return; + } + // Disable active selection before saving. + // This looks fishy, here's what's going on: When an object is selected and it has moved, the + // the user is observing the selection canvas in the HMD, but we will be saving the main canvas. + // Because they haven't deselected yet, the selection canvas and the main canvas are out of sync + // so the strokes that will be saved will not match what the user sees. + // + // Here we deselect to force the main canvas to sync with the selection canvas, which is more + // correct from the user's perspective. Push the deselect operation onto the stack so the user + // can undo it after save, if desired. + SelectionManager.m_Instance.ClearActiveSelection(); + GenerateReplacementSaveIcon(); + if (iParam1 == -1) + { + if (iParam2 == 1) + { + // Do a save in Tiltasaurus mode, which creates a new filename prefixed with + // "Tiltasaurus_" and the current prompt. Also, don't eat gaze input so that the + // Tiltasaurus prompt stays open. + StartCoroutine(SaveLoadScript.m_Instance.SaveOverwrite(tiltasaurusMode: true)); + } + else + { + StartCoroutine(SaveLoadScript.m_Instance.SaveOverwrite()); + EatGazeObjectInput(); + } + } + else + { + StartCoroutine(SaveLoadScript.m_Instance.SaveMonoscopic(iParam1)); + } + break; + } + case GlobalCommands.SaveNew: + { + if (!FileUtils.CheckDiskSpaceWithError(App.UserSketchPath())) + { + return; + } + if (iParam1 == 1) + { + GenerateBoundingBoxSaveIcon(); + } + StartCoroutine(SaveLoadScript.m_Instance.SaveNewName()); + EatGazeObjectInput(); + break; + } + case GlobalCommands.SaveAndUpload: + { + if (!FileUtils.CheckDiskSpaceWithError(App.UserSketchPath())) + { + Debug.LogError("SaveAndUpload: Disk space error"); + return; + } + SelectionManager.m_Instance.ClearActiveSelection(); + m_PanelManager.GetPanel(m_CurrentGazeObject).CreatePopUp( + GlobalCommands.UploadToGenericCloud, (int)Cloud.None, -1); + EatGazeObjectInput(); + break; + } + case GlobalCommands.ExportAll: + { + StartCoroutine(LoadAndExportAll()); + break; + } + // Glen Keane request: a way to draw guidelines that can be toggled on and off + // at runtime. + case GlobalCommands.DraftingVisibility: + { + if (!Enum.IsDefined(typeof(DraftingVisibilityOption), iParam1)) + { + Debug.LogError("Unknown draft visibility value: " + iParam1); + return; + } + DraftingVisibilityOption option = (DraftingVisibilityOption)iParam1; + if (option != m_DraftingVisibility) + { + m_DraftingVisibility = option; + UpdateDraftingVisibility(); + } + break; + } + case GlobalCommands.MergeBrushStrokes: + { + // TODO Refactor with Load below + var index = iParam1; + var sketchSetType = (SketchSetType)iParam2; + SketchSet sketchSet = SketchCatalog.m_Instance.GetSet(sketchSetType); + SceneFileInfo rInfo = sketchSet.GetSketchSceneFileInfo(index); + if (rInfo != null) + { + MergeBrushStrokes(rInfo); + if (m_ControlsType != ControlsType.ViewingOnly) + { + EatGazeObjectInput(); + } + } + break; + } + case GlobalCommands.Load: + { + var index = iParam1; + var sketchSetType = (SketchSetType)iParam2; + SketchSet sketchSet = SketchCatalog.m_Instance.GetSet(sketchSetType); + SceneFileInfo rInfo = sketchSet.GetSketchSceneFileInfo(index); + if (rInfo != null) + { + LoadSketch(rInfo); + if (m_ControlsType != ControlsType.ViewingOnly) + { + EatGazeObjectInput(); + } + } + break; + } + case GlobalCommands.LoadNamedFile: + LoadNamed(sParam, iParam1 == (int)LoadSpeed.Quick, iParam2 != -1); + break; + case GlobalCommands.NewSketch: + NewSketch(fade: true); + Vector3 vTrashSoundPos = m_CurrentGazeRay.origin; + if (App.VrSdk.GetControllerDof() == VrSdk.DoF.Six) + { + vTrashSoundPos = InputManager.m_Instance.GetControllerPosition( + InputManager.ControllerName.Wand); + } + AudioManager.m_Instance.PlayTrashSound(vTrashSoundPos); + PromoManager.m_Instance.RequestAdvancedPanelsPromo(); + break; + case GlobalCommands.SymmetryPlane: + if (PointerManager.m_Instance.CurrentSymmetryMode != SymmetryMode.SinglePlane) + { + PointerManager.m_Instance.SetSymmetryMode(SymmetryMode.SinglePlane); + ControllerConsoleScript.m_Instance.AddNewLine("Mirror Enabled"); + } + else + { + PointerManager.m_Instance.SetSymmetryMode(SymmetryMode.None); + ControllerConsoleScript.m_Instance.AddNewLine("Mirror Off"); + } + break; + case GlobalCommands.MultiMirror: + if (PointerManager.m_Instance.CurrentSymmetryMode != SymmetryMode.MultiMirror) + { + PointerManager.m_Instance.SetSymmetryMode(SymmetryMode.MultiMirror); + ControllerConsoleScript.m_Instance.AddNewLine("Symmetry Enabled"); + } + else + { + PointerManager.m_Instance.SetSymmetryMode(SymmetryMode.None); + ControllerConsoleScript.m_Instance.AddNewLine("Symmetry Off"); + } + InputManager.m_Instance.TriggerHaptics(InputManager.ControllerName.Brush, 0.1f); + break; + case GlobalCommands.SymmetryTwoHanded: + if (PointerManager.m_Instance.CurrentSymmetryMode != SymmetryMode.TwoHanded) + { + PointerManager.m_Instance.SetSymmetryMode(SymmetryMode.TwoHanded); + ControllerConsoleScript.m_Instance.AddNewLine("Symmetry Enabled"); + } + else + { + PointerManager.m_Instance.SetSymmetryMode(SymmetryMode.None); + ControllerConsoleScript.m_Instance.AddNewLine("Symmetry Off"); + } + InputManager.m_Instance.TriggerHaptics(InputManager.ControllerName.Brush, 0.1f); + break; + case GlobalCommands.StraightEdge: + PointerManager.m_Instance.StraightEdgeModeEnabled = !PointerManager.m_Instance.StraightEdgeModeEnabled; + if (PointerManager.m_Instance.StraightEdgeModeEnabled) + { + ControllerConsoleScript.m_Instance.AddNewLine("Straight Edge On"); + } + else + { + ControllerConsoleScript.m_Instance.AddNewLine("Straight Edge Off"); + } + break; + case GlobalCommands.AutoOrient: + m_AutoOrientAfterRotation = !m_AutoOrientAfterRotation; + if (m_AutoOrientAfterRotation) + { + ControllerConsoleScript.m_Instance.AddNewLine("Auto-Orient On"); + } + else + { + ControllerConsoleScript.m_Instance.AddNewLine("Auto-Orient Off"); + } + break; + case GlobalCommands.Undo: + SketchMemoryScript.m_Instance.StepBack(); + break; + case GlobalCommands.Redo: + SketchMemoryScript.m_Instance.StepForward(); + break; + case GlobalCommands.AudioVisualization: // Intentionally blank. + break; + case GlobalCommands.ResetAllPanels: + m_PanelManager.ResetWandPanelsConfiguration(); + EatGazeObjectInput(); + break; + case GlobalCommands.SketchOrigin: + m_SketchSurfacePanel.EnableSpecificTool(BaseTool.ToolType.SketchOrigin); + EatGazeObjectInput(); + break; + case GlobalCommands.ViewOnly: + m_ViewOnly = !m_ViewOnly; + RequestPanelsVisibility(!m_ViewOnly); + PointerManager.m_Instance.RequestPointerRendering(!m_ViewOnly); + // TODO - decide if this is a permanent change + // With this line, you can't set a tool such as fly or teleport + // and switch to View Only mode as the mode change disables all tools + //m_SketchSurface.SetActive(!m_ViewOnly); + m_Decor.SetActive(!m_ViewOnly); + break; + case GlobalCommands.SaveGallery: + m_SketchSurfacePanel.EnableSpecificTool(BaseTool.ToolType.SaveIconTool); + break; + case GlobalCommands.DropCam: + // Want to enable this if in monoscopic or VR modes. + // TODO: seems odd to tie this switch to the controller type, should be based on some + // other build-time configuration setting. + if (App.VrSdk.GetControllerDof() != VrSdk.DoF.None) + { + m_DropCam.Show(!m_DropCam.gameObject.activeSelf); + } + break; + case GlobalCommands.AnalyticsEnabled_Deprecated: + break; + case GlobalCommands.ToggleAutosimplification: + QualityControls.AutosimplifyEnabled = !QualityControls.AutosimplifyEnabled; + break; + case GlobalCommands.Credits: + LoadSketch(new DiskSceneFileInfo(m_CreditsSketchFilename, embedded: true, readOnly: true)); + EatGazeObjectInput(); + break; + case GlobalCommands.AshleysSketch: + LoadSketch(new DiskSceneFileInfo(m_AshleysSketchFilename, embedded: true, readOnly: true)); + EatGazeObjectInput(); + break; + case GlobalCommands.FAQ: + OpenURLAndInformUser(m_HelpCenterURL); + break; + case GlobalCommands.ReleaseNotes: + OpenURLAndInformUser(m_ReleaseNotesURL); + break; + case GlobalCommands.ExportRaw: + if (!FileUtils.CheckDiskSpaceWithError(App.UserExportPath())) + { + return; + } + EatGazeObjectInput(); + StartCoroutine(ExportCoroutine()); + break; + case GlobalCommands.IRC: + if (m_IRCChatWidget == null) + { + GameObject widgetobject = (GameObject)Instantiate(m_IRCChatPrefab); + widgetobject.transform.parent = App.Instance.m_RoomTransform; + m_IRCChatWidget = widgetobject.GetComponent(); + m_IRCChatWidget.Show(true); + } + else + { + m_IRCChatWidget.Show(false); + m_IRCChatWidget = null; + } + break; + case GlobalCommands.YouTubeChat: + if (m_YouTubeChatWidget == null) + { + GameObject widgetobject = (GameObject)Instantiate(m_YouTubeChatPrefab); + widgetobject.transform.parent = App.Instance.m_RoomTransform; + m_YouTubeChatWidget = widgetobject.GetComponent(); + m_YouTubeChatWidget.Show(true); + } + else + { + m_YouTubeChatWidget.Show(false); + m_YouTubeChatWidget = null; + } + break; + case GlobalCommands.CameraOptions: + // If we're switching in to Camera mode, make sure Multicam is selected. + if (!m_PanelManager.CameraActive()) + { + SketchSurfacePanel.m_Instance.EnableSpecificTool(BaseTool.ToolType.MultiCamTool); + } + m_PanelManager.ToggleCameraPanels(); + PointerManager.m_Instance.EatLineEnabledInput(); + SketchSurfacePanel.m_Instance.EatToolsInput(); + break; + case GlobalCommands.ShowSketchFolder: + { + var index = iParam1; + var sketchSetType = (SketchSetType)iParam2; + SketchSet sketchSet = SketchCatalog.m_Instance.GetSet(sketchSetType); + SceneFileInfo rInfo = sketchSet.GetSketchSceneFileInfo(index); + EatGazeObjectInput(); + //launch external window and tell the user we did so + //this call is windows only + if ((Application.platform == RuntimePlatform.WindowsPlayer) || + (Application.platform == RuntimePlatform.WindowsEditor)) + { + OutputWindowScript.m_Instance.CreateInfoCardAtController( + InputManager.ControllerName.Brush, + kRemoveHeadsetFyi, fPopScalar: 0.5f); + System.Diagnostics.Process.Start("explorer.exe", + "/select," + rInfo.FullPath); + } + break; + } + case GlobalCommands.About: + OpenURLAndInformUser(m_ThirdPartyNoticesURL); + break; + case GlobalCommands.StencilsDisabled: + SketchMemoryScript.m_Instance.PerformAndRecordCommand(new StencilsVisibleCommand()); + break; + case GlobalCommands.StraightEdgeMeterDisplay: + PointerManager.m_Instance.StraightEdgeGuide.FlipMeter(); + break; + case GlobalCommands.Sketchbook: + m_PanelManager.ToggleSketchbookPanels(); + PointerManager.m_Instance.EatLineEnabledInput(); + SketchSurfacePanel.m_Instance.EatToolsInput(); + break; + case GlobalCommands.StraightEdgeShape: + // Previously experimental mode only. + // Untested and currently untriggerable. + PointerManager.m_Instance.StraightEdgeGuide.SetTempShape( + (StraightEdgeGuideScript.Shape)iParam1); + break; + case GlobalCommands.DeleteSketch: + { + var sketchSetType = (SketchSetType)iParam2; + SketchSet sketchSet = SketchCatalog.m_Instance.GetSet(sketchSetType); + sketchSet.DeleteSketch(iParam1); + DismissPopupOnCurrentGazeObject(false); + break; + } + case GlobalCommands.RenameSketch: + { + var sketchSetType = (SketchSetType)iParam2; + SketchSet sketchSet = SketchCatalog.m_Instance.GetSet(sketchSetType); + if (sketchSetType == SketchSetType.User) + { + sketchSet.RenameSketch(iParam1, KeyboardPopUpWindow.m_LastInput); + } + DismissPopupOnCurrentGazeObject(false); + break; + } + case GlobalCommands.RenameLayer: + { + var layer = App.Scene.GetCanvasByLayerIndex(iParam1); + App.Scene.RenameLayer(layer, KeyboardPopUpWindow.m_LastInput); + DismissPopupOnCurrentGazeObject(false); + break; + } + case GlobalCommands.EditMultiplayerRoomName: + { + var panel = (MultiplayerPanel)m_PanelManager.GetActivePanelByType(BasePanel.PanelType.Multiplayer); + panel.RoomName = KeyboardPopUpWindow.m_LastInput; + DismissPopupOnCurrentGazeObject(false); + break; + } + case GlobalCommands.EditMultiplayerNickName: + { + var panel = (MultiplayerPanel)m_PanelManager.GetActivePanelByType(BasePanel.PanelType.Multiplayer); + panel.NickName = KeyboardPopUpWindow.m_LastInput; + DismissPopupOnCurrentGazeObject(false); + break; + } + case GlobalCommands.ShowWindowGUI: + break; + case GlobalCommands.Disco: + LightsControlScript.m_Instance.DiscoMode = !LightsControlScript.m_Instance.DiscoMode; + break; + case GlobalCommands.AccountInfo: break; // Intentionally blank. + case GlobalCommands.LoginToGenericCloud: + { + var ident = App.GetIdentity((Cloud)iParam1); + if (!ident.LoggedIn) { ident.LoginAsync(); } + // iParam2 is being used as a UX flag. If not set to the default, it will cause the UI + // to lose focus. + if (iParam2 != -1) { EatGazeObjectInput(); } + break; + } + case GlobalCommands.LogOutOfGenericCloud: + { + var ident = App.GetIdentity((Cloud)iParam1); + if (ident.LoggedIn) { ident.Logout(); } + break; + } + case GlobalCommands.UploadToGenericCloud: + { + Cloud cloud = (Cloud)iParam1; + var ident = App.GetIdentity(cloud); + if (!ident.LoggedIn) + { + ident.LoginAsync(); + break; + } + SelectionManager.m_Instance.ClearActiveSelection(); + VrAssetService.m_Instance.UploadCurrentSketchAsync(cloud, isDemoUpload: false).AsAsyncVoid(); + EatGazeObjectInput(); + break; + } + case GlobalCommands.ViewOnlineGallery: + OpenURLAndInformUser(kTiltBrushGalleryUrl); + break; + case GlobalCommands.CancelUpload: + VrAssetService.m_Instance.CancelUpload(); + break; + case GlobalCommands.ViewLastUpload: + if (VrAssetService.m_Instance.LastUploadCompleteUrl != null) + { + var url = VrAssetService.m_Instance.LastUploadCompleteUrl; + App.OpenURL(url); + + // The upload flow is different on mobile and requires the user to manually accept + // that they'll go to the browser for publishing. In that case, we want to reset + // state when the leave to publish. This is automatically part of the + // UploadPopUpWindow state flow on PC. + if (App.Config.IsMobileHardware) + { + DismissPopupOnCurrentGazeObject(true); + } + } + break; + case GlobalCommands.ShowGoogleDrive: + string baseDriveUrl = "https://drive.google.com"; + string driveURL = !App.GoogleIdentity.LoggedIn ? baseDriveUrl : + string.Format( + "http://accounts.google.com/AccountChooser?Email={0}&continue={1}", + App.GoogleIdentity.Profile.email, baseDriveUrl); + OpenURLAndInformUser(driveURL); + break; + case GlobalCommands.GoogleDriveSync: + App.DriveSync.SyncEnabled = !App.DriveSync.SyncEnabled; + break; + case GlobalCommands.GoogleDriveSync_Folder: + App.DriveSync.ToggleSyncOnFolderOfType((DriveSync.SyncedFolderType)iParam1); + break; + case GlobalCommands.Duplicate: + { + int selectedVerts = SelectionManager.m_Instance.NumVertsInSelection; + + // TODO - this code has never taken imported models etc into account + if (PointerManager.m_Instance.CurrentSymmetryMode == SymmetryMode.MultiMirror) + { + selectedVerts *= PointerManager.m_Instance.CustomMirrorMatrices.Count; + } + + if (!SketchMemoryScript.m_Instance.MemoryWarningAccepted && + SketchMemoryScript.m_Instance.WillVertCountPutUsOverTheMemoryLimit(selectedVerts)) + { + AudioManager.m_Instance.PlayUploadCanceledSound(InputManager.Wand.Transform.position); + if (!m_PanelManager.MemoryWarningActive()) + { + m_PanelManager.ToggleMemoryWarningMode(); + } + } + else + { + ClipboardManager.Instance.DuplicateSelection( + stampMode: IsUserInteractingWithSelectionWidget()); + } + EatToolScaleInput(); + break; + } + case GlobalCommands.AdvancedPanelsToggle: + m_PanelManager.ToggleAdvancedPanels(); + // If we're now in basic mode, ensure we don't have advanced abilities. + if (!m_PanelManager.AdvancedModeActive()) + { + m_WidgetManager.StencilsDisabled = true; + m_WidgetManager.CameraPathsVisible = false; + App.Switchboard.TriggerStencilModeChanged(); + m_SketchSurfacePanel.EnsureUserHasBasicToolEnabled(); + if (PointerManager.m_Instance.CurrentSymmetryMode != SymmetryMode.None) + { + PointerManager.m_Instance.SetSymmetryMode(SymmetryMode.None, false); + } + } + PromoManager.m_Instance.RecordCompletion(PromoType.AdvancedPanels); + EatGazeObjectInput(); + break; + case GlobalCommands.Music: break; // Intentionally blank. + case GlobalCommands.ToggleGroupStrokesAndWidgets: + SelectionManager.m_Instance.ToggleGroupSelectedStrokesAndWidgets(); + EatToolScaleInput(); + break; + case GlobalCommands.SaveModel: + SaveModel(); + break; + case GlobalCommands.ViewPolyPage: + OpenURLAndInformUser(kPolyMainPageUri); + break; + case GlobalCommands.ViewPolyGallery: + OpenURLAndInformUser(kBlocksGalleryUrl); + break; + case GlobalCommands.ExportListed: + StartCoroutine(ExportListAndQuit()); + break; + case GlobalCommands.RenderCameraPath: + StartCoroutine(RenderPathAndQuit()); + break; + case GlobalCommands.ToggleProfiling: + ToggleProfiling(); + break; + case GlobalCommands.DoAutoProfile: + DoAutoProfile(); + break; + case GlobalCommands.DoAutoProfileAndQuit: + DoAutoProfileAndQuit(); + break; + case GlobalCommands.ToggleSettings: + m_PanelManager.ToggleSettingsPanels(); + PointerManager.m_Instance.EatLineEnabledInput(); + SketchSurfacePanel.m_Instance.EatToolsInput(); + break; + case GlobalCommands.SummonMirror: + PointerManager.m_Instance.BringSymmetryToUser(); + break; + case GlobalCommands.InvertSelection: + SelectionManager.m_Instance.InvertSelection(); + break; + case GlobalCommands.SelectAll: + SketchSurfacePanel.m_Instance.EnableSpecificTool(BaseTool.ToolType.SelectionTool); + SelectionManager.m_Instance.SelectAll(); + EatGazeObjectInput(); + break; + case GlobalCommands.FlipSelection: + SelectionManager.m_Instance.FlipSelection(); + break; + case GlobalCommands.ToggleBrushLab: + m_PanelManager.ToggleBrushLabPanels(); + PointerManager.m_Instance.EatLineEnabledInput(); + SketchSurfacePanel.m_Instance.EatToolsInput(); + break; + case GlobalCommands.ToggleCameraPostEffects: + CameraConfig.PostEffects = !CameraConfig.PostEffects; + break; + case GlobalCommands.ToggleWatermark: + if (PlayerPrefs.GetInt("Promo_Contribution", 0) == 0) + { + OutputWindowScript.m_Instance.CreateInfoCardAtController( + InputManager.ControllerName.Wand, + m_ContributionPromoText, fPopScalar: 1.0f); + PlayerPrefs.SetInt("Promo_Contribution", 1); + } + CameraConfig.Watermark = !CameraConfig.Watermark; + break; + case GlobalCommands.LoadConfirmComplexHigh: + IssueGlobalCommand(GlobalCommands.Load, iParam1, iParam2, null); + break; + case GlobalCommands.LoadConfirmComplex: + { + var index = iParam1; + var sketchSetType = (SketchSetType)iParam2; + bool loadSketch = true; + + // If the sketchbook is active, we may want to show a popup instead of load. + if (m_PanelManager.SketchbookActive()) + { + BasePanel sketchBook = m_PanelManager.GetSketchBookPanel(); + if (sketchBook != null) + { + // Get triangle count from cloud scene file info. + SketchSet sketchSet = SketchCatalog.m_Instance.GetSet(sketchSetType); + SceneFileInfo sfi = sketchSet.GetSketchSceneFileInfo(index); + int tris = sfi.TriangleCount ?? -1; + + // Show "this is bad" popup if we're over the triangle limit. + if (tris > QualityControls.m_Instance.AppQualityLevels.MaxPolySketchTriangles) + { + loadSketch = false; + sketchBook.CreatePopUp(GlobalCommands.LoadConfirmComplexHigh, iParam1, iParam2); + } + else if (tris > + QualityControls.m_Instance.AppQualityLevels.WarningPolySketchTriangles) + { + // Show, "this could be bad" popup if we're over the warning limit. + loadSketch = false; + sketchBook.CreatePopUp(GlobalCommands.Load, iParam1, iParam2); + } + } + } + + if (loadSketch) + { + IssueGlobalCommand(GlobalCommands.Load, iParam1, iParam2, null); + } + } + break; + case GlobalCommands.LoadConfirmUnsaved: + { + BasePanel sketchBook = m_PanelManager.GetSketchBookPanel(); + if ((sketchBook != null) && SketchMemoryScript.m_Instance.IsMemoryDirty()) + { + sketchBook.CreatePopUp(GlobalCommands.LoadWaitOnDownload, iParam1, iParam2, null); + } + else + { + IssueGlobalCommand(GlobalCommands.LoadWaitOnDownload, iParam1, iParam2, null); + } + } + break; + case GlobalCommands.LoadWaitOnDownload: + { + bool download = false; + if (iParam2 == (int)SketchSetType.Drive) + { + BasePanel sketchBook = m_PanelManager.GetSketchBookPanel(); + var googleSketchSet = SketchCatalog.m_Instance.GetSet(SketchSetType.Drive); + if (sketchBook != null + && googleSketchSet != null + && googleSketchSet.IsSketchIndexValid(iParam1) + && !googleSketchSet.GetSketchSceneFileInfo(iParam1).Available) + { + sketchBook.CreatePopUp(GlobalCommands.LoadConfirmComplex, iParam1, iParam2, null); + download = true; + } + } + if (!download) + { + IssueGlobalCommand(GlobalCommands.LoadConfirmComplex, iParam1, iParam2, null); + } + } + break; + case GlobalCommands.MemoryWarning: + if (iParam1 > 0) + { + SketchMemoryScript.m_Instance.MemoryWarningAccepted = true; + } + m_PanelManager.ToggleMemoryWarningMode(); + break; + case GlobalCommands.MemoryExceeded: + // If we're in the memory exceeded app state, exit. + if (App.CurrentState == App.AppState.MemoryExceeded) + { + App.Instance.SetDesiredState(App.AppState.Standard); + } + else + { + // If we're not in the full app state, just switch our panel mode. + m_PanelManager.ToggleMemoryWarningMode(); + } + break; + case GlobalCommands.ShowTos: + OpenURLAndInformUser(m_TosURL); + break; + case GlobalCommands.ShowPrivacy: + OpenURLAndInformUser(m_PrivacyURL); + break; + case GlobalCommands.ShowQuestSideLoading: + OpenURLAndInformUser(m_QuestSideLoadingHowToURL); + break; + case GlobalCommands.ShowContribution: + OpenURLAndInformUser(m_ContributionURL); + break; + case GlobalCommands.UnloadReferenceImageCatalog: + ReferenceImageCatalog.m_Instance.UnloadAllImages(); + break; + case GlobalCommands.ToggleCameraPathVisuals: + m_WidgetManager.CameraPathsVisible = !m_WidgetManager.CameraPathsVisible; + break; + case GlobalCommands.ToggleCameraPathPreview: + m_WidgetManager.FollowingPath = !m_WidgetManager.FollowingPath; + break; + case GlobalCommands.DeleteCameraPath: + { + var cameraPath = m_WidgetManager.GetCurrentCameraPath(); + GrabWidget cameraPathWidget = cameraPath == null ? null : cameraPath.m_WidgetScript; + m_WidgetManager.DeleteCameraPath(cameraPathWidget); + } + break; + case GlobalCommands.RecordCameraPath: + // Turn off MultiCam if we're going to record the camera path. + if (m_SketchSurfacePanel.GetCurrentToolType() == BaseTool.ToolType.MultiCamTool) + { + m_SketchSurfacePanel.EnableDefaultTool(); + } + CameraPathCaptureRig.RecordPath(); + EatGazeObjectInput(); + break; + case GlobalCommands.OpenScriptsCommandsList: + OpenURLAndInformUser($"http://localhost:{App.HttpServer.HttpPort}/help/commands"); + break; + case GlobalCommands.OpenScriptsList: + OpenURLAndInformUser($"http://localhost:{App.HttpServer.HttpPort}/scripts"); + break; + case GlobalCommands.OpenExampleScriptsList: + OpenURLAndInformUser($"http://localhost:{App.HttpServer.HttpPort}/examplescripts"); + break; + case GlobalCommands.MultiplayerTogglePanel: + m_PanelManager.ToggleMultiplayerPanels(); + PointerManager.m_Instance.EatLineEnabledInput(); + SketchSurfacePanel.m_Instance.EatToolsInput(); + break; + case GlobalCommands.RepaintOptions: break; // Intentionally blank. + case GlobalCommands.Null: break; // Intentionally blank. + case GlobalCommands.MultiplayerPanelOptions: break; // Intentionally blank. + case GlobalCommands.MultiplayerJoinRoom: break; // Intentionally blank. case GlobalCommands.MultiplayerLeaveRoom: break; // Intentionally blank. - case GlobalCommands.MultiplayerConnect: break; // Intentionally blank. - case GlobalCommands.MultiplayerDisconnect: break; // Intentionally blank. - default: - Debug.LogError($"Unrecognized command {rEnum}"); - break; - } - } - - private void LoadNamed(string path, bool quickload, bool additive) - { - var fileInfo = new DiskSceneFileInfo(path); - fileInfo.ReadMetadata(); - if (SaveLoadScript.m_Instance.LastMetadataError != null) - { - ControllerConsoleScript.m_Instance.AddNewLine( - string.Format("Error detected in sketch '{0}'.\nTry re-saving.", - fileInfo.HumanName)); - Debug.LogWarning(string.Format("Error reading metadata for {0}.\n{1}", - fileInfo.FullPath, SaveLoadScript.m_Instance.LastMetadataError)); - } - LoadSketch(fileInfo, quickload, additive); - if (m_ControlsType != ControlsType.ViewingOnly) - { - EatGazeObjectInput(); - } - } - - public void OpenURLAndInformUser(string url) - { - // On desktop - launch external browser and inform the user - // On mobile - the browser appears over the app - if (!App.Config.IsMobileHardware) - { - OutputWindowScript.m_Instance.CreateInfoCardAtController( - InputManager.ControllerName.Brush, - kRemoveHeadsetFyi, fPopScalar: 0.5f); - } - App.OpenURL(url); - EatGazeObjectInput(); - } - - public bool IsCommandActive(GlobalCommands rEnum, int iParam = -1) - { - switch (rEnum) - { - case GlobalCommands.StraightEdge: return PointerManager.m_Instance.StraightEdgeModeEnabled; - case GlobalCommands.StraightEdgeMeterDisplay: return PointerManager.m_Instance.StraightEdgeGuide.IsShowingMeter(); - case GlobalCommands.SymmetryPlane: return PointerManager.m_Instance.CurrentSymmetryMode == SymmetryMode.SinglePlane; - case GlobalCommands.MultiMirror: return PointerManager.m_Instance.CurrentSymmetryMode == SymmetryMode.MultiMirror; - case GlobalCommands.SymmetryTwoHanded: return PointerManager.m_Instance.CurrentSymmetryMode == SymmetryMode.TwoHanded; - case GlobalCommands.AutoOrient: return m_AutoOrientAfterRotation; - case GlobalCommands.AudioVisualization: return VisualizerManager.m_Instance.VisualsRequested; - case GlobalCommands.AdvancedPanelsToggle: return m_PanelManager.AdvancedModeActive(); - case GlobalCommands.Music: return VisualizerManager.m_Instance.VisualsRequested; - case GlobalCommands.DropCam: return m_DropCam.gameObject.activeSelf; - case GlobalCommands.ToggleAutosimplification: return QualityControls.AutosimplifyEnabled; - case GlobalCommands.DraftingVisibility: return m_DraftingVisibility == (DraftingVisibilityOption)iParam; - case GlobalCommands.Cameras: - return SketchSurfacePanel.m_Instance.GetCurrentToolType() == BaseTool.ToolType.AutoGif || - SketchSurfacePanel.m_Instance.GetCurrentToolType() == BaseTool.ToolType.ScreenshotTool; - case GlobalCommands.IRC: return m_IRCChatWidget != null; - case GlobalCommands.YouTubeChat: return m_YouTubeChatWidget != null; - case GlobalCommands.StencilsDisabled: return m_WidgetManager.StencilsDisabled; - case GlobalCommands.StraightEdgeShape: - // Previously experimental mode only. - // Untested and currently untriggerable. - return PointerManager.m_Instance.StraightEdgeGuide.TempShape == (StraightEdgeGuideScript.Shape)iParam || - (PointerManager.m_Instance.StraightEdgeGuide.TempShape == StraightEdgeGuideScript.Shape.None - && PointerManager.m_Instance.StraightEdgeGuide.CurrentShape == (StraightEdgeGuideScript.Shape)iParam); - case GlobalCommands.Disco: return LightsControlScript.m_Instance.DiscoMode; - case GlobalCommands.ToggleGroupStrokesAndWidgets: return SelectionManager.m_Instance.UngroupingAllowed; - case GlobalCommands.ToggleProfiling: return UnityEngine.Profiling.Profiler.enabled; - case GlobalCommands.ToggleCameraPostEffects: return CameraConfig.PostEffects; - case GlobalCommands.ToggleWatermark: return CameraConfig.Watermark; - case GlobalCommands.ToggleCameraPathVisuals: return m_WidgetManager.CameraPathsVisible; - case GlobalCommands.ToggleCameraPathPreview: return m_WidgetManager.FollowingPath; - case GlobalCommands.SelectCameraPath: - return m_WidgetManager.IsCameraPathAtIndexCurrent(iParam) && - m_WidgetManager.CameraPathsVisible; - case GlobalCommands.GoogleDriveSync_Folder: - return App.DriveSync.IsFolderOfTypeSynced((DriveSync.SyncedFolderType)iParam); - case GlobalCommands.GoogleDriveSync: return App.DriveSync.SyncEnabled; - case GlobalCommands.RecordCameraPath: return VideoRecorderUtils.ActiveVideoRecording != null; - } - return false; - } - - public void NewSketch(bool fade) - { - LightsControlScript.m_Instance.DiscoMode = false; - m_WidgetManager.FollowingPath = false; - SketchMemoryScript.m_Instance.ClearMemory(); - ControllerConsoleScript.m_Instance.AddNewLine("Sketch Cleared"); - ResetGrabbedPose(everything: true); - QualityControls.m_Instance.ResetAutoQuality(); - InputManager.m_Instance.TriggerHaptics(InputManager.ControllerName.Brush, 0.1f); - SaveLoadScript.m_Instance.ResetLastFilename(); - SelectionManager.m_Instance.RemoveFromSelection(false); - PointerManager.m_Instance.ResetSymmetryToHome(); - App.Scene.ResetLayers(notify: true); - ApiManager.Instance.ResetBrushTransform(); - - // If we've got the camera path tool active, switch back to the default tool. - // I'm doing this because if we leave the camera path tool active, the camera path - // panel shows the button highlighted, which affects the user's flow for being - // invited to start a path. It looks weird. - if (m_SketchSurfacePanel.ActiveToolType == BaseTool.ToolType.CameraPathTool) - { - m_SketchSurfacePanel.EnableDefaultTool(); - } - - m_WidgetManager.DestroyAllWidgets(); - if (LightsControlScript.m_Instance.LightsChanged || - SceneSettings.m_Instance.EnvironmentChanged) - { - SceneSettings.m_Instance.RecordSkyColorsForFading(); - SceneSettings.m_Instance.SetDesiredPreset( - SceneSettings.m_Instance.GetDesiredPreset(), skipFade: !fade); - } - // Blank the thumbnail position so that autosave won't save the thumbnail position to be - // the one from the old sketch. - SaveLoadScript.m_Instance.LastThumbnail_SS = null; - - // Re-set the quality level to reset simplification level - QualityControls.m_Instance.QualityLevel = QualityControls.m_Instance.QualityLevel; - - App.PolyAssetCatalog.ClearLoadingQueue(); - App.PolyAssetCatalog.UnloadUnusedModels(); - } - - private bool WorldIsReset(bool toSavedXf) - { - return App.Scene.Pose == - (toSavedXf ? SketchMemoryScript.m_Instance.InitialSketchTransform : TrTransform.identity); - } - - public bool IsCommandAvailable(GlobalCommands rEnum, int iParam = -1) - { - // TODO: hide gallery view / publish if there are no saved sketches - switch (rEnum) - { - case GlobalCommands.Undo: return SketchMemoryScript.m_Instance.CanUndo(); - case GlobalCommands.Redo: return SketchMemoryScript.m_Instance.CanRedo(); - case GlobalCommands.Save: - bool canSave = - SaveLoadScript.m_Instance.SceneFile.Valid && - SaveLoadScript.m_Instance.IsSavingAllowed(); - return canSave && (!WorldIsReset(toSavedXf: true) || - (SketchHasChanges() && SketchMemoryScript.m_Instance.IsMemoryDirty())); - case GlobalCommands.SaveOptions: - case GlobalCommands.SaveNew: - case GlobalCommands.SaveGallery: - return SketchHasChanges(); - case GlobalCommands.SaveOnLocalChanges: - if (!SaveLoadScript.m_Instance.SceneFile.Valid) - { - // No save file, but something has changed. - return SketchHasChanges(); - } - else - { - if (SaveLoadScript.m_Instance.CanOverwriteSource) - { - // Save file, and it's our file. Whether we have changes is irrelevant. - return true; - } - // Save file, but it's not our file. Only make a copy if there are local changes. - return SketchMemoryScript.m_Instance.IsMemoryDirty(); - } - case GlobalCommands.UploadToGenericCloud: - return SketchMemoryScript.m_Instance.HasVisibleObjects() || - m_WidgetManager.ExportableModelWidgets.Any(w => w.gameObject.activeSelf) || - m_WidgetManager.ImageWidgets.Any(w => w.gameObject.activeSelf) || - VrAssetService.m_Instance.UploadProgress >= 1.0f || - VrAssetService.m_Instance.LastUploadFailed; - case GlobalCommands.SaveAndUpload: - return App.GoogleIdentity.LoggedIn && - (VrAssetService.m_Instance.UploadProgress <= 0.0f) && - IsCommandAvailable(GlobalCommands.UploadToGenericCloud); - case GlobalCommands.NewSketch: - return SketchHasChanges(); - case GlobalCommands.Credits: - case GlobalCommands.AshleysSketch: - return !SketchHasChanges() && !SketchMemoryScript.m_Instance.IsMemoryDirty(); - case GlobalCommands.Tiltasaurus: return TiltBrush.Tiltasaurus.m_Instance.TiltasaurusAvailable(); - case GlobalCommands.ExportRaw: - return SketchMemoryScript.m_Instance.HasVisibleObjects() || - m_WidgetManager.ModelWidgets.Any(w => w.gameObject.activeSelf) || - m_WidgetManager.ImageWidgets.Any(w => w.gameObject.activeSelf); - case GlobalCommands.ResetAllPanels: return m_PanelManager.PanelsHaveBeenCustomized(); - case GlobalCommands.Duplicate: return ClipboardManager.Instance.CanCopy; - case GlobalCommands.ToggleGroupStrokesAndWidgets: return SelectionManager.m_Instance.SelectionCanBeGrouped; - case GlobalCommands.SaveModel: return SelectionManager.m_Instance.HasSelection; - case GlobalCommands.SummonMirror: - return PointerManager.m_Instance.CurrentSymmetryMode != - SymmetryMode.None; - case GlobalCommands.InvertSelection: - case GlobalCommands.FlipSelection: - return SelectionManager.m_Instance.HasSelection; - case GlobalCommands.SelectAll: - return SketchMemoryScript.m_Instance.HasVisibleObjects() || - m_WidgetManager.HasSelectableWidgets(); - case GlobalCommands.UnloadReferenceImageCatalog: - return ReferenceImageCatalog.m_Instance.AnyImageValid(); - case GlobalCommands.ToggleCameraPathPreview: - return m_WidgetManager.CanRecordCurrentCameraPath(); - case GlobalCommands.DeleteCameraPath: - return CameraPathCaptureRig.Enabled && m_WidgetManager.AnyActivePathHasAKnot(); - case GlobalCommands.ToggleCameraPathVisuals: - return m_WidgetManager.AnyActivePathHasAKnot(); - case GlobalCommands.GoogleDriveSync: - return App.GoogleIdentity.LoggedIn; + case GlobalCommands.MultiplayerConnect: break; // Intentionally blank. + case GlobalCommands.MultiplayerDisconnect: break; // Intentionally blank. + default: + Debug.LogError($"Unrecognized command {rEnum}"); + break; + } + } + + private void LoadNamed(string path, bool quickload, bool additive) + { + var fileInfo = new DiskSceneFileInfo(path); + fileInfo.ReadMetadata(); + if (SaveLoadScript.m_Instance.LastMetadataError != null) + { + ControllerConsoleScript.m_Instance.AddNewLine( + string.Format("Error detected in sketch '{0}'.\nTry re-saving.", + fileInfo.HumanName)); + Debug.LogWarning(string.Format("Error reading metadata for {0}.\n{1}", + fileInfo.FullPath, SaveLoadScript.m_Instance.LastMetadataError)); + } + LoadSketch(fileInfo, quickload, additive); + if (m_ControlsType != ControlsType.ViewingOnly) + { + EatGazeObjectInput(); + } + } + + public void OpenURLAndInformUser(string url) + { + // On desktop - launch external browser and inform the user + // On mobile - the browser appears over the app + if (!App.Config.IsMobileHardware) + { + OutputWindowScript.m_Instance.CreateInfoCardAtController( + InputManager.ControllerName.Brush, + kRemoveHeadsetFyi, fPopScalar: 0.5f); + } + App.OpenURL(url); + EatGazeObjectInput(); + } + + public bool IsCommandActive(GlobalCommands rEnum, int iParam = -1) + { + switch (rEnum) + { + case GlobalCommands.StraightEdge: return PointerManager.m_Instance.StraightEdgeModeEnabled; + case GlobalCommands.StraightEdgeMeterDisplay: return PointerManager.m_Instance.StraightEdgeGuide.IsShowingMeter(); + case GlobalCommands.SymmetryPlane: return PointerManager.m_Instance.CurrentSymmetryMode == SymmetryMode.SinglePlane; + case GlobalCommands.MultiMirror: return PointerManager.m_Instance.CurrentSymmetryMode == SymmetryMode.MultiMirror; + case GlobalCommands.SymmetryTwoHanded: return PointerManager.m_Instance.CurrentSymmetryMode == SymmetryMode.TwoHanded; + case GlobalCommands.AutoOrient: return m_AutoOrientAfterRotation; + case GlobalCommands.AudioVisualization: return VisualizerManager.m_Instance.VisualsRequested; + case GlobalCommands.AdvancedPanelsToggle: return m_PanelManager.AdvancedModeActive(); + case GlobalCommands.Music: return VisualizerManager.m_Instance.VisualsRequested; + case GlobalCommands.DropCam: return m_DropCam.gameObject.activeSelf; + case GlobalCommands.ToggleAutosimplification: return QualityControls.AutosimplifyEnabled; + case GlobalCommands.DraftingVisibility: return m_DraftingVisibility == (DraftingVisibilityOption)iParam; + case GlobalCommands.Cameras: + return SketchSurfacePanel.m_Instance.GetCurrentToolType() == BaseTool.ToolType.AutoGif || + SketchSurfacePanel.m_Instance.GetCurrentToolType() == BaseTool.ToolType.ScreenshotTool; + case GlobalCommands.IRC: return m_IRCChatWidget != null; + case GlobalCommands.YouTubeChat: return m_YouTubeChatWidget != null; + case GlobalCommands.StencilsDisabled: return m_WidgetManager.StencilsDisabled; + case GlobalCommands.StraightEdgeShape: + // Previously experimental mode only. + // Untested and currently untriggerable. + return PointerManager.m_Instance.StraightEdgeGuide.TempShape == (StraightEdgeGuideScript.Shape)iParam || + (PointerManager.m_Instance.StraightEdgeGuide.TempShape == StraightEdgeGuideScript.Shape.None + && PointerManager.m_Instance.StraightEdgeGuide.CurrentShape == (StraightEdgeGuideScript.Shape)iParam); + case GlobalCommands.Disco: return LightsControlScript.m_Instance.DiscoMode; + case GlobalCommands.ToggleGroupStrokesAndWidgets: return SelectionManager.m_Instance.UngroupingAllowed; + case GlobalCommands.ToggleProfiling: return UnityEngine.Profiling.Profiler.enabled; + case GlobalCommands.ToggleCameraPostEffects: return CameraConfig.PostEffects; + case GlobalCommands.ToggleWatermark: return CameraConfig.Watermark; + case GlobalCommands.ToggleCameraPathVisuals: return m_WidgetManager.CameraPathsVisible; + case GlobalCommands.ToggleCameraPathPreview: return m_WidgetManager.FollowingPath; + case GlobalCommands.SelectCameraPath: + return m_WidgetManager.IsCameraPathAtIndexCurrent(iParam) && + m_WidgetManager.CameraPathsVisible; + case GlobalCommands.GoogleDriveSync_Folder: + return App.DriveSync.IsFolderOfTypeSynced((DriveSync.SyncedFolderType)iParam); + case GlobalCommands.GoogleDriveSync: return App.DriveSync.SyncEnabled; + case GlobalCommands.RecordCameraPath: return VideoRecorderUtils.ActiveVideoRecording != null; + } + return false; + } + + public void NewSketch(bool fade) + { + LightsControlScript.m_Instance.DiscoMode = false; + m_WidgetManager.FollowingPath = false; + SketchMemoryScript.m_Instance.ClearMemory(); + ControllerConsoleScript.m_Instance.AddNewLine("Sketch Cleared"); + ResetGrabbedPose(everything: true); + QualityControls.m_Instance.ResetAutoQuality(); + InputManager.m_Instance.TriggerHaptics(InputManager.ControllerName.Brush, 0.1f); + SaveLoadScript.m_Instance.ResetLastFilename(); + SelectionManager.m_Instance.RemoveFromSelection(false); + PointerManager.m_Instance.ResetSymmetryToHome(); + App.Scene.ResetLayers(notify: true); + ApiManager.Instance.ResetBrushTransform(); + + // If we've got the camera path tool active, switch back to the default tool. + // I'm doing this because if we leave the camera path tool active, the camera path + // panel shows the button highlighted, which affects the user's flow for being + // invited to start a path. It looks weird. + if (m_SketchSurfacePanel.ActiveToolType == BaseTool.ToolType.CameraPathTool) + { + m_SketchSurfacePanel.EnableDefaultTool(); + } + + m_WidgetManager.DestroyAllWidgets(); + if (LightsControlScript.m_Instance.LightsChanged || + SceneSettings.m_Instance.EnvironmentChanged) + { + SceneSettings.m_Instance.RecordSkyColorsForFading(); + SceneSettings.m_Instance.SetDesiredPreset( + SceneSettings.m_Instance.GetDesiredPreset(), skipFade: !fade); + } + // Blank the thumbnail position so that autosave won't save the thumbnail position to be + // the one from the old sketch. + SaveLoadScript.m_Instance.LastThumbnail_SS = null; + + // Re-set the quality level to reset simplification level + QualityControls.m_Instance.QualityLevel = QualityControls.m_Instance.QualityLevel; + + App.PolyAssetCatalog.ClearLoadingQueue(); + App.PolyAssetCatalog.UnloadUnusedModels(); + } + + private bool WorldIsReset(bool toSavedXf) + { + return App.Scene.Pose == + (toSavedXf ? SketchMemoryScript.m_Instance.InitialSketchTransform : TrTransform.identity); + } + + public bool IsCommandAvailable(GlobalCommands rEnum, int iParam = -1) + { + // TODO: hide gallery view / publish if there are no saved sketches + switch (rEnum) + { + case GlobalCommands.Undo: return SketchMemoryScript.m_Instance.CanUndo(); + case GlobalCommands.Redo: return SketchMemoryScript.m_Instance.CanRedo(); + case GlobalCommands.Save: + bool canSave = + SaveLoadScript.m_Instance.SceneFile.Valid && + SaveLoadScript.m_Instance.IsSavingAllowed(); + return canSave && (!WorldIsReset(toSavedXf: true) || + (SketchHasChanges() && SketchMemoryScript.m_Instance.IsMemoryDirty())); + case GlobalCommands.SaveOptions: + case GlobalCommands.SaveNew: + case GlobalCommands.SaveGallery: + return SketchHasChanges(); + case GlobalCommands.SaveOnLocalChanges: + if (!SaveLoadScript.m_Instance.SceneFile.Valid) + { + // No save file, but something has changed. + return SketchHasChanges(); + } + else + { + if (SaveLoadScript.m_Instance.CanOverwriteSource) + { + // Save file, and it's our file. Whether we have changes is irrelevant. + return true; + } + // Save file, but it's not our file. Only make a copy if there are local changes. + return SketchMemoryScript.m_Instance.IsMemoryDirty(); + } + case GlobalCommands.UploadToGenericCloud: + return SketchMemoryScript.m_Instance.HasVisibleObjects() || + m_WidgetManager.ExportableModelWidgets.Any(w => w.gameObject.activeSelf) || + m_WidgetManager.ImageWidgets.Any(w => w.gameObject.activeSelf) || + VrAssetService.m_Instance.UploadProgress >= 1.0f || + VrAssetService.m_Instance.LastUploadFailed; + case GlobalCommands.SaveAndUpload: + return App.GoogleIdentity.LoggedIn && + (VrAssetService.m_Instance.UploadProgress <= 0.0f) && + IsCommandAvailable(GlobalCommands.UploadToGenericCloud); + case GlobalCommands.NewSketch: + return SketchHasChanges(); + case GlobalCommands.Credits: + case GlobalCommands.AshleysSketch: + return !SketchHasChanges() && !SketchMemoryScript.m_Instance.IsMemoryDirty(); + case GlobalCommands.Tiltasaurus: return TiltBrush.Tiltasaurus.m_Instance.TiltasaurusAvailable(); + case GlobalCommands.ExportRaw: + return SketchMemoryScript.m_Instance.HasVisibleObjects() || + m_WidgetManager.ModelWidgets.Any(w => w.gameObject.activeSelf) || + m_WidgetManager.ImageWidgets.Any(w => w.gameObject.activeSelf); + case GlobalCommands.ResetAllPanels: return m_PanelManager.PanelsHaveBeenCustomized(); + case GlobalCommands.Duplicate: return ClipboardManager.Instance.CanCopy; + case GlobalCommands.ToggleGroupStrokesAndWidgets: return SelectionManager.m_Instance.SelectionCanBeGrouped; + case GlobalCommands.SaveModel: return SelectionManager.m_Instance.HasSelection; + case GlobalCommands.SummonMirror: + return PointerManager.m_Instance.CurrentSymmetryMode != + SymmetryMode.None; + case GlobalCommands.InvertSelection: + case GlobalCommands.FlipSelection: + return SelectionManager.m_Instance.HasSelection; + case GlobalCommands.SelectAll: + return SketchMemoryScript.m_Instance.HasVisibleObjects() || + m_WidgetManager.HasSelectableWidgets(); + case GlobalCommands.UnloadReferenceImageCatalog: + return ReferenceImageCatalog.m_Instance.AnyImageValid(); + case GlobalCommands.ToggleCameraPathPreview: + return m_WidgetManager.CanRecordCurrentCameraPath(); + case GlobalCommands.DeleteCameraPath: + return CameraPathCaptureRig.Enabled && m_WidgetManager.AnyActivePathHasAKnot(); + case GlobalCommands.ToggleCameraPathVisuals: + return m_WidgetManager.AnyActivePathHasAKnot(); + case GlobalCommands.GoogleDriveSync: + return App.GoogleIdentity.LoggedIn; case GlobalCommands.RecordCameraPath: - return m_WidgetManager.CameraPathsVisible; + return m_WidgetManager.CameraPathsVisible; case GlobalCommands.AdvancedPanelsToggle: - return !(MultiplayerManager.m_Instance.State == ConnectionState.IN_ROOM); + return !(MultiplayerManager.m_Instance.State == ConnectionState.IN_ROOM); case GlobalCommands.MultiplayerConnect: - return MultiplayerManager.m_Instance.IsConnectable(); + return MultiplayerManager.m_Instance.IsConnectable(); case GlobalCommands.MultiplayerDisconnect: return MultiplayerManager.m_Instance.IsDisconnectable(); case GlobalCommands.MultiplayerJoinRoom: - return !PanelManager.m_Instance.AdvancedModeActive() && MultiplayerManager.m_Instance.CanJoinRoom(); - case GlobalCommands.MultiplayerLeaveRoom: - return MultiplayerManager.m_Instance.CanLeaveRoom(); - } - return true; - } - - public bool SketchHasChanges() - { - if (SceneSettings.m_Instance.IsTransitioning) { return false; } - return SketchMemoryScript.m_Instance.HasVisibleObjects() || - SceneSettings.m_Instance.EnvironmentChanged || - LightsControlScript.m_Instance.LightsChanged || - m_WidgetManager.ModelWidgets.Any(w => w.gameObject.activeSelf) || - m_WidgetManager.LightWidgets.Any(w => w.gameObject.activeSelf) || - m_WidgetManager.StencilWidgets.Any(w => w.gameObject.activeSelf) || - m_WidgetManager.ImageWidgets.Any(w => w.gameObject.activeSelf) || - m_WidgetManager.VideoWidgets.Any(w => w.gameObject.activeSelf) || - m_WidgetManager.AnyCameraPathWidgetsActive; - } - - public void OpenPanelOfType(BasePanel.PanelType type, TrTransform trSpawnXf) - { - m_PanelManager.OpenPanel(type, trSpawnXf); - EatGazeObjectInput(); - } - - public void RestoreFloatingPanels() - { - if (!m_SketchSurfacePanel.ActiveTool.HidePanels()) - { - m_PanelManager.RestoreHiddenPanels(); - } - } - - public void UpdateDraftingVisibility() - { - float value = 0; - switch (m_DraftingVisibility) - { - case DraftingVisibilityOption.Visible: - value = 1; - break; - case DraftingVisibilityOption.Transparent: - value = .5f; - break; - case DraftingVisibilityOption.Hidden: - value = 0; - break; - } - Shader.SetGlobalFloat("_DraftingVisibility01", value); - } - - private void ToggleProfiling() - { - if (Debug.isDebugBuild && ProfileDisplay.Instance != null) - { - ProfileDisplay.Instance.gameObject.SetActive(UnityEngine.Profiling.Profiler.enabled); - } - if (UnityEngine.Profiling.Profiler.enabled) - { - ProfilingManager.Instance.StopProfiling(); - } - else - { - ProfilingManager.Instance.StartProfiling(App.UserConfig.Profiling.ProfilingMode); - } - } - - private void DoAutoProfile() - { - StartCoroutine(DoProfiling()); - } - - private void DoAutoProfileAndQuit() - { - StartCoroutine(DoProfiling(andQuit: true)); - } - - private IEnumerator DoProfiling(bool andQuit = false) - { - TrTransform oldWandPose = TrTransform.FromTransform(InputManager.Wand.Geometry.transform); - TrTransform oldBrushPose = TrTransform.FromTransform(InputManager.Brush.Geometry.transform); - - App.AppState oldState = App.CurrentState; - App.Instance.SetDesiredState(App.AppState.AutoProfiling); - while (App.CurrentState != App.AppState.AutoProfiling) - { - yield return null; - } - - TrTransform camPose = App.Scene.Pose * SaveLoadScript.m_Instance.ReasonableThumbnail_SS; - camPose.ToTransform(App.VrSdk.GetVrCamera().transform); - float controllerDirection = App.UserConfig.Profiling.ShowControllers ? 1f : -1f; - Vector3 roffset = Camera.main.transform.right * 2f; - Vector3 fOffset = Camera.main.transform.forward * 4f * controllerDirection; - InputManager.Brush.Geometry.transform.position = Camera.main.transform.position + roffset + fOffset; - InputManager.Brush.Geometry.transform.rotation = Camera.main.transform.rotation; - InputManager.Wand.Geometry.transform.position = Camera.main.transform.position - roffset + fOffset; - InputManager.Wand.Geometry.transform.rotation = Camera.main.transform.rotation; - m_PanelManager.LockPanelsToController(); - - ProfilingManager.Instance.StartProfiling(App.UserConfig.Profiling.ProfilingMode); - yield return new WaitForSeconds(App.UserConfig.Profiling.Duration); - ProfilingManager.Instance.StopProfiling(); - - if (App.UserConfig.Profiling.TakeScreenshot) - { - GameObject camObj = new GameObject("ScreenShotter"); - Camera cam = camObj.AddComponent(); - cam.CopyFrom(App.VrSdk.GetVrCamera()); - cam.stereoTargetEye = StereoTargetEyeMask.None; - cam.clearFlags = CameraClearFlags.SolidColor; - camPose.ToTransform(camObj.transform); - int res = App.UserConfig.Profiling.ScreenshotResolution; - RenderTexture renderTexture = RenderTexture.GetTemporary(res, res, 24); - try - { - cam.targetTexture = renderTexture; - cam.Render(); - RenderTexture prev = RenderTexture.active; - RenderTexture.active = renderTexture; - var texture = new Texture2D(res, res, TextureFormat.RGB24, false); - texture.ReadPixels(new Rect(0, 0, texture.width, texture.height), 0, 0); - RenderTexture.active = prev; - byte[] jpegBytes = texture.EncodeToJPG(); - string filename = - Path.GetFileNameWithoutExtension(SaveLoadScript.m_Instance.SceneFile.FullPath); - File.WriteAllBytes(Path.Combine(App.UserPath(), filename + ".jpg"), jpegBytes); - } - finally - { - Destroy(camObj); - RenderTexture.ReleaseTemporary(renderTexture); - } - } - - oldWandPose.ToTransform(InputManager.Wand.Geometry.transform); - oldBrushPose.ToTransform(InputManager.Brush.Geometry.transform); - App.Instance.SetDesiredState(oldState); - - if (andQuit) - { - QuitApp(); - } - } - } - -} // namespace TiltBrush + return !PanelManager.m_Instance.AdvancedModeActive() && MultiplayerManager.m_Instance.CanJoinRoom(); + case GlobalCommands.MultiplayerLeaveRoom: + return MultiplayerManager.m_Instance.CanLeaveRoom(); + } + return true; + } + + public bool SketchHasChanges() + { + if (SceneSettings.m_Instance.IsTransitioning) { return false; } + return SketchMemoryScript.m_Instance.HasVisibleObjects() || + SceneSettings.m_Instance.EnvironmentChanged || + LightsControlScript.m_Instance.LightsChanged || + m_WidgetManager.ModelWidgets.Any(w => w.gameObject.activeSelf) || + m_WidgetManager.LightWidgets.Any(w => w.gameObject.activeSelf) || + m_WidgetManager.StencilWidgets.Any(w => w.gameObject.activeSelf) || + m_WidgetManager.ImageWidgets.Any(w => w.gameObject.activeSelf) || + m_WidgetManager.VideoWidgets.Any(w => w.gameObject.activeSelf) || + m_WidgetManager.AnyCameraPathWidgetsActive; + } + + public void OpenPanelOfType(BasePanel.PanelType type, TrTransform trSpawnXf) + { + m_PanelManager.OpenPanel(type, trSpawnXf); + EatGazeObjectInput(); + } + + public void RestoreFloatingPanels() + { + if (!m_SketchSurfacePanel.ActiveTool.HidePanels()) + { + m_PanelManager.RestoreHiddenPanels(); + } + } + + public void UpdateDraftingVisibility() + { + float value = 0; + switch (m_DraftingVisibility) + { + case DraftingVisibilityOption.Visible: + value = 1; + break; + case DraftingVisibilityOption.Transparent: + value = .5f; + break; + case DraftingVisibilityOption.Hidden: + value = 0; + break; + } + Shader.SetGlobalFloat("_DraftingVisibility01", value); + } + + private void ToggleProfiling() + { + if (Debug.isDebugBuild && ProfileDisplay.Instance != null) + { + ProfileDisplay.Instance.gameObject.SetActive(UnityEngine.Profiling.Profiler.enabled); + } + if (UnityEngine.Profiling.Profiler.enabled) + { + ProfilingManager.Instance.StopProfiling(); + } + else + { + ProfilingManager.Instance.StartProfiling(App.UserConfig.Profiling.ProfilingMode); + } + } + + private void DoAutoProfile() + { + StartCoroutine(DoProfiling()); + } + + private void DoAutoProfileAndQuit() + { + StartCoroutine(DoProfiling(andQuit: true)); + } + + private IEnumerator DoProfiling(bool andQuit = false) + { + TrTransform oldWandPose = TrTransform.FromTransform(InputManager.Wand.Geometry.transform); + TrTransform oldBrushPose = TrTransform.FromTransform(InputManager.Brush.Geometry.transform); + + App.AppState oldState = App.CurrentState; + App.Instance.SetDesiredState(App.AppState.AutoProfiling); + while (App.CurrentState != App.AppState.AutoProfiling) + { + yield return null; + } + + TrTransform camPose = App.Scene.Pose * SaveLoadScript.m_Instance.ReasonableThumbnail_SS; + camPose.ToTransform(App.VrSdk.GetVrCamera().transform); + float controllerDirection = App.UserConfig.Profiling.ShowControllers ? 1f : -1f; + Vector3 roffset = Camera.main.transform.right * 2f; + Vector3 fOffset = Camera.main.transform.forward * 4f * controllerDirection; + InputManager.Brush.Geometry.transform.position = Camera.main.transform.position + roffset + fOffset; + InputManager.Brush.Geometry.transform.rotation = Camera.main.transform.rotation; + InputManager.Wand.Geometry.transform.position = Camera.main.transform.position - roffset + fOffset; + InputManager.Wand.Geometry.transform.rotation = Camera.main.transform.rotation; + m_PanelManager.LockPanelsToController(); + + ProfilingManager.Instance.StartProfiling(App.UserConfig.Profiling.ProfilingMode); + yield return new WaitForSeconds(App.UserConfig.Profiling.Duration); + ProfilingManager.Instance.StopProfiling(); + + if (App.UserConfig.Profiling.TakeScreenshot) + { + GameObject camObj = new GameObject("ScreenShotter"); + Camera cam = camObj.AddComponent(); + cam.CopyFrom(App.VrSdk.GetVrCamera()); + cam.stereoTargetEye = StereoTargetEyeMask.None; + cam.clearFlags = CameraClearFlags.SolidColor; + camPose.ToTransform(camObj.transform); + int res = App.UserConfig.Profiling.ScreenshotResolution; + RenderTexture renderTexture = RenderTexture.GetTemporary(res, res, 24); + try + { + cam.targetTexture = renderTexture; + cam.Render(); + RenderTexture prev = RenderTexture.active; + RenderTexture.active = renderTexture; + var texture = new Texture2D(res, res, TextureFormat.RGB24, false); + texture.ReadPixels(new Rect(0, 0, texture.width, texture.height), 0, 0); + RenderTexture.active = prev; + byte[] jpegBytes = texture.EncodeToJPG(); + string filename = + Path.GetFileNameWithoutExtension(SaveLoadScript.m_Instance.SceneFile.FullPath); + File.WriteAllBytes(Path.Combine(App.UserPath(), filename + ".jpg"), jpegBytes); + } + finally + { + Destroy(camObj); + RenderTexture.ReleaseTemporary(renderTexture); + } + } + + oldWandPose.ToTransform(InputManager.Wand.Geometry.transform); + oldBrushPose.ToTransform(InputManager.Brush.Geometry.transform); + App.Instance.SetDesiredState(oldState); + + if (andQuit) + { + QuitApp(); + } + } + } + +} // namespace TiltBrush From 69214870cd63ccfba46e13627d1f56a1e674d8f5 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Wed, 30 Oct 2024 16:18:07 +0000 Subject: [PATCH 065/174] Add Alerts/Errors to multiplayer UI --- Assets/Prefabs/Panels/MultiplayerPanel.prefab | 174 +++++++++++++++++- Assets/Scripts/GUI/MultiplayerPanel.cs | 69 +++++-- .../Multiplayer/Photon/PhotonManager.cs | 63 +++---- .../Multiplayer/Photon/PhotonVoiceManager.cs | 13 +- 4 files changed, 270 insertions(+), 49 deletions(-) diff --git a/Assets/Prefabs/Panels/MultiplayerPanel.prefab b/Assets/Prefabs/Panels/MultiplayerPanel.prefab index 0761b4d3c9..b029c16d79 100644 --- a/Assets/Prefabs/Panels/MultiplayerPanel.prefab +++ b/Assets/Prefabs/Panels/MultiplayerPanel.prefab @@ -114,7 +114,7 @@ MonoBehaviour: m_State: {fileID: 352620620088219705} m_RoomNumber: {fileID: 9160963822650286723} m_Nickname: {fileID: 4888819335275065630} - m_Alerts: {fileID: 0} + m_AlertsErrors: {fileID: 6437857310660163665} references: version: 2 RefIds: [] @@ -3527,6 +3527,7 @@ Transform: - {fileID: 4609499178869085959} - {fileID: 2677652558882906679} - {fileID: 8994233172856634412} + - {fileID: 5239577752120043582} m_Father: {fileID: 415082} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &6171746570159008301 @@ -4661,6 +4662,177 @@ MonoBehaviour: references: version: 2 RefIds: [] +--- !u!1 &7868523408258216887 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 5239577752120043582} + - component: {fileID: 8722975735802607274} + - component: {fileID: 6437857310660163665} + m_Layer: 16 + m_Name: AlertsErrors + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 0 +--- !u!224 &5239577752120043582 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7868523408258216887} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: -0.05} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 8346410928580794432} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0.088, y: 0.614} + m_SizeDelta: {x: 1.104868, y: 1.4460607} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!23 &8722975735802607274 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7868523408258216887} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, 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: 0 + 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!114 &6437857310660163665 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7868523408258216887} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9541d86e2fd84c1d9990edf0852d74ab, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_text: Allerts + m_isRightToLeft: 0 + m_fontAsset: {fileID: 11400000, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + m_sharedMaterial: {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + m_fontSharedMaterials: [] + m_fontMaterial: {fileID: 0} + m_fontMaterials: [] + m_fontColor32: + serializedVersion: 2 + rgba: 4278190335 + m_fontColor: {r: 1, g: 0, b: 0, a: 1} + m_enableVertexGradient: 0 + m_colorMode: 3 + m_fontColorGradient: + topLeft: {r: 1, g: 1, b: 1, a: 1} + topRight: {r: 1, g: 1, b: 1, a: 1} + bottomLeft: {r: 1, g: 1, b: 1, a: 1} + bottomRight: {r: 1, g: 1, b: 1, a: 1} + m_fontColorGradientPreset: {fileID: 0} + m_spriteAsset: {fileID: 0} + m_tintAllSprites: 0 + m_StyleSheet: {fileID: 0} + m_TextStyleHashCode: -1183493901 + m_overrideHtmlColors: 0 + m_faceColor: + serializedVersion: 2 + rgba: 4294967295 + m_fontSize: 1.5 + m_fontSizeBase: 1.5 + m_fontWeight: 400 + m_enableAutoSizing: 0 + m_fontSizeMin: 18 + m_fontSizeMax: 72 + m_fontStyle: 0 + m_HorizontalAlignment: 2 + m_VerticalAlignment: 1024 + m_textAlignment: 65535 + m_characterSpacing: 0 + m_wordSpacing: 0 + m_lineSpacing: -30 + m_lineSpacingMax: 0 + m_paragraphSpacing: 0 + m_charWidthMaxAdj: 0 + m_enableWordWrapping: 1 + m_wordWrappingRatios: 0.4 + m_overflowMode: 0 + m_linkedTextComponent: {fileID: 0} + parentLinkedComponent: {fileID: 0} + m_enableKerning: 1 + m_enableExtraPadding: 0 + checkPaddingRequired: 0 + m_isRichText: 1 + m_parseCtrlCharacters: 1 + m_isOrthographic: 0 + m_isCullingEnabled: 0 + m_horizontalMapping: 0 + m_verticalMapping: 0 + m_uvLineOffset: 0 + m_geometrySortingOrder: 0 + m_IsTextObjectScaleStatic: 0 + m_VertexBufferAutoSizeReduction: 0 + m_useMaxVisibleDescender: 1 + m_pageToDisplay: 1 + m_margin: {x: -0.31141979, y: -1.0333014, z: -0.1872099, w: 1.1764753} + m_isUsingLegacyAnimationComponent: 0 + m_isVolumetricText: 0 + _SortingLayer: 0 + _SortingLayerID: 0 + _SortingOrder: 0 + m_hasFontAssetChanged: 0 + m_renderer: {fileID: 8722975735802607274} + m_maskType: 0 --- !u!1 &8016613551395990211 GameObject: m_ObjectHideFlags: 0 diff --git a/Assets/Scripts/GUI/MultiplayerPanel.cs b/Assets/Scripts/GUI/MultiplayerPanel.cs index 0b21f3d20a..749e525e8b 100644 --- a/Assets/Scripts/GUI/MultiplayerPanel.cs +++ b/Assets/Scripts/GUI/MultiplayerPanel.cs @@ -13,6 +13,8 @@ // limitations under the License. using OpenBrush.Multiplayer; +using System; +using System.Collections.Generic; using TMPro; using UnityEngine; @@ -24,7 +26,7 @@ public class MultiplayerPanel : BasePanel [SerializeField] private TextMeshPro m_State; [SerializeField] private TextMeshPro m_RoomNumber; [SerializeField] private TextMeshPro m_Nickname; - [SerializeField] private TextMeshPro m_Alerts; + [SerializeField] private TextMeshPro m_AlertsErrors; public string RoomName { @@ -59,6 +61,8 @@ public string NickName private RoomCreateData data; + private List>> alertChecks; + public void Awake() { data = new RoomCreateData @@ -69,20 +73,20 @@ public void Awake() voiceDisabled = false }; + alertChecks = new List>> + { + CheckAdvancedModeActive, + CheckMultiplayerManagerErrors, + }; + if (MultiplayerManager.m_Instance != null) MultiplayerManager.m_Instance.StateUpdated += OnStateUpdated; - UpdateDisplay(); } - private void UserInBeginnerMode() + protected override void OnEnablePanel() { - if (m_Alerts) - { - PanelManager panelManager = PanelManager.m_Instance; - bool IsAdavancedModeActive = panelManager.AdvancedModeActive(); - Debug.Log(IsAdavancedModeActive); - m_Alerts.gameObject.SetActive(IsAdavancedModeActive); - } + base.OnEnablePanel(); + UpdateDisplay(); } private static string GenerateUniqueRoomName() @@ -102,11 +106,12 @@ private static string GenerateRandomRoomName() return random.Next(100000, 999999).ToString(); } + private void UpdateDisplay() { if (m_RoomNumber) m_RoomNumber.text = "RoomName: " + data.roomName; if (m_Nickname) m_Nickname.text = "Nickname: " + NickName; - + Alerts(); } private async void Connect() @@ -145,6 +150,48 @@ private async void Disconnect() private void OnStateUpdated(ConnectionState newState) { m_State.text = "State: " + newState.ToString(); + UpdateDisplay(); + } + + private Tuple CheckAdvancedModeActive() + { + bool isAdvancedModeActive = PanelManager.m_Instance.AdvancedModeActive(); + return Tuple.Create(isAdvancedModeActive, "Switch to beginner mode to Join Room"); + } + + private Tuple CheckMultiplayerManagerErrors() + { + + if (MultiplayerManager.m_Instance != null) + { + if (MultiplayerManager.m_Instance.State == ConnectionState.ERROR) + return Tuple.Create(true, MultiplayerManager.m_Instance.LastError); + } + + return Tuple.Create(false, ""); + + } + + private void Alerts() + { + if (m_AlertsErrors) + { + bool shouldShowAlert = false; + string alertMessage = ""; + + foreach (Func> check in alertChecks) + { + var (isTriggered, message) = check.Invoke(); + if (isTriggered) + { + shouldShowAlert = true; + alertMessage += message + "/n"; + break; + } + } + m_AlertsErrors.gameObject.GetComponent().text = alertMessage; + m_AlertsErrors.gameObject.SetActive(shouldShowAlert); + } } public void OnMultiplayerPanelButtonPressed(MultiplayerPanelButton button) diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs index 01d308424e..d29ee5dd14 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs @@ -41,7 +41,7 @@ public class PhotonManager : IDataConnectionHandler, INetworkRunnerCallbacks private AppSettings m_PhotonAppSettings; public event Action Disconnected; - + public ConnectionUserInfo UserInfo { get; set; } public ConnectionState State { get; private set; } public string LastError { get; private set; } @@ -77,12 +77,12 @@ public async Task Init() State = ConnectionState.ERROR; LastError = $"[PhotonManager] Failed to Initialize lobby: {ex.Message}"; ControllerConsoleScript.m_Instance.AddNewLine(LastError); - return false; + return false; } ControllerConsoleScript.m_Instance.AddNewLine("[PhotonManager] Runner Initialized"); State = ConnectionState.INITIALIZED; - return true; + return true; } public async Task Connect() @@ -112,7 +112,7 @@ public async Task JoinRoom(RoomCreateData roomCreateData) { if (m_Runner == null) Init(); - + State = ConnectionState.JOINING_ROOM; var args = new StartGameArgs() @@ -141,7 +141,7 @@ public async Task JoinRoom(RoomCreateData roomCreateData) } return result.Ok; - + } public async Task Disconnect() @@ -160,7 +160,7 @@ public async Task Disconnect() await m_Runner.Shutdown(forceShutdownProcedure: false); GameObject.Destroy(m_Runner.gameObject); - if(m_Runner.IsShutdown) + if (m_Runner.IsShutdown) { State = ConnectionState.DISCONNECTED; ControllerConsoleScript.m_Instance.AddNewLine("[PhotonManager] Left Room"); @@ -207,7 +207,7 @@ public void Update() } } -#region IConnectionHandler Methods + #region IConnectionHandler Methods public async Task PerformCommand(BaseCommand command) { await Task.Yield(); @@ -234,13 +234,13 @@ public async Task RpcSyncToSharedAnchor(string uuid) await Task.Yield(); return true; } -#endregion + #endregion -#region Command Methods + #region Command Methods private bool ProcessCommand(BaseCommand command) { bool success = true; - switch(command) + switch (command) { case BrushStrokeCommand: success = CommandBrushStroke(command as BrushStrokeCommand); @@ -260,9 +260,9 @@ private bool ProcessCommand(BaseCommand command) break; } - if(command.ChildrenCount > 0) + if (command.ChildrenCount > 0) { - foreach(var child in command.Children) + foreach (var child in command.Children) { success &= ProcessCommand(child); } @@ -298,12 +298,12 @@ private bool CommandBrushStroke(BrushStrokeCommand command) // Middle for (int rounds = 1; rounds < numSplits + 1; ++rounds) { - var controlPoints = stroke.m_ControlPoints.Skip(rounds* maxPointsPerChunk).Take(maxPointsPerChunk).ToArray(); - var dropPoints = stroke.m_ControlPointsToDrop.Skip(rounds* maxPointsPerChunk).Take(maxPointsPerChunk).ToArray(); + var controlPoints = stroke.m_ControlPoints.Skip(rounds * maxPointsPerChunk).Take(maxPointsPerChunk).ToArray(); + var dropPoints = stroke.m_ControlPointsToDrop.Skip(rounds * maxPointsPerChunk).Take(maxPointsPerChunk).ToArray(); var netControlPoints = new NetworkedControlPoint[controlPoints.Length]; - for (int point = 0; point < controlPoints.Length; ++ point) + for (int point = 0; point < controlPoints.Length; ++point) { netControlPoints[point] = new NetworkedControlPoint().Init(controlPoints[point]); } @@ -342,7 +342,7 @@ private bool CommandSwitchEnvironment(SwitchEnvironmentCommand command) } #endregion -#region Photon Callbacks + #region Photon Callbacks public void OnConnectedToServer(NetworkRunner runner) { var rpc = m_Runner.gameObject.AddComponent(); @@ -357,18 +357,18 @@ 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); + { + 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(player.PlayerId, m_LocalPlayer); - } - else - { - m_PlayersSpawning.Add(player); - } + m_Manager.localPlayerJoined?.Invoke(player.PlayerId, m_LocalPlayer); + } + else + { + m_PlayersSpawning.Add(player); + } } catch (Exception ex) { @@ -399,10 +399,11 @@ public void OnSessionListUpdated(NetworkRunner runner, List session m_Manager.roomDataRefreshed?.Invoke(roomData); } -#endregion + #endregion -#region Unused Photon Callbacks - public void OnShutdown(NetworkRunner runner, ShutdownReason shutdownReason) { + #region Unused Photon Callbacks + public void OnShutdown(NetworkRunner runner, ShutdownReason shutdownReason) + { Disconnected?.Invoke(); } public void OnDisconnectedFromServer(NetworkRunner runner) { } @@ -416,7 +417,7 @@ public void OnHostMigration(NetworkRunner runner, HostMigrationToken hostMigrati public void OnReliableDataReceived(NetworkRunner runner, PlayerRef player, ArraySegment data) { } public void OnSceneLoadDone(NetworkRunner runner) { } public void OnSceneLoadStart(NetworkRunner runner) { } -#endregion + #endregion } } diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonVoiceManager.cs b/Assets/Scripts/Multiplayer/Photon/PhotonVoiceManager.cs index 0c3b016d72..e231dcc6e1 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonVoiceManager.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonVoiceManager.cs @@ -54,7 +54,7 @@ public async Task Init() m_VoiceConnection.Settings = new AppSettings { AppIdVoice = App.Config.PhotonVoiceSecrets.ClientId, - FixedRegion = "", + FixedRegion = "", }; m_VoiceConnection.Client.AddCallbackTarget(this); @@ -115,11 +115,11 @@ public async Task JoinRoom(RoomCreateData RoomData) bool connected = await Connect(); if (!connected) { - return false; + return false; } } - var RoomParameters = new EnterRoomParams{RoomName = RoomData.roomName}; + var RoomParameters = new EnterRoomParams { RoomName = RoomData.roomName }; bool roomJoined = m_VoiceConnection.Client.OpJoinOrCreateRoom(RoomParameters); if (roomJoined) @@ -141,8 +141,8 @@ public async Task LeaveRoom(bool force) { State = ConnectionState.DISCONNECTING; - if (!m_VoiceConnection.Client.InRoom) return false; - + if (!m_VoiceConnection.Client.InRoom) return false; + bool leftRoom = m_VoiceConnection.Client.OpLeaveRoom(false); if (!leftRoom) @@ -172,7 +172,8 @@ public async Task LeaveRoom(bool force) } } - public async Task Disconnect() { + public async Task Disconnect() + { State = ConnectionState.DISCONNECTING; From 21d659d6816605b6d073e42dc6c34566e16d29a8 Mon Sep 17 00:00:00 2001 From: Mike Miller Date: Thu, 31 Oct 2024 10:43:18 +0200 Subject: [PATCH 066/174] dos2unix --- .github/FUNDING.yml | 6 +- .github/dependabot.yml | 30 +- .github/workflows/build.yml | 2626 ++++++++--------- .github/workflows/delete_branch_cache.yml | 80 +- .github/workflows/export_secrets.yml | 66 +- .github/workflows/generate_certs.yml | 94 +- .github/workflows/get_license.yml | 44 +- .github/workflows/ios_setup.yml | 72 +- .github/workflows/pre-commit.yml | 78 +- .github/workflows/test_unity_credentials.yml | 44 +- .github/workflows/third_party_notices.yml | 38 +- .pre-commit-config.yaml | 96 +- .../Multiplayer/Photon/PhotonManager.cs | 28 +- .../Multiplayer/Photon/PhotonVoiceManager.cs | 28 +- 14 files changed, 1665 insertions(+), 1665 deletions(-) diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 971753fd20..681f2ee1d8 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,3 +1,3 @@ ---- -github: icosa-foundation -open_collective: icosa +--- +github: icosa-foundation +open_collective: icosa diff --git a/.github/dependabot.yml b/.github/dependabot.yml index e3edcc6248..6dd25a79a2 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,15 +1,15 @@ ---- -version: 2 -updates: - - package-ecosystem: "github-actions" - directory: "/" - schedule: - interval: "daily" - reviewers: - - "mikeage" - assignees: - - "mikeage" - groups: - all-actions-updates: - patterns: - - "**" +--- +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" + reviewers: + - "mikeage" + assignees: + - "mikeage" + groups: + all-actions-updates: + patterns: + - "**" diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 43c82828ee..617e8cc0aa 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,1313 +1,1313 @@ ---- -# yamllint disable rule:line-length -# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json -name: Builds - -on: # yamllint disable-line rule:truthy - pull_request: {} - push: - branches: - - "**" - tags: - - "v*" - schedule: - - cron: "34 19 * * sat" # Weekly, Saturday at 19:34 UTC - -env: - UNITY_VERSION: "2022.3.34f1" - PHOTON_PAT: ${{ secrets.PHOTON_PAT }} # This needs to be here, since you can't use a ${{ secrets }} within an if: condition -jobs: - configuration: - if: | - (github.event_name == 'schedule') || - (github.event_name == 'pull_request') || - ( - github.event_name == 'push' && - ( - github.ref == 'refs/heads/main' || - contains(github.ref, 'refs/tags/v') || - contains(github.event.head_commit.message, '[CI BUILD]') - ) - ) - name: Configure Build Parameters - runs-on: ubuntu-latest - outputs: - version: ${{ steps.version.outputs.version}} - androidVersionCode: ${{ steps.version.outputs.androidVersionCode }} - stamp: ${{ steps.version.outputs.stamp }} - prerelease: ${{ steps.version.outputs.prerelease }} - previousrelease: ${{ steps.rawchangelogdata.outputs.previousrelease }} - previousfullrelease: ${{ steps.rawchangelogdata.outputs.previousfullrelease }} - currentrelease: ${{ steps.rawchangelogdata.outputs.currentrelease }} - rawchangelog: ${{ steps.rawchangelogdata.outputs.rawchangelog}} - basename: ${{ steps.github.outputs.basename }} - description: ${{ steps.github.outputs.description}} - itchchannelname: ${{ steps.version.outputs.itchchannelname }} - fastlanelane: ${{ steps.version.outputs.fastlanelane}} - uid: ${{ steps.github.outputs.uid }} - gid: ${{ steps.github.outputs.gid }} - flavors: ${{ steps.flavors.outputs.flavors }} - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - fetch-depth: 0 - lfs: true # We don't use LFS, but it adds no time, and leave it here in case we do at some point later - - - name: Calculate version and stamp - id: version - run: | - # General note: for pull requests, we query github.event.pull_request.head.sha rather than the default sha, which is a merge commit of the target branch into the head. For pushes or tag commits, there's no additional commits made by the CI, so we can use the default, current, reference - - # Get the first two numbers from the last tag, including a tag on the current commit (to handle the case of a formal build) - MAJOR_MINOR=$(git describe --tags --abbrev=0 --match "v[0-9]*.[0-9]*" ${{ github.event.pull_request.head.sha }}) - - # How many commits have been made since the last tag of the form vX.Y. - # - # We used to use this version, however, it couldn't handle these two cases at the same time: - # (v2.1) - # | - # /-c2..c4..c5-\ - # / \ - # c...c0..c1....c3.........m6..c7....c10.c11.....m13...c14 <- [main] - # ^ \ / - # (v2.0) \-c8..c9..c12-/ - # If we use --first-parent, it wouldn't find a tag that was not a first parent, and so it'll think we're now in 2.0.8, though it skips the commits on the branches. If we did not use --first-parent, it gets the proper tag (v2.1), but counts each commit in the feature branch, and gives 2.1.10. While we almost always squash, if we ever do have an explicit merge commit, we don't want to count the commits on the feature branch. In this case, we actually want to get 2.1.7 (commits c3, m6, c7, c10, c11, m13, and c14). - ######## OLD CODE ######## - # # If the value is not equal to zero, git describe will give us a version in the form vX.Y-Z-gAAAAAAA, where Z is the count. If the current commit has a vX.Y tag, it'll just return that, so the 'cut' does nothing. We test for this below - # PATCH_VERSION=$(git describe --tags --match "v[0-9]*.[0-9]*" --first-parent ${{ github.event.pull_request.head.sha }} | cut -d'-' -f2) - ######## END OLD CODE ######## - - # Instead, we'll find the last tag, wherever it is, and then count the --first-parent commits "since" then (i.e., not included; they might be historically behind it) - CLOSEST_TAG=$(git describe --tags --match "v[0-9]*.[0-9]*" --abbrev=0 HEAD) - PATCH_VERSION=$(git log ${CLOSEST_TAG}.. --oneline --first-parent | wc -l) - - if [ $PATCH_VERSION == "0" ] - then - STAMP="" - echo "prerelease=false" >> $GITHUB_OUTPUT - echo "itchchannelname=release" >> $GITHUB_OUTPUT - echo "fastlanelane=beta" >> $GITHUB_OUTPUT - - else - # This is the first 7 characters of the commit; we do it this way rather than via rev-parse to avoid an 'if' conditional depending on whether it's a PR or push. (unlike git describe, git rev-parse doesn't default to the current HEAD) - STAMP=$(git describe --tags --match "v[0-9]*.[0-9]*" ${{ github.event.pull_request.head.sha }} | cut -d'-' -f3) - echo "prerelease=true" >> $GITHUB_OUTPUT - echo "itchchannelname=beta" >> $GITHUB_OUTPUT - echo "fastlanelane=beta" >> $GITHUB_OUTPUT - fi - VERSION=$(echo "$MAJOR_MINOR.$PATCH_VERSION" | sed -e 's/^v//') - echo "version=$VERSION" >> $GITHUB_OUTPUT - echo "stamp=$STAMP" >> $GITHUB_OUTPUT - MAJOR=$(echo $VERSION | cut -d '.' -f 1) - MINOR=$(echo $VERSION | cut -d '.' -f 2) - ANDROID_VERSION_CODE=$((MAJOR * 1000000 + MINOR * 1000 + PATCH_VERSION)) - echo "androidVersionCode=$ANDROID_VERSION_CODE" >> $GITHUB_OUTPUT - echo "Version $VERSION stamp=$STAMP androidVersionCode=$ANDROID_VERSION_CODE" - - name: Calculate Release tags for Changelog and raw changelog - id: rawchangelogdata - env: - PRERELEASE: ${{ steps.version.outputs.prerelease }} - VERSION: ${{ steps.version.outputs.version }} - run: | - if [ "$PRERELEASE" == "true" ] - then - PREV=$(git describe --tags --abbrev=0 HEAD^) - else - PREV=$(git describe --tags --match "v[0-9]*.[0-9]*" --first-parent --abbrev=0 HEAD^) - fi - PREVFULL=$(git describe --tags --match "v[0-9]*.[0-9]*" --first-parent --abbrev=0 HEAD^) - CUR="$(git rev-parse HEAD)" - echo "previousrelease=$PREV" >> $GITHUB_OUTPUT - echo "previousfullrelease=$PREVFULL" >> $GITHUB_OUTPUT - echo "currentrelease=$CUR" >> $GITHUB_OUTPUT - LAST_TAG=$(git describe --tags --match 'v[0-9]*.[0-9]*' --abbrev=0 HEAD^) - RAW_CHANGELOG=$(echo "$(git log --first-parent ${LAST_TAG}.. --pretty=format:'%D-g%h: %s' | sed -e 's/tag: //' -e 's/HEAD -> main, //')" | sed -e "s/origin\/main/$VERSION/" | tac) - echo "rawchangelog=${RAW_CHANGELOG//$'\n'/'\n'}" >> $GITHUB_OUTPUT - - - name: Echo Changelog (for debugging purposes) - env: - CHANGELOG: ${{ steps.rawchangelogdata.outputs.rawchangelog}} - run: | - echo "CHANGELOG=$CHANGELOG" - - - name: Set custom app name and package name, if relevant - id: github - env: - PRERELEASE: ${{ steps.version.outputs.prerelease }} - run: | - # For a PR action (i.e., synchronize / open), the value of github.ref will be refs/pull/1234/merge - # For a push action, it will be either refs/heads/foo_branch_name OR refs/tags/v1234. - # We want to use the base name for pushes of tags or to main, the PR number for PRs, and the branch name for named branches. - if [[ "$PRERELEASE" == "false" || ${{ github.ref }} == refs/heads/main ]] - then - echo "basename=OpenBrush" >> $GITHUB_OUTPUT - echo "description=" >> $GITHUB_OUTPUT - else - if [[ ${{ github.ref }} == refs/pull/* ]] - then - DESCRIPTION="PR#$(echo ${{ github.ref }} | sed -e 's#refs/pull/##' -e 's#/merge##')" - elif [[ ${{ github.ref }} == refs/heads/* ]] - then - DESCRIPTION="$(echo ${{ github.ref }} | sed -e 's#refs/heads/##')" - else - DESCRIPTION="Unknown" - fi - echo "description=-btb-description ${DESCRIPTION}" >> $GITHUB_OUTPUT - IDENTIFIER=$(echo ${DESCRIPTION} | sed -e 's/[\/#_-]//g') - echo "basename=OpenBrush-${IDENTIFIER}" >> $GITHUB_OUTPUT - fi - echo "uid=$(id -u)" >> $GITHUB_OUTPUT - echo "gid=$(id -g)" >> $GITHUB_OUTPUT - - - name: Determine whether to build Development builds or not - id: flavors - run: | - set -x - if [[ $(git log --format=%B ${{ github.event.pull_request.head.sha }} -1) == *'[CI BUILD DEV]'* ]] - then - echo 'flavors=[{"development": true, "title": "Development"}, {"development": false}]' >> $GITHUB_OUTPUT - - else - echo 'flavors=[{"development": false}]' >> $GITHUB_OUTPUT - fi - - build: - name: ${{ matrix.name }} ${{ matrix.flavors.title }} - needs: configuration - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - flavors: ${{ fromJson(needs.configuration.outputs.flavors) }} - name: [Windows OpenXR, Windows Pimax, Windows Rift, Linux, MacOS, Android OpenXR, Oculus Quest (1), Oculus Quest (2+), Android Pico, Android Pico (CN), iOS Zapbox] # These will all be overwritten, but because we have the flavors matrix as well, we can't just add configurations via include; they'll overwrite each other. This way ensures that we get each one - include: - - name: Windows OpenXR - targetPlatform: StandaloneWindows64 - vrsdk: OpenXR - cache: Windows - - - name: Windows Pimax - targetPlatform: StandaloneWindows64 - vrsdk: OpenXR - cache: Windows - extra_defines: PIMAX_SUPPORTED - - - name: Windows Rift - targetPlatform: StandaloneWindows64 - vrsdk: Oculus - cache: Windows - extra_defines: OCULUS_SUPPORTED - - - name: Linux - targetPlatform: StandaloneLinux64 - vrsdk: Monoscopic # All builds include monoscopic, but this one has no additional XrSdk, so we'll keep the name monoscopic - cache: Linux - - - name: MacOS - targetPlatform: StandaloneOSX - vrsdk: Monoscopic - cache: MacOS - packages_to_remove: com.meta.xr.sdk.core - - - name: Android OpenXR - targetPlatform: Android - vrsdk: OpenXR - cache: Android_Vulkan - extraoptions: -btb-il2cpp - versionSuffix: 0 - - - name: Oculus Quest (1) - targetPlatform: Android - vrsdk: OpenXR - cache: Android_Vulkan - extraoptions: -btb-il2cpp - versionSuffix: 0 - extra_defines: USE_QUEST_PACKAGE_NAME FORCE_QUEST_SUPPORT_DEVICE FORCE_FOCUSAWARE FORCE_HEADTRACKING - packages_to_remove: com.meta.xr.sdk.platform com.meta.xr.sdk.utilities - - - name: Oculus Quest (2+) - targetPlatform: Android - vrsdk: Oculus - cache: Android_Vulkan - extraoptions: -btb-il2cpp - versionSuffix: 1 - extra_defines: OCULUS_SUPPORTED USE_QUEST_PACKAGE_NAME - - - name: Android Pico - targetPlatform: Android - vrsdk: Pico - cache: Android_GLES - extraoptions: -btb-il2cpp - versionSuffix: 0 - extra_defines: PICO_SUPPORTED - - - name: Android Pico (CN) - targetPlatform: Android - vrsdk: Pico - cache: Android_GLES - # Pico requested Chinese build that doesn't have google/sketchfab login. - extraoptions: -btb-il2cpp -btb-disableAccountLogins - versionSuffix: 1 - extra_defines: PICO_SUPPORTED - - - name: iOS Zapbox - targetPlatform: iOS - vrsdk: Zapbox - cache: iOS - extraoptions: -btb-il2cpp - extra_defines: ZAPBOX_SUPPORTED - packages_to_remove: com.unity.formats.usd - - steps: - - name: Set masking - run: echo "::add-mask::DoNotStealThis1" - - name: Free extra space - # As of 02/08/2024, this increases free space from 21GB to 47GB - run: | - echo "Initial free space" - df -h / - echo "Removing all pre-loaded docker images" - docker rmi $(docker image ls -aq) # Removes ~3GB - df -h / - echo "Listing 100 largest packages" - dpkg-query -Wf '${Installed-Size}\t${Package}\n' | sort -rn | head -n 100 - echo "Removing unneeded large packages" - sudo apt update - sudo apt remove -y '^ghc-.*' '^dotnet-.*' azure-cli powershell google-chrome-stable firefox microsoft-edge-stable 'mongodb-*' 'mysql-*' 'mariadb-*' 'temurin-*' 'openjdk-*' default-jre-headless mono-devel libgl1-mesa-dri # Removes ~6GB - sudo apt autoremove -y - sudo apt clean - df -h / - echo "Removing Android" - sudo rm -rf /usr/local/lib/android # Removes ~9GB - df -h / - echo "Removing remaining large directories" - rm -rf /usr/share/dotnet/ # Removes ~1GB - rm -rf "$AGENT_TOOLSDIRECTORY" # Removes ~7GB - echo "Disk space after cleanup" - df -h / - - - name: Checkout repository - uses: actions/checkout@v4 - with: - lfs: true # We don't use LFS, but it adds no time, and leave it here in case we do at some point later - - - name: Install Pimax unity package - if: startsWith(matrix.name, 'Windows Pimax') - run: | - # version 0.6.3 - # Same as above, but adapted to work for Pimax instead. - wget -q https://dl.appstore.pimax.com/sdk/Pimax_Platform_Unity_SDK_v0.6.3.zip -O package.zip - unzip package.zip - mkdir tmp - tar -C tmp -xzf PimaxPlatform_v0.6.3.unitypackage - find tmp -type f | xargs chmod a-x - for pn in tmp/*/pathname; do - id=${pn%/*} - id=${id#*/} - p=$(head -1 $pn) - d=${p%/*} - mkdir -p "tmp/$d" - [ -f "tmp/$id/asset" ] && cp -v "tmp/$id/asset" "tmp/$p" - cp "tmp/$id/asset.meta" "tmp/${p}.meta" - done - cp -R tmp/Assets/Pimax Assets/ - rm -rf tmp package.zip PimaxPlatform_v0.6.3.unitypackage Tools - - - name: Install Pico unity package - if: matrix.vrsdk == 'Pico' - run: | - # version 2.1.1 - 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: | - # version 3.0.6; must be updated if the version changes - # This replaces the GUI's "Window -> TextMesh Pro -> Import TMP Essential Resources". I don't know why Unity makes this sort of thing so hard! - mkdir tmp.plugin - wget -q https://download.packages.unity.com/com.unity.textmeshpro/-/com.unity.textmeshpro-3.0.6.tgz -O tmp.plugin/plugin.tgz - tar -C tmp.plugin -xzf tmp.plugin/plugin.tgz - mkdir tmp.package - tar -C tmp.package -xzf 'tmp.plugin/package/Package Resources/TMP Essential Resources.unitypackage' - for pn in tmp.package/*/pathname; do - id=${pn%/*} - id=${id#*/} - p=$(head -1 $pn) - d=${p%/*} - mkdir -p "tmp.package/$d" - [ -f "tmp.package/$id/asset" ] && cp -v "tmp.package/$id/asset" "tmp.package/$p" - cp "tmp.package/$id/asset.meta" "tmp.package/${p}.meta" - done - mkdir -p 'Assets/TextMesh Pro' - 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 - ref: Fusion_v1.1.10_Voice_2 - 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@v4 - env: - SEGMENT_DOWNLOAD_TIMEOUT_MINS: 10 - with: - path: Library - # Some platforms share a cache; it's not a 1:1 mapping of either targetPlatform or vrsdk, so we have a distinct variable for which cache to use - key: Library_${{ matrix.cache }}_${{ env.UNITY_VERSION }} - - - name: Restore Library/PackageCache - id: cache_packagecache - uses: actions/cache/restore@v4 - env: - SEGMENT_DOWNLOAD_TIMEOUT_MINS: 10 - with: - path: Library/PackageCache - key: Library_PackageCache_${{ env.UNITY_VERSION }}_${{ hashFiles('Packages/packages-lock.json') }} - restore-keys: | - Library_PackageCache_${{ env.UNITY_VERSION }} - Library_PackageCache - - - name: Remove problematic packages - if: ${{ matrix.packages_to_remove }} - run: | - cp Packages/manifest.json{,.bak} - cp Packages/packages-lock.json{,.bak} - for PACKAGE in ${{ matrix.packages_to_remove }}; do - cat Packages/manifest.json | jq 'del( .dependencies ["'${PACKAGE}'"] )' > Packages/manifest.json.new - mv Packages/manifest.json.new Packages/manifest.json - cat Packages/packages-lock.json | jq 'del( .dependencies ["'${PACKAGE}'"] )' > Packages/packages-lock.json.new - mv Packages/packages-lock.json.new Packages/packages-lock.json - done - diff -u Packages/manifest.json.bak Packages/manifest.json || true - diff -u Packages/packages-lock.json.bak Packages/packages-lock.json || true - - - name: Set output filename - env: - BASENAME: ${{ needs.configuration.outputs.basename }} - run: | - if [[ "${{ matrix.targetPlatform}}" == "StandaloneWindows64" ]]; then - echo "filename=$BASENAME.exe" >> $GITHUB_ENV - elif [[ "${{ matrix.targetPlatform}}" == "StandaloneLinux64" ]]; then - echo "filename=$BASENAME" >> $GITHUB_ENV - elif [[ "${{ matrix.targetPlatform}}" == "iOS" ]]; then - echo "filename=$BASENAME" >> $GITHUB_ENV - elif [[ "${{ matrix.targetPlatform}}" == "StandaloneOSX" ]]; then - echo "filename=$BASENAME.app" >> $GITHUB_ENV - elif [[ "${{ matrix.targetPlatform}}" == "Android" ]]; then - echo "filename=com.Icosa.$BASENAME.apk" >> $GITHUB_ENV - fi - - - name: Set build stamp - if: ${{ needs.configuration.outputs.stamp }} - # We checkout the merge commit, but for the purpose of the tag, use the version from the PR, not the merge commit, which is rather hard to find later. We skip the version tag, since this comes from the code and can't be easily overwritten - run: | - echo "stamp=-btb-stamp ${{needs.configuration.outputs.stamp}}" >> $GITHUB_ENV - - - name: Enable Development Mode - if: ${{ matrix.flavors.development == true }} - run: | - echo "btbbopts=-btb-bopt Development" >> $GITHUB_ENV - - - name: Update version - env: - VERSION: ${{ needs.configuration.outputs.version}} - run: | - sed -e "s/m_VersionNumber:.*$/m_VersionNumber: $VERSION/" -i Assets/Scenes/Main.unity - sed -e "s/bundleVersion:.*$/bundleVersion: $VERSION/" -i ProjectSettings/ProjectSettings.asset - - - name: Add secure secrets file - env: - SECRETS_ASSET: ${{ secrets.SECRETS_ASSET }} - SECRETS_ASSET_META: ${{ secrets.SECRETS_ASSET_META }} - if: | - env.SECRETS_ASSET != null && - env.SECRETS_ASSET_META != null - run: | - echo "$SECRETS_ASSET" > Assets/Secrets.asset - echo "$SECRETS_ASSET_META" > Assets/Secrets.asset.meta - SECRETS_ASSET_META_GUID=$(grep "guid:" Assets/Secrets.asset.meta | cut -d" " -f2) - sed -e "s/Secrets:.*$/Secrets: {fileID: 11400000, guid: $SECRETS_ASSET_META_GUID, type: 2}/" -i Assets/Scenes/Main.unity - - - name: Enable keystore - run: | - sed -e 's/androidUseCustomKeystore.*$/androidUseCustomKeystore: 1/' -i ProjectSettings/ProjectSettings.asset - - - name: Add PHOTON_PAT specific define - if: ${{ env.PHOTON_PAT }} - run: | - echo -e "\n -define:PHOTON_UNITY_NETWORKING \n-define:PUN_2_0_OR_NEWER \n-define:PUN_2_OR_NEWER \n-define:PUN_2_19_OR_NEWER \n-define:FUSION_WEAVER \n-define:CROSS_PLATFORM_INPUT \n-define:MOBILE_INPUT \n-define:PHOTON_VOICE_DEFINED" | tee -a Assets/csc.rsp - - - name: Update build matrix specific defines in csc.rsp - if: ${{ matrix.extra_defines }} - run: | - for DEFINE in ${{ matrix.extra_defines }}; do - echo -e "\n-define:$DEFINE" | tee -a Assets/csc.rsp - done - - - name: Build project - uses: Wandalen/wretry.action@v3 - env: - VERSION: ${{ needs.configuration.outputs.version}} - UNITY_EMAIL: ${{ fromJSON(format('["unitytest@mikeage.net", "{0}"]', vars.UNITY_EMAIL))[secrets.UNITY_SERIAL != null] }} - UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }} - 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] }} - with: - retry_condition: steps._this.outputs.engineExitCode == 1 - action: game-ci/unity-builder@v4 - with: | - allowDirtyBuild: true # Because of the OVR Update, the build tree might be dirty - unityVersion: ${{ env.UNITY_VERSION }} - targetPlatform: ${{ matrix.targetPlatform }} - customParameters: -btb-target ${{ matrix.targetPlatform }} -btb-display ${{ matrix.vrsdk }} -btb-out /github/workspace/build/${{ matrix.vrsdk }}/${{ matrix.targetPlatform }}/${{ env.filename }} ${{ needs.configuration.outputs.description}} ${{ env.stamp }} ${{ env.btbbopts }} ${{ matrix.extraoptions }} - versioning: Custom - androidVersionCode: "${{ needs.configuration.outputs.androidVersionCode }}${{ matrix.versionSuffix }}" - version: ${{ needs.configuration.outputs.version }} - buildName: ${{ needs.configuration.outputs.basename }} - buildsPath: build/${{ matrix.vrsdk }} - chownFilesTo: ${{ needs.configuration.outputs.uid }}:${{ needs.configuration.outputs.gid }} - buildMethod: BuildTiltBrush.CommandLine - androidKeystoreName: openbrush.keystore - androidKeystoreBase64: ${{ secrets.ANDROID_KEYSTORE_BASE64 || '/u3+7QAAAAIAAAABAAAAAQAWb3BlbmJydXNoLW5vbi1vZmZpY2lhbAAAAX66M2FtAAAFATCCBP0wDgYKKwYBBAEqAhEBAQUABIIE6Wufa9OVstw7Bu/gdATKqoPafXGefygChsN1d4LGY0SMLPORjHXiryEVMKi2rt61kNeXzeLkiM4yIQAam4HZtNTxgjoFQ6KB7uzkqMJYKViBUgg1HCAl2e+QpYjqG+YNJT67CiPgjpsJHNE628CwKAvjJ85FhqFz+MKzNF8BOpS5g5waqFda67oxaE4qO8eAL+F9P7us+ziY5B4O3EJC9s7xpT2GV2ro0m0fZI2dr3OO9UdUO72CYTg5qs250JiSij26Haf4t8Vq28F2S8rTcMUVtN4FRtzeR/wjeeZ3laER+WoxYni4MrZEXhYYCGhfor8Zcfi3p5ka8TJCQxywTKpghpSwgykgMJLn1HksxB0vhIMGTb87c2CTqS4t5Js/OPdcYS4Jnr7mHdQtOGfJCvl3TJC7NJwzLLOzUTmVIogaZCA9GlRballbD7XYbR8mcPxs+jLq5HJJk8/3B8ojAz/YA9vp6ml3RSYDA+yv9fBIefxNniAredJeqAnmH4o9er3+n0rKmpoqiXdzFkp1ywYbDDxrsFTiPrTc0gEiLRbfCERBx8GZ/7zGv6exKW1mc1L7QcFRmT1PRuJo6vRfCOtjdAdp0Mj1bllGGe9oBSKOxqtxs/NFygaVZjMDqryRvObKaJaj5CDhNdwsa21EsQ3+YvQWBzlcs5FTi5S2zG3W4+tMb+HoyV36SEV4yBLtqqrczhVCuPMlZu2p1iFLyODJJOxrWnmZy49BlQiudmiR7wILJoYKIFFvGv1jCJnTl9cI6UGX8IwSHYjGJIdLxaQM6c/7tw15+h+3jPajzZqkIQ7r0fyBp2TxE+QXMCP/knYu/dVzzQoBe5CgnAr5Fj60eEF78mJZbU3m9EjuVglURCTs2hDiyl3eRENgJjTc8p9iho4aK5eT5BVF7v2TAsTkfm+AwOq78chbWfh7J5OYnycG+v6S76LE6T8Yy0Arkk4lOF5SC05SmrDQpFcbRC9B7pR8XwJx3rabt4jvFsdqQtqv7TRasNQs95oROSC8335tzsaQfPwL/sGH4wi4zsH3YZ6As2V9myMEytqVEX5DdGBtzRr1opkx0aisyG48Evtk1UHMR9ROoZmkbNOIFNDUxCBvw7CU20aJSri4GX7kahg8Lj670Lfpx1C9OMwH0xRGUHE4e2ZWaw6Smkjc0Rru7j4YFKel0KtJgQaei2fz2i+6wOv1uz+H4j6f98pVMsf3HODmnh4x+qlUXaJWbNILQEGwv3zVReY123TPHIzkwImNLej62BLaqnEgiPkKr/gp/2MdrgepUEGC8FN0MTPbazDR4aE5XqLtnehhq8/9EfIk3b5WzNh00IAELwFrWnabkob5xmSLORBH8SpS3J6NwWa4jJMADRAGPYOUH7tYUM1/GRUK1HuboNP9v3KAny/k30CrxLvNHwe/zkXgoU9+M+gXVXL8pJJLMawVe/Dg13XyqTTa00UX7TsQFJZGm6lHrgeFIejKBEMLsMXNAIccphZe6sDnycDm/GY8vqmfjg9R05GwJOhBd46vhDi7Ph8YbLjohEoT4KfE5o8+Norzc/VHbRv5Y+G6JCL6hRV72meb3LswLYGUzGYP4nh2Y/yixg+rAtre80xjbXFfdvXVF6CuibKn5gmjCmiRN31rvEfdwVPIQDCaqv19Do2cQYDN+yGCo7yDHAAAAAEABVguNTA5AAADhzCCA4MwggJroAMCAQICBGNtJJswDQYJKoZIhvcNAQELBQAwcTELMAkGA1UEBhMCR0IxFzAVBgNVBAgTDldlc3QgWW9ya3NoaXJlMQ4wDAYDVQQHEwVMZWVkczEOMAwGA1UEChMFSWNvc2ExEzARBgNVBAsTCk9wZW4gQnJ1c2gxFDASBgNVBAMTC01pa2UgTWlsbGVyMCAXDTIyMDIwMjExMzAyNVoYDzIwNzIwMTIxMTEzMDI1WjBxMQswCQYDVQQGEwJHQjEXMBUGA1UECBMOV2VzdCBZb3Jrc2hpcmUxDjAMBgNVBAcTBUxlZWRzMQ4wDAYDVQQKEwVJY29zYTETMBEGA1UECxMKT3BlbiBCcnVzaDEUMBIGA1UEAxMLTWlrZSBNaWxsZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCZOlUSd2Z9VSuVE1NK2AKiKCYR3ADh3f3PN6ipTtqUdxP44l5jJnPVXc5YXJ4DyBsXHGTqCSiL9wiqdRCNTMcRf6vrpcuRWxqwMMu4bid0eDiFBU+wModQl70N0VblMolYZzD/y0NpXWh7VKPSXyA22ZwygeOPQFzxR4j2jRvM/g+9HeJeVN1p5f+6pvceg/9FBSCEOQg5fbDtO+ytZfMiawcyhSSwwlOzEOGT0Dq6d9xIs1/zTA8LxAlGYHLSpQCT/n3X27LNgUMNrCpWgLTtxH/qQ61NU3juqTqBBWT4nzTXl1J9JyPaHH1yzC908YiI5PQSFehX80KTvsf0B65DAgMBAAGjITAfMB0GA1UdDgQWBBTThSJ0yfVNgUC4h3Sa9o8aUmLY3jANBgkqhkiG9w0BAQsFAAOCAQEAUqE9NJA+PaMBrCcVHkxmk32DsVNIVCM/eaTPCyjBM3V5COgxscven160OKGHRn6Xhplr/UDy+StphE9Hwk8MAwSJ4reBdPiNMQvIsDEQ/aXSAyTiKQeIU5Zc+cYuJvHcyxIOVektDe8Er2AITvpXQDK1JRvYU6lFKym3j/CZ4comUwjdolB1C6fzlTkhP3ZuuFMfv543WyuVtb3A1mioLzQ5kfFlbTO0uXqEm+gltkK8AMqU6B5RJDYtQXIJkjR//UzNgpaILVvQ4pyyS6VvBNbUbrHaUKabtP3daDtQ0AQw3gSkCJ+QPpY9joIq38LMcVY5/x5/nbcxTuYvUlHozn/+qtNvA7MtikSNPcblNpmifg4o' }} - androidKeystorePass: ${{ secrets.ANDROID_KEYSTORE_PASS || 'FakeKey' }} - androidKeyaliasName: ${{ secrets.ANDROID_KEYALIAS_NAME || 'openbrush-non-official' }} - androidKeyaliasPass: ${{ secrets.ANDROID_KEYALIAS_PASS || 'FakeKey' }} - - - name: Prepare for packaging (permissions and compression, OSX only) - if: matrix.targetPlatform == 'StandaloneOSX' - run: | - mv build/${{ matrix.vrsdk }}/${{ matrix.targetPlatform }}/Support "build/${{ matrix.vrsdk }}/${{ matrix.targetPlatform }}/${{ env.filename }}/Contents/" - find build -name 'UnityFbxSdkNative.bundle' -delete - # Compress, but skip the top directories - tar -c -v -z -f OpenBrush.tgz -C build/${{ matrix.vrsdk }} ${{ matrix.targetPlatform}} - rm -rf build/${{ matrix.vrsdk }}/* - mv OpenBrush.tgz build/${{ matrix.vrsdk }}/ - - - name: Upload build/ - uses: actions/upload-artifact@v4 - with: - name: ${{ matrix.name }} ${{ matrix.flavors.title }} - path: | - build/${{ matrix.vrsdk }} - !build/Pico/*.symbols.zip - !build/**/*_BackUpThisFolder_ButDontShipItWithYourGame - - - name: Check if packages-lock.json has changed or if it's cacheable - id: check_packagecache - run: | - # Check if there are any changes to the packages-lock.json file - set +e - git diff --exit-code -- Packages/packages-lock.json - CHANGES="$?" - set -e - echo "changes=$CHANGES" >> $GITHUB_OUTPUT - echo "diff returned: $CHANGES" - - - name: Save Library/PackageCache cache - uses: actions/cache/save@v4 - if: github.ref == 'refs/heads/main' && steps.check_packagecache.outputs.changes == 0 && steps.cache_packagecache.outputs.cache-hit != 'true' && ! matrix.packages_to_remove # Ideally, we'd save caches on branches, but they're too big, and branch caches can evict those from main, which is unacceptable. - env: - SEGMENT_DOWNLOAD_TIMEOUT_MINS: 10 - with: - path: Library/PackageCache - key: Library_PackageCache_${{ env.UNITY_VERSION }}_${{ hashFiles('Packages/packages-lock.json') }} - - - name: Clean Library before caching - if: github.ref == 'refs/heads/main' && steps.cache_library.outputs.cache-hit != 'true' # Ideally, we'd save caches on branches, but they're too big, and branch caches can evict those from main, which is unacceptable. - run: | - # Remove the large files from the Library directory that we know we'll rebuild. As our il2cpp caches are huge and barely fit in the Github quota, it's better not to save an unneeded 1GB of space (or so). If a new Unity version is taken, this may need to be updated - # Debugging - echo "Library/ directories" - du -mcsh Library/* - find Library -size +50M -exec ls -altrh {} \; - # chown all files, since some are owned by root after the docker run - docker run -v $(pwd)/Library:/mnt alpine chown $(id -u).$(id -g) -R /mnt/ - # Print the files to be deleted - find Library/Bee/ -name 'symbols.zip' -or -name 'libil2cpp*.so' -or -name 'launcher-release.apk' | tee todelete.txt - cat todelete.txt | xargs -r rm - # The package cache is stored in a separate, shared, cache - rm -rf Library/PackageCache - echo "Final space used" - du -mcsh Library - - - name: Save Library/ cache - uses: actions/cache/save@v4 - if: github.ref == 'refs/heads/main' && steps.cache_library.outputs.cache-hit != 'true' # Ideally, we'd save caches on branches, but they're too big, and branch caches can evict those from main, which is unacceptable. - env: - SEGMENT_DOWNLOAD_TIMEOUT_MINS: 10 - with: - path: Library - # Some platforms share a cache; it's not a 1:1 mapping of either targetPlatform or vrsdk, so we have a distinct variable for which cache to use - key: Library_${{ matrix.cache }}_${{ env.UNITY_VERSION }} - - signmacos: - name: Sign the MacOS .app - needs: [configuration, build] - if: | - github.event_name == 'push' && - github.repository == 'icosa-foundation/open-brush' && - (github.ref == 'refs/heads/main' || contains(github.ref, 'refs/tags/v')) - runs-on: macos-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - fetch-depth: 0 - lfs: true # We don't use LFS, but it adds no time, and leave it here in case we do at some point later - sparse-checkout: | - Support/macos - - - name: Download Build Artifacts - uses: actions/download-artifact@v4 - with: - name: MacOS - path: build_macos - - # See https://docs.github.com/en/actions/deployment/deploying-xcode-applications/installing-an-apple-certificate-on-macos-runners-for-xcode-development - - name: Install the Apple certificate and provisioning profile - env: - BUILD_CERTIFICATE_BASE64: ${{ secrets.APPLE_BUILD_CERTIFICATE_BASE64 }} - INSTALL_CERTIFICATE_BASE64: ${{ secrets.APPLE_INSTALL_CERTIFICATE_BASE64 }} - P12_PASSWORD: ${{ secrets.APPLE_P12_PASSWORD }} - BUILD_PROVISION_PROFILE_BASE64: ${{ secrets.APPLE_BUILD_PROVISION_PROFILE_BASE64 }} - KEYCHAIN_PASSWORD: ${{ secrets.APPLE_KEYCHAIN_PASSWORD }} - run: | - BUILD_CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12 - INSTALL_CERTIFICATE_PATH=$RUNNER_TEMP/install_certificate.p12 - PP_PATH=$RUNNER_TEMP/openbrushmac.provisionprofile - KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db - - echo -n "$BUILD_CERTIFICATE_BASE64" | base64 --decode -o $BUILD_CERTIFICATE_PATH - echo -n "$INSTALL_CERTIFICATE_BASE64" | base64 --decode -o $INSTALL_CERTIFICATE_PATH - echo -n "$BUILD_PROVISION_PROFILE_BASE64" | base64 --decode -o $PP_PATH - - # create temporary keychain - security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH - security set-keychain-settings -lut 21600 $KEYCHAIN_PATH - security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH - - # import certificate to keychain - security import $BUILD_CERTIFICATE_PATH -P "$P12_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH - security import $INSTALL_CERTIFICATE_PATH -P "$P12_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH - security list-keychain -d user -s $KEYCHAIN_PATH - - mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles - cp $PP_PATH ~/Library/MobileDevice/Provisioning\ Profiles - - - name: Sign the release - env: - VERSION: ${{ needs.configuration.outputs.version }} - run: | - tar xvfz build_macos/*tgz - - export FILENAME=$(basename $(readlink -f StandaloneOSX/OpenBrush*.app)) - - cd StandaloneOSX/ - codesign --deep --force --verify --verbose --timestamp --options runtime --entitlements ../Support/macos/OpenBrush.entitlements --sign "Developer ID Application: Icosa Gallery Ltd (${{ vars.APPLE_TEAM_ID }})" $FILENAME/Contents/Support/ThirdParty/ffmpeg/bin/ffmpeg - for bundle in $FILENAME/Contents/Plugins/*.bundle; do - codesign --deep --force --verify --verbose --timestamp --options runtime --entitlements ../Support/macos/OpenBrush.entitlements --sign "Developer ID Application: Icosa Gallery Ltd (${{ vars.APPLE_TEAM_ID }})" $bundle - done - codesign --deep --force --verify --verbose --timestamp --options runtime --entitlements ../Support/macos/OpenBrush.entitlements --sign "Developer ID Application: Icosa Gallery Ltd (${{ vars.APPLE_TEAM_ID }})" $FILENAME - cd - - - tar -c -v -z -f OpenBrush.tgz StandaloneOSX - - name: Upload signed app - uses: actions/upload-artifact@v4 - with: - name: MacOS (signed) - path: | - OpenBrush.tgz - - createdmg: - name: Create and Notarize DMG - needs: [configuration, signmacos] - if: | - github.event_name == 'push' && - github.repository == 'icosa-foundation/open-brush' && - (github.ref == 'refs/heads/main' || contains(github.ref, 'refs/tags/v')) - - runs-on: macos-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - fetch-depth: 0 - lfs: true # We don't use LFS, but it adds no time, and leave it here in case we do at some point later - sparse-checkout: | - Support/macos - - - name: Download Build Artifacts - uses: actions/download-artifact@v4 - with: - name: MacOS (signed) - path: build_macos - - - name: Create a notarized DMG - env: - VERSION: ${{ needs.configuration.outputs.version }} - run: | - tar xvfz build_macos/*tgz - - export FILENAME=$(basename $(readlink -f StandaloneOSX/OpenBrush*.app)) - mkdir dist - - cp Support/macos/background.png StandaloneOSX/ - cp Support/macos/background@2x.png StandaloneOSX/ - pip install jinjanator - jinjanate Support/macos/openbrush-dmg.json.j2 > StandaloneOSX/openbrush-dmg.json - pushd StandaloneOSX/ - npx appdmg openbrush-dmg.json ../dist/OpenBrush.dmg - popd - - xcrun notarytool submit \ - --team-id ${{ vars.APPLE_TEAM_ID }} \ - --apple-id ${{ vars.APPLE_ID }} \ - --password ${{ secrets.APPLE_APPLICATION_SPECIFIC_PASSWORD }} \ - --wait \ - dist/OpenBrush.dmg - - xcrun stapler staple dist/OpenBrush.dmg - - - name: Upload notarized dmg - uses: actions/upload-artifact@v4 - with: - name: MacOS (DMG) - path: | - dist/OpenBrush.dmg - - release: - name: Create Github Release - needs: [configuration, build, createdmg] - runs-on: ubuntu-latest - if: | - github.event_name == 'push' && - github.repository == 'icosa-foundation/open-brush' && - (github.ref == 'refs/heads/main' || contains(github.ref, 'refs/tags/v')) - - steps: - - name: "Build Changelog" - id: changelog - uses: mikepenz/release-changelog-builder-action@v5 - with: - fromTag: "${{ needs.configuration.outputs.previousrelease }}" - toTag: "${{ needs.configuration.outputs.currentrelease }}" - configurationJson: | - { - "categories": [ - { - "title": "## 🚀 Features", - "labels": ["feature", "enhancement"] - }, - { - "title": "## 🎨 UI / UX", - "labels": ["ux"] - }, - { - "title": "## 🐛 Fixes", - "labels": ["fix", "bugfix"] - }, - { - "title": "## 🛠️ Infrastructure", - "labels": ["infrastructure"] - }, - { - "title": "## 📦 Dependencies / Maintenance", - "labels": ["dependencies", "maintenance"] - }, - { - "title": "## 💬 Uncategorized", - "labels": [] - } - ], - "pr_template": "- #{{TITLE}} (PR ##{{NUMBER}} by @#{{AUTHOR}})" - } - - - name: Echo Changelog (for debugging purposes) - env: - CHANGELOG: ${{ steps.changelog.outputs.changelog }} - run: echo "$CHANGELOG" - - - name: Download Build Artifacts (Windows OpenXR) - uses: actions/download-artifact@v4 - with: - name: Windows OpenXR - path: build_windows_openxr - - - name: Download Build Artifacts (Windows Rift) - uses: actions/download-artifact@v4 - with: - name: Windows Rift - path: build_windows_rift - - - name: Download Build Artifacts (Android OpenXR) - uses: actions/download-artifact@v4 - with: - name: Android OpenXR - path: build_android_openxr - - - name: Download Build Artifacts (Oculus Quest 2+) - uses: actions/download-artifact@v4 - with: - name: Oculus Quest (2+) - path: build_oculus_quest - - - name: Download Build Artifacts (Pico) - uses: actions/download-artifact@v4 - with: - name: Android Pico - path: build_android_pico - - - name: Download Build Artifacts (Linux) - uses: actions/download-artifact@v4 - with: - name: Linux - path: build_linux - - - name: Download Build Artifacts (Mac) - uses: actions/download-artifact@v4 - with: - name: MacOS (DMG) - path: build_macos - - - name: Package Artifacts for release - env: - VERSION: ${{ needs.configuration.outputs.version }} - run: | - mkdir releases - mv build_oculus_quest/*/com.Icosa.OpenBrush*apk releases/OpenBrush_Quest_$VERSION.apk - mv build_android_openxr/*/com.Icosa.OpenBrush*apk releases/OpenBrush_OpenXR_$VERSION.apk - mv build_android_pico/*/com.Icosa.OpenBrush*apk releases/OpenBrush_Pico_$VERSION.apk - mv build_windows_openxr/StandaloneWindows64/ releases/OpenBrush_Desktop_$VERSION/ - mv build_windows_rift/StandaloneWindows64/ releases/OpenBrush_Rift_$VERSION/ - mv build_macos/*.dmg releases/OpenBrush_Mac_$VERSION.dmg - mv build_linux/StandaloneLinux64/ releases/OpenBrush_Linux_$VERSION/ - cd releases - zip -r OpenBrush_Desktop_$VERSION.zip OpenBrush_Desktop_$VERSION/ - rm -rf OpenBrush_Desktop_$VERSION - zip -r OpenBrush_Rift_$VERSION.zip OpenBrush_Rift_$VERSION/ - rm -rf OpenBrush_Rift_$VERSION - zip -r OpenBrush_Linux_$VERSION.zip OpenBrush_Linux_$VERSION/ - - - name: Publish - uses: softprops/action-gh-release@v2 - with: - body: ${{ steps.changelog.outputs.changelog }} - prerelease: ${{ needs.configuration.outputs.prerelease }} - target_commitish: ${{ needs.configuration.outputs.currentrelease }} - tag_name: ${{ needs.configuration.outputs.version }} - files: releases/* - token: ${{ secrets.RELEASE_TOKEN }} - - publish_gitbook: - name: Publish changelog from last major build to open-brush-docs - needs: [configuration, build] - runs-on: ubuntu-latest - if: | - github.event_name == 'push' && - github.repository == 'icosa-foundation/open-brush' && - (github.ref == 'refs/heads/main' || contains(github.ref, 'refs/tags/v')) - steps: - - name: "Build Changelog" - id: changelog - uses: mikepenz/release-changelog-builder-action@v5 - with: - fromTag: "${{ needs.configuration.outputs.previousfullrelease }}" - toTag: "${{ needs.configuration.outputs.currentrelease }}" - configurationJson: | - { - "categories": [ - { - "title": "## 🚀 Features", - "labels": ["feature", "enhancement"] - }, - { - "title": "## 🎨 UI / UX", - "labels": ["ux"] - }, - { - "title": "## 🐛 Fixes", - "labels": ["fix", "bugfix"] - }, - { - "title": "## 🛠️ Infrastructure", - "labels": ["infrastructure"] - }, - { - "title": "## 📦 Dependencies / Maintenance", - "labels": ["dependencies", "maintenance"] - }, - { - "title": "## 💬 Uncategorized", - "labels": [] - } - ], - "template": "# Changelog since #{{FROM_TAG}}\n\n[Full release details](#{{RELEASE_DIFF}})\n\n#{{CHANGELOG}}\n\n", - "pr_template": "- #{{TITLE}} ([PR ##{{NUMBER}}](#{{URL}}) by @#{{AUTHOR}})\n" - } - - - name: Get the current contents of the docs repository - uses: actions/checkout@v4 - with: - repository: icosa-foundation/open-brush-docs - path: open-brush-docs - ref: master - fetch-depth: 0 - sparse-checkout: | - release-history/ - - - name: Create Changelog file - env: - CHANGELOG: ${{ steps.changelog.outputs.changelog }} - run: | - echo "$CHANGELOG" | tee open-brush-docs/release-history/automatic-changelog.md - - - name: Publish release notes - uses: cpina/github-action-push-to-another-repository@composite-1.5.1 - env: - SSH_DEPLOY_KEY: ${{ secrets.OPENBRUSH_DOCS_SSH_DEPLOY_KEY }} - with: - source-directory: 'open-brush-docs/release-history/' - target-directory: 'release-history/' - destination-github-username: 'icosa-foundation' - destination-repository-name: 'open-brush-docs' - user-name: 'release-note-bot' - user-email: automatic-release@icosa - target-branch: master - - publish_steam: - name: Publish Steam Release - needs: [configuration, build, signmacos] - if: | - github.event_name == 'push' && - github.repository == 'icosa-foundation/open-brush' && - (github.ref == 'refs/heads/main' || contains(github.ref, 'refs/tags/v')) - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - fetch-depth: 0 - sparse-checkout: | - Support/steam - lfs: true # We don't use LFS, but it adds no time, and leave it here in case we do at some point later - - name: Setup steamcmd - uses: CyberAndrii/setup-steamcmd@v1.2.0 - - name: Restore steam login config - run: | - mkdir -p /home/runner/Steam/config - echo "${{ secrets.STEAM_CONFIG_VDF}}" | base64 -d - | gunzip - > /home/runner/Steam/config/config.vdf - md5sum /home/runner/Steam/config/config.vdf - - name: Download Build Artifacts (Windows OpenXR) - uses: actions/download-artifact@v4 - with: - name: Windows OpenXR - path: build_windows_openxr - - name: Download Build Artifacts (MacOS Signed) - uses: actions/download-artifact@v4 - with: - name: MacOS (signed) - path: build_macos - - name: Download Build Artifacts (Linux) - uses: actions/download-artifact@v4 - with: - name: Linux - path: build_linux - - name: Upload Build - run: | - cd build_macos - tar xvfz OpenBrush.tgz - cd - - pip install -U jinjanator - jinjanate Support/steam/app.vdf.j2 > app.vdf - jinjanate Support/steam/main_depot.win.vdf.j2 > build_windows_openxr/main_depot.vdf - jinjanate Support/steam/main_depot.mac.vdf.j2 > build_macos/main_depot.vdf - jinjanate Support/steam/main_depot.linux.vdf.j2 > build_linux/main_depot.vdf - jinjanate Support/steam/installscript_win.vdf.j2 > build_windows_openxr/installscript_win.vdf - steamcmd +login $STEAM_USERNAME +run_app_build $(pwd)/app.vdf +quit - env: - STEAM_USERNAME: ${{ vars.STEAM_USERNAME }} - STEAM_PASSWORD: ${{ secrets.STEAM_PASSWORD }} - VERSION: ${{ needs.configuration.outputs.version }} - OPEN_BRUSH_APP_ID: ${{ vars.STEAM_APP_ID }} - OPEN_BRUSH_WINDOWS_DEPOT_ID: ${{ vars.STEAM_WINDOWS_DEPOT_ID }} - OPEN_BRUSH_MAC_DEPOT_ID: ${{ vars.STEAM_MAC_DEPOT_ID }} - OPEN_BRUSH_LINUX_DEPOT_ID: ${{ vars.STEAM_LINUX_DEPOT_ID }} - OPEN_BRUSH_WINDOWS_EXECUTABLE: ${{ needs.configuration.outputs.basename}}.exe - CHANNEL: beta - - name: Update steam login secret - run: | - gzip /home/runner/Steam/config/config.vdf -c | base64 | gh secret set --visibility all --org icosa-foundation STEAM_CONFIG_VDF - md5sum /home/runner/Steam/config/config.vdf - env: - GITHUB_TOKEN: ${{ secrets.SECRET_UPDATER_PAT }} - - name: Save logs - uses: actions/upload-artifact@v4 - if: ${{ failure() }} - with: - name: steamcmd logs - path: build_output/ - - publish_pimax: - name: Publish Pimax Release - needs: [configuration, build] - runs-on: windows-latest - env: - PIMAX_APP_ID: ${{ vars.PIMAX_APP_ID }} - PIMAX_USERNAME: ${{ vars.PIMAX_USERNAME }} - PIMAX_PASSWORD: ${{ secrets.PIMAX_PASSWORD }} - VERSION: ${{ needs.configuration.outputs.version}} - if: | - github.event_name == 'push' && - github.repository == 'icosa-foundation/open-brush' && - contains(github.ref, 'refs/tags/v') - - steps: - - name: Download Build Artifacts (Windows Pimax) - uses: actions/download-artifact@v4 - with: - name: Windows Pimax - path: build_windows_pimax - - - name: Publish Pimax Builds - run: | - New-Item "releases" -Type Directory - Move-Item -Path build_windows_pimax/StandaloneWindows64/* releases/ - Set-Location -Path "releases" - Compress-Archive -Path ./* -DestinationPath OpenBrush_Pimax_${Env:VERSION}.zip - Invoke-WebRequest -Uri https://dl.appstore.pimax.com/tools/pimax-dev-util.exe -OutFile pimax-dev-util.exe - ./pimax-dev-util.exe login -u $Env:PIMAX_USERNAME -p $Env:PIMAX_PASSWORD - ./pimax-dev-util.exe upload-pc-build -a $Env:PIMAX_APP_ID -d OpenBrush_Pimax_${Env:VERSION}.zip - - publish_viveport: - name: Publish Viveport Release - needs: [configuration, build] - runs-on: windows-latest - env: - VIVEPORT_EMAIL: ${{ vars.VIVEPORT_EMAIL }} - VIVEPORT_PASSWORD: ${{ secrets.VIVEPORT_PASSWORD }} - VIVEPORT_APP_ID: ${{ vars.VIVEPORT_APP_ID }} - VIVEPORT_ORG_ID: ${{ vars.VIVEPORT_ORG_ID }} - VERSION: ${{ needs.configuration.outputs.version}} - if: | - github.event_name == 'push' && - github.repository == 'icosa-foundation/open-brush' && - contains(github.ref, 'refs/tags/v') - - steps: - - name: Download Build Artifacts (Windows OpenXR) - uses: actions/download-artifact@v4 - with: - name: Windows OpenXR - path: build_windows_openxr - - - name: Publish Viveport Builds - run: | - New-Item "releases" -Type Directory - Move-Item -Path build_windows_openxr/StandaloneWindows64/* releases/ - Set-Location -Path "releases" - Compress-Archive -Path ./* -DestinationPath OpenBrush_Viveport_${Env:VERSION}.zip - Invoke-WebRequest -Uri https://assets-global.viveport.com/static-misc/developer-tool/ViveportUploader.exe -OutFile ViveportUploader.exe - ./ViveportUploader.exe -email ${Env:VIVEPORT_EMAIL} -password ${Env:VIVEPORT_PASSWORD} -filepath OpenBrush_Viveport_${Env:VERSION}.zip -appid ${Env:VIVEPORT_APP_ID} -orgid ${Env:VIVEPORT_ORG_ID} -version ${VERSION} - - publish_itch: - name: Publish Itch.io Release - needs: [configuration, build] - runs-on: ubuntu-latest - env: - ITCH_SUBCHANNEL_NAME: ${{ needs.configuration.outputs.itchchannelname }} - BUTLER_CREDENTIALS: ${{ secrets.BUTLER_CREDENTIALS }} - ITCH_GAME: openbrush - ITCH_USER: openbrush - VERSION: ${{ needs.configuration.outputs.version }} - if: | - github.event_name == 'push' && - github.repository == 'icosa-foundation/open-brush' && - (github.ref == 'refs/heads/main' || contains(github.ref, 'refs/tags/v')) - - steps: - - name: Download Build Artifacts (Windows OpenXR) - uses: actions/download-artifact@v4 - with: - name: Windows OpenXR - path: build_windows_openxr - - - name: Download Build Artifacts (Oculus Quest 2+) - uses: actions/download-artifact@v4 - with: - name: Oculus Quest (2+) - path: build_oculus_quest - - - name: Package Artifacts for release - run: | - mkdir releases - mv build_oculus_quest/*/com.Icosa.OpenBrush*apk releases/OpenBrush_Quest_$VERSION.apk - mv build_windows_openxr/StandaloneWindows64/ releases/OpenBrush_Desktop_$VERSION/ - - name: Publish Windows - uses: josephbmanley/butler-publish-itchio-action@master - env: - CHANNEL: windows-${{ env.ITCH_SUBCHANNEL_NAME }} - PACKAGE: releases/OpenBrush_Desktop_${{ needs.configuration.outputs.version }} - - name: Publish Quest - uses: josephbmanley/butler-publish-itchio-action@master - env: - CHANNEL: android-quest-${{ env.ITCH_SUBCHANNEL_NAME }} - PACKAGE: releases/OpenBrush_Quest_${{ needs.configuration.outputs.version }}.apk - - publish_oculus_quest: - name: Publish Oculus Quest 2+ Release - needs: [configuration, build] - runs-on: macos-latest # the ovr-platform-util tool is only available for Mac and Windows - if: | - github.event_name == 'push' && - github.repository == 'icosa-foundation/open-brush' && - (github.ref == 'refs/heads/main' || contains(github.ref, 'refs/tags/v')) - - steps: - - name: Download Build Artifacts (Oculus Quest 2+) - uses: actions/download-artifact@v4 - with: - name: Oculus Quest (2+) - path: build_oculus_quest - - name: Publish Oculus Builds - env: - VERSION: ${{ needs.configuration.outputs.version }} - PRERELEASE: ${{ needs.configuration.outputs.prerelease }} - RAW_CHANGELOG: ${{ needs.configuration.outputs.rawchangelog }} - OCULUS_QUEST_APP_ID: ${{ vars.OCULUS_QUEST_APP_ID }} - OCULUS_QUEST_APP_SECRET: ${{ secrets.OCULUS_QUEST_APP_SECRET }} - run: | - mkdir releases - mv build_oculus_quest/*/com.Icosa.OpenBrush*apk releases/OpenBrush_Quest_$VERSION.apk - mv build_oculus_quest/*/com.Icosa.OpenBrush*.symbols.zip releases/symbols.zip - - cd releases - unzip symbols.zip - curl -L 'https://www.oculus.com/download_app/?id=1462426033810370' -o ovr-platform-util - chmod 755 ovr-platform-util - - if [ "$PRERELEASE" == "false" ] - then - ./ovr-platform-util upload-quest-build --app-id ${OCULUS_QUEST_APP_ID} --app-secret ${OCULUS_QUEST_APP_SECRET} --apk OpenBrush_Quest_$VERSION.apk --channel LIVE:quest2+ --debug_symbols_dir ./arm64-v8a/ --debug-symbols-pattern '*.so' --age-group MIXED_AGES - else - CHANGELOG="${RAW_CHANGELOG}" - ./ovr-platform-util upload-quest-build --app-id ${OCULUS_QUEST_APP_ID} --app-secret ${OCULUS_QUEST_APP_SECRET} --apk OpenBrush_Quest_$VERSION.apk --channel Beta:quest2+ --debug_symbols_dir ./arm64-v8a/ --debug-symbols-pattern '*.so' --notes "${CHANGELOG}" --age-group MIXED_AGES - fi - - publish_oculus_quest1: - name: Publish Oculus Quest 1 Release - needs: [configuration, build] - runs-on: macos-latest # the ovr-platform-util tool is only available for Mac and Windows - if: | - github.event_name == 'push' && - github.repository == 'icosa-foundation/open-brush' && - (github.ref == 'refs/heads/main' || contains(github.ref, 'refs/tags/v')) - - steps: - - name: Download Build Artifacts (Oculus Quest 1) - uses: actions/download-artifact@v4 - with: - name: Oculus Quest (1) - path: build_oculus_quest1 - - name: Publish Oculus Builds - env: - VERSION: ${{ needs.configuration.outputs.version }} - PRERELEASE: ${{ needs.configuration.outputs.prerelease }} - RAW_CHANGELOG: ${{ needs.configuration.outputs.rawchangelog }} - OCULUS_QUEST_APP_ID: ${{ vars.OCULUS_QUEST_APP_ID }} - OCULUS_QUEST_APP_SECRET: ${{ secrets.OCULUS_QUEST_APP_SECRET }} - run: | - mkdir releases1 - mv build_oculus_quest1/*/com.Icosa.OpenBrush*apk releases1/OpenBrush_Quest1_$VERSION.apk - mv build_oculus_quest1/*/com.Icosa.OpenBrush*.symbols.zip releases1/symbols.zip - - cd releases1 - unzip symbols.zip - curl -L 'https://www.oculus.com/download_app/?id=1462426033810370' -o ovr-platform-util - chmod 755 ovr-platform-util - - if [ "$PRERELEASE" == "false" ] - then - ./ovr-platform-util upload-quest-build --app-id ${OCULUS_QUEST_APP_ID} --app-secret ${OCULUS_QUEST_APP_SECRET} --apk OpenBrush_Quest1_$VERSION.apk --channel LIVE:quest1only --debug_symbols_dir ./arm64-v8a/ --debug-symbols-pattern '*.so' --age-group MIXED_AGES - else - CHANGELOG="${RAW_CHANGELOG}" - ./ovr-platform-util upload-quest-build --app-id ${OCULUS_QUEST_APP_ID} --app-secret ${OCULUS_QUEST_APP_SECRET} --apk OpenBrush_Quest1_$VERSION.apk --channel Beta:quest1only --debug_symbols_dir ./arm64-v8a/ --debug-symbols-pattern '*.so' --notes "${CHANGELOG}" --age-group MIXED_AGES - fi - - publish_oculus_rift: - name: Publish Oculus Rift Release - needs: [configuration, build] - runs-on: macos-latest # the ovr-platform-util tool is only available for Mac and Windows - if: | - github.event_name == 'push' && - github.repository == 'icosa-foundation/open-brush' && - (github.ref == 'refs/heads/main' || contains(github.ref, 'refs/tags/v')) - - steps: - - name: Download Build Artifacts (Windows Rift) - uses: actions/download-artifact@v4 - with: - name: Windows Rift - path: build_windows_rift - - name: Publish Oculus Builds - env: - VERSION: ${{ needs.configuration.outputs.version }} - PRERELEASE: ${{ needs.configuration.outputs.prerelease }} - RAW_CHANGELOG: ${{ needs.configuration.outputs.rawchangelog }} - OCULUS_RIFT_APP_ID: ${{ vars.OCULUS_RIFT_APP_ID }} - OCULUS_RIFT_APP_SECRET: ${{ secrets.OCULUS_RIFT_APP_SECRET }} - run: | - mkdir releases - mv build_windows_rift/StandaloneWindows64/ releases/OpenBrush_Rift_$VERSION/ - cd releases - zip -r OpenBrush_Rift_$VERSION.zip OpenBrush_Rift_$VERSION/ - curl -L 'https://www.oculus.com/download_app/?id=1462426033810370' -o ovr-platform-util - chmod 755 ovr-platform-util - - if [ "$PRERELEASE" == "false" ] - then - ./ovr-platform-util upload-rift-build --app-id ${OCULUS_RIFT_APP_ID} --app-secret ${OCULUS_RIFT_APP_SECRET} --build-dir OpenBrush_Rift_$VERSION --launch-file OpenBrush.exe --channel LIVE --version $VERSION --firewall_exceptions true --redistributables 822786567843179,1675031999409058,2657209094360789 - else - CHANGELOG="${RAW_CHANGELOG}" - ./ovr-platform-util upload-rift-build --app-id ${OCULUS_RIFT_APP_ID} --app-secret ${OCULUS_RIFT_APP_SECRET} --build-dir OpenBrush_Rift_$VERSION --launch-file OpenBrush.exe --channel BETA --version $VERSION --firewall_exceptions true --redistributables 822786567843179,1675031999409058,2657209094360789 --notes "${CHANGELOG}" - fi - - publish_pico: - name: Publish Pico Releases - needs: [configuration, build] - runs-on: ubuntu-latest # the ovr-platform-util tool is only available for Mac and Windows - if: | - github.event_name == 'push' && - github.repository == 'icosa-foundation/open-brush' && - (github.ref == 'refs/heads/main' || contains(github.ref, 'refs/tags/v')) - - steps: - - name: Download Build Artifacts (Android Pico) - uses: actions/download-artifact@v4 - with: - name: Android Pico - path: build_android_pico - - name: Download Build Artifacts (Android Pico CN) - uses: actions/download-artifact@v4 - with: - name: Android Pico (CN) - path: build_android_pico_cn - - name: Publish Pico Builds - env: - VERSION: ${{ needs.configuration.outputs.version }} - PRERELEASE: ${{ needs.configuration.outputs.prerelease }} - RAW_CHANGELOG: ${{ needs.configuration.outputs.rawchangelog }} - PICO_APP_ID: ${{ vars.PICO_APP_ID }} - PICO_APP_SECRET: ${{ secrets.PICO_APP_SECRET }} - run: | - mkdir releases - mv build_android_pico/*/com.Icosa.OpenBrush*apk releases/OpenBrush_Pico_$VERSION.apk - mv build_android_pico_cn/*/com.Icosa.OpenBrush*apk releases/OpenBrush_Pico_CN_$VERSION.apk - - cd releases - # pico-cli v1.0.3 - curl -L 'https://p16-platform-static-va.ibyteimg.com/tos-maliva-i-jo6vmmv194-us/linux-noncn/202304111056/pico-cli?r=1681181814847245000' -o pico-cli - chmod 755 pico-cli - - if [ "$PRERELEASE" == "false" ] - then - # The order here matters, because the Chinese build has a slightly higher version code due to the suffix of 1 vs 0 - ./pico-cli upload-build --app-id $PICO_APP_ID --app-secret $PICO_APP_SECRET --region noncn --apk OpenBrush_Pico_$VERSION.apk --channel 2 --notes-en "Version $VERSION" --device 'PICO Neo3,PICO Neo3 Pro,PICO Neo3 Eye,PICO 4' - ./pico-cli upload-build --app-id $PICO_APP_ID --app-secret $PICO_APP_SECRET --region noncn --apk OpenBrush_Pico_CN_$VERSION.apk --channel 1 --notes-en "Version $VERSION" --device 'PICO Neo3,PICO Neo3 Pro,PICO Neo3 Eye,PICO 4' - else - # For Pico, Beta channels can only get one build, not a separate China / non-China build - ./pico-cli upload-build --app-id $PICO_APP_ID --app-secret $PICO_APP_SECRET --region noncn --apk OpenBrush_Pico_$VERSION.apk --channel 3 --notes-en "Version $VERSION" --device 'PICO Neo3,PICO Neo3 Pro,PICO Neo3 Eye,PICO 4' - fi - - publish_ios_zapbox: - name: Publish Zapbox iOS - needs: [configuration, build] - runs-on: macos-latest - if: | - github.event_name == 'push' && - github.repository == 'icosa-foundation/open-brush' && - (github.ref == 'refs/heads/main' || contains(github.ref, 'refs/tags/v')) - steps: - - name: Checkout Repository - uses: actions/checkout@v4 - with: - fetch-depth: 0 - sparse-checkout: | - Gemfile - Gemfile.lock - fastlane - - - name: Free extra space - # As of 02/08/2024, this increases free space from 21GB to 47GB - run: | - echo "Initial free space" - df -h - rm -rf "$AGENT_TOOLSDIRECTORY" - echo "Disk space after cleanup of \$AGENT_TOOLSDIRECTORY" - df -h - echo "Deleting all Xcode versions except 15.4" - find /Applications/Xcode_* -maxdepth 0 -type d ! -name 'Xcode_15.4.app' -exec rm -rf {} \; - df -h - find /Applications/Xcode* -name "*.app" -exec du -mcsh {} \; # Shows Xcode app sizes - - - name: Download iOS Artifact - uses: actions/download-artifact@v4 - with: - name: iOS Zapbox - path: build - - - name: Fix File Permissions - run: | - export FILENAME=$(basename $(readlink -f build/iOS/OpenBrush*)) - export IOS_BUILD_PATH=$(pwd)/build/iOS/${FILENAME} - - find $IOS_BUILD_PATH -type f -name "*.sh" -exec chmod +x {} \; - - - name: Run fastlane - env: - APPLE_CONNECT_EMAIL: ${{ secrets.APPLE_CONNECT_EMAIL }} - APPLE_DEVELOPER_EMAIL: ${{ secrets.APPLE_DEVELOPER_EMAIL }} - APPLE_TEAM_ID: ${{ vars.APPLE_TEAM_ID }} - - MATCH_REPOSITORY: ${{ secrets.MATCH_REPOSITORY }} - MATCH_DEPLOY_KEY: ${{ secrets.MATCH_DEPLOY_KEY }} - MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }} - - APPSTORE_ISSUER_ID: ${{ secrets.APPSTORE_ISSUER_ID }} - APPSTORE_KEY_ID: ${{ secrets.APPSTORE_KEY_ID }} - APPSTORE_P8: ${{ secrets.APPSTORE_P8 }} - - IOS_BUNDLE_ID: ${{ vars.IOS_ZAPBOX_BUNDLE_ID }} - PROJECT_NAME: Open Brush for Zapbox - FASTLANE_LANE: ${{ needs.configuration.outputs.fastlanelane }} - run: | - eval "$(ssh-agent -s)" - ssh-add - <<< "${MATCH_DEPLOY_KEY}" - export FILENAME=$(basename $(readlink -f build/iOS/OpenBrush*)) - export IOS_BUILD_PATH=$(pwd)/build/iOS/${FILENAME} - - bundle install - bundle exec fastlane ios ${FASTLANE_LANE} - - - name: Save logs - uses: actions/upload-artifact@v4 - if: ${{ failure() }} - with: - name: fastlane logs - path: /Users/runner/Library/Logs/gym/ +--- +# yamllint disable rule:line-length +# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json +name: Builds + +on: # yamllint disable-line rule:truthy + pull_request: {} + push: + branches: + - "**" + tags: + - "v*" + schedule: + - cron: "34 19 * * sat" # Weekly, Saturday at 19:34 UTC + +env: + UNITY_VERSION: "2022.3.34f1" + PHOTON_PAT: ${{ secrets.PHOTON_PAT }} # This needs to be here, since you can't use a ${{ secrets }} within an if: condition +jobs: + configuration: + if: | + (github.event_name == 'schedule') || + (github.event_name == 'pull_request') || + ( + github.event_name == 'push' && + ( + github.ref == 'refs/heads/main' || + contains(github.ref, 'refs/tags/v') || + contains(github.event.head_commit.message, '[CI BUILD]') + ) + ) + name: Configure Build Parameters + runs-on: ubuntu-latest + outputs: + version: ${{ steps.version.outputs.version}} + androidVersionCode: ${{ steps.version.outputs.androidVersionCode }} + stamp: ${{ steps.version.outputs.stamp }} + prerelease: ${{ steps.version.outputs.prerelease }} + previousrelease: ${{ steps.rawchangelogdata.outputs.previousrelease }} + previousfullrelease: ${{ steps.rawchangelogdata.outputs.previousfullrelease }} + currentrelease: ${{ steps.rawchangelogdata.outputs.currentrelease }} + rawchangelog: ${{ steps.rawchangelogdata.outputs.rawchangelog}} + basename: ${{ steps.github.outputs.basename }} + description: ${{ steps.github.outputs.description}} + itchchannelname: ${{ steps.version.outputs.itchchannelname }} + fastlanelane: ${{ steps.version.outputs.fastlanelane}} + uid: ${{ steps.github.outputs.uid }} + gid: ${{ steps.github.outputs.gid }} + flavors: ${{ steps.flavors.outputs.flavors }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + lfs: true # We don't use LFS, but it adds no time, and leave it here in case we do at some point later + + - name: Calculate version and stamp + id: version + run: | + # General note: for pull requests, we query github.event.pull_request.head.sha rather than the default sha, which is a merge commit of the target branch into the head. For pushes or tag commits, there's no additional commits made by the CI, so we can use the default, current, reference + + # Get the first two numbers from the last tag, including a tag on the current commit (to handle the case of a formal build) + MAJOR_MINOR=$(git describe --tags --abbrev=0 --match "v[0-9]*.[0-9]*" ${{ github.event.pull_request.head.sha }}) + + # How many commits have been made since the last tag of the form vX.Y. + # + # We used to use this version, however, it couldn't handle these two cases at the same time: + # (v2.1) + # | + # /-c2..c4..c5-\ + # / \ + # c...c0..c1....c3.........m6..c7....c10.c11.....m13...c14 <- [main] + # ^ \ / + # (v2.0) \-c8..c9..c12-/ + # If we use --first-parent, it wouldn't find a tag that was not a first parent, and so it'll think we're now in 2.0.8, though it skips the commits on the branches. If we did not use --first-parent, it gets the proper tag (v2.1), but counts each commit in the feature branch, and gives 2.1.10. While we almost always squash, if we ever do have an explicit merge commit, we don't want to count the commits on the feature branch. In this case, we actually want to get 2.1.7 (commits c3, m6, c7, c10, c11, m13, and c14). + ######## OLD CODE ######## + # # If the value is not equal to zero, git describe will give us a version in the form vX.Y-Z-gAAAAAAA, where Z is the count. If the current commit has a vX.Y tag, it'll just return that, so the 'cut' does nothing. We test for this below + # PATCH_VERSION=$(git describe --tags --match "v[0-9]*.[0-9]*" --first-parent ${{ github.event.pull_request.head.sha }} | cut -d'-' -f2) + ######## END OLD CODE ######## + + # Instead, we'll find the last tag, wherever it is, and then count the --first-parent commits "since" then (i.e., not included; they might be historically behind it) + CLOSEST_TAG=$(git describe --tags --match "v[0-9]*.[0-9]*" --abbrev=0 HEAD) + PATCH_VERSION=$(git log ${CLOSEST_TAG}.. --oneline --first-parent | wc -l) + + if [ $PATCH_VERSION == "0" ] + then + STAMP="" + echo "prerelease=false" >> $GITHUB_OUTPUT + echo "itchchannelname=release" >> $GITHUB_OUTPUT + echo "fastlanelane=beta" >> $GITHUB_OUTPUT + + else + # This is the first 7 characters of the commit; we do it this way rather than via rev-parse to avoid an 'if' conditional depending on whether it's a PR or push. (unlike git describe, git rev-parse doesn't default to the current HEAD) + STAMP=$(git describe --tags --match "v[0-9]*.[0-9]*" ${{ github.event.pull_request.head.sha }} | cut -d'-' -f3) + echo "prerelease=true" >> $GITHUB_OUTPUT + echo "itchchannelname=beta" >> $GITHUB_OUTPUT + echo "fastlanelane=beta" >> $GITHUB_OUTPUT + fi + VERSION=$(echo "$MAJOR_MINOR.$PATCH_VERSION" | sed -e 's/^v//') + echo "version=$VERSION" >> $GITHUB_OUTPUT + echo "stamp=$STAMP" >> $GITHUB_OUTPUT + MAJOR=$(echo $VERSION | cut -d '.' -f 1) + MINOR=$(echo $VERSION | cut -d '.' -f 2) + ANDROID_VERSION_CODE=$((MAJOR * 1000000 + MINOR * 1000 + PATCH_VERSION)) + echo "androidVersionCode=$ANDROID_VERSION_CODE" >> $GITHUB_OUTPUT + echo "Version $VERSION stamp=$STAMP androidVersionCode=$ANDROID_VERSION_CODE" + - name: Calculate Release tags for Changelog and raw changelog + id: rawchangelogdata + env: + PRERELEASE: ${{ steps.version.outputs.prerelease }} + VERSION: ${{ steps.version.outputs.version }} + run: | + if [ "$PRERELEASE" == "true" ] + then + PREV=$(git describe --tags --abbrev=0 HEAD^) + else + PREV=$(git describe --tags --match "v[0-9]*.[0-9]*" --first-parent --abbrev=0 HEAD^) + fi + PREVFULL=$(git describe --tags --match "v[0-9]*.[0-9]*" --first-parent --abbrev=0 HEAD^) + CUR="$(git rev-parse HEAD)" + echo "previousrelease=$PREV" >> $GITHUB_OUTPUT + echo "previousfullrelease=$PREVFULL" >> $GITHUB_OUTPUT + echo "currentrelease=$CUR" >> $GITHUB_OUTPUT + LAST_TAG=$(git describe --tags --match 'v[0-9]*.[0-9]*' --abbrev=0 HEAD^) + RAW_CHANGELOG=$(echo "$(git log --first-parent ${LAST_TAG}.. --pretty=format:'%D-g%h: %s' | sed -e 's/tag: //' -e 's/HEAD -> main, //')" | sed -e "s/origin\/main/$VERSION/" | tac) + echo "rawchangelog=${RAW_CHANGELOG//$'\n'/'\n'}" >> $GITHUB_OUTPUT + + - name: Echo Changelog (for debugging purposes) + env: + CHANGELOG: ${{ steps.rawchangelogdata.outputs.rawchangelog}} + run: | + echo "CHANGELOG=$CHANGELOG" + + - name: Set custom app name and package name, if relevant + id: github + env: + PRERELEASE: ${{ steps.version.outputs.prerelease }} + run: | + # For a PR action (i.e., synchronize / open), the value of github.ref will be refs/pull/1234/merge + # For a push action, it will be either refs/heads/foo_branch_name OR refs/tags/v1234. + # We want to use the base name for pushes of tags or to main, the PR number for PRs, and the branch name for named branches. + if [[ "$PRERELEASE" == "false" || ${{ github.ref }} == refs/heads/main ]] + then + echo "basename=OpenBrush" >> $GITHUB_OUTPUT + echo "description=" >> $GITHUB_OUTPUT + else + if [[ ${{ github.ref }} == refs/pull/* ]] + then + DESCRIPTION="PR#$(echo ${{ github.ref }} | sed -e 's#refs/pull/##' -e 's#/merge##')" + elif [[ ${{ github.ref }} == refs/heads/* ]] + then + DESCRIPTION="$(echo ${{ github.ref }} | sed -e 's#refs/heads/##')" + else + DESCRIPTION="Unknown" + fi + echo "description=-btb-description ${DESCRIPTION}" >> $GITHUB_OUTPUT + IDENTIFIER=$(echo ${DESCRIPTION} | sed -e 's/[\/#_-]//g') + echo "basename=OpenBrush-${IDENTIFIER}" >> $GITHUB_OUTPUT + fi + echo "uid=$(id -u)" >> $GITHUB_OUTPUT + echo "gid=$(id -g)" >> $GITHUB_OUTPUT + + - name: Determine whether to build Development builds or not + id: flavors + run: | + set -x + if [[ $(git log --format=%B ${{ github.event.pull_request.head.sha }} -1) == *'[CI BUILD DEV]'* ]] + then + echo 'flavors=[{"development": true, "title": "Development"}, {"development": false}]' >> $GITHUB_OUTPUT + + else + echo 'flavors=[{"development": false}]' >> $GITHUB_OUTPUT + fi + + build: + name: ${{ matrix.name }} ${{ matrix.flavors.title }} + needs: configuration + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + flavors: ${{ fromJson(needs.configuration.outputs.flavors) }} + name: [Windows OpenXR, Windows Pimax, Windows Rift, Linux, MacOS, Android OpenXR, Oculus Quest (1), Oculus Quest (2+), Android Pico, Android Pico (CN), iOS Zapbox] # These will all be overwritten, but because we have the flavors matrix as well, we can't just add configurations via include; they'll overwrite each other. This way ensures that we get each one + include: + - name: Windows OpenXR + targetPlatform: StandaloneWindows64 + vrsdk: OpenXR + cache: Windows + + - name: Windows Pimax + targetPlatform: StandaloneWindows64 + vrsdk: OpenXR + cache: Windows + extra_defines: PIMAX_SUPPORTED + + - name: Windows Rift + targetPlatform: StandaloneWindows64 + vrsdk: Oculus + cache: Windows + extra_defines: OCULUS_SUPPORTED + + - name: Linux + targetPlatform: StandaloneLinux64 + vrsdk: Monoscopic # All builds include monoscopic, but this one has no additional XrSdk, so we'll keep the name monoscopic + cache: Linux + + - name: MacOS + targetPlatform: StandaloneOSX + vrsdk: Monoscopic + cache: MacOS + packages_to_remove: com.meta.xr.sdk.core + + - name: Android OpenXR + targetPlatform: Android + vrsdk: OpenXR + cache: Android_Vulkan + extraoptions: -btb-il2cpp + versionSuffix: 0 + + - name: Oculus Quest (1) + targetPlatform: Android + vrsdk: OpenXR + cache: Android_Vulkan + extraoptions: -btb-il2cpp + versionSuffix: 0 + extra_defines: USE_QUEST_PACKAGE_NAME FORCE_QUEST_SUPPORT_DEVICE FORCE_FOCUSAWARE FORCE_HEADTRACKING + packages_to_remove: com.meta.xr.sdk.platform com.meta.xr.sdk.utilities + + - name: Oculus Quest (2+) + targetPlatform: Android + vrsdk: Oculus + cache: Android_Vulkan + extraoptions: -btb-il2cpp + versionSuffix: 1 + extra_defines: OCULUS_SUPPORTED USE_QUEST_PACKAGE_NAME + + - name: Android Pico + targetPlatform: Android + vrsdk: Pico + cache: Android_GLES + extraoptions: -btb-il2cpp + versionSuffix: 0 + extra_defines: PICO_SUPPORTED + + - name: Android Pico (CN) + targetPlatform: Android + vrsdk: Pico + cache: Android_GLES + # Pico requested Chinese build that doesn't have google/sketchfab login. + extraoptions: -btb-il2cpp -btb-disableAccountLogins + versionSuffix: 1 + extra_defines: PICO_SUPPORTED + + - name: iOS Zapbox + targetPlatform: iOS + vrsdk: Zapbox + cache: iOS + extraoptions: -btb-il2cpp + extra_defines: ZAPBOX_SUPPORTED + packages_to_remove: com.unity.formats.usd + + steps: + - name: Set masking + run: echo "::add-mask::DoNotStealThis1" + - name: Free extra space + # As of 02/08/2024, this increases free space from 21GB to 47GB + run: | + echo "Initial free space" + df -h / + echo "Removing all pre-loaded docker images" + docker rmi $(docker image ls -aq) # Removes ~3GB + df -h / + echo "Listing 100 largest packages" + dpkg-query -Wf '${Installed-Size}\t${Package}\n' | sort -rn | head -n 100 + echo "Removing unneeded large packages" + sudo apt update + sudo apt remove -y '^ghc-.*' '^dotnet-.*' azure-cli powershell google-chrome-stable firefox microsoft-edge-stable 'mongodb-*' 'mysql-*' 'mariadb-*' 'temurin-*' 'openjdk-*' default-jre-headless mono-devel libgl1-mesa-dri # Removes ~6GB + sudo apt autoremove -y + sudo apt clean + df -h / + echo "Removing Android" + sudo rm -rf /usr/local/lib/android # Removes ~9GB + df -h / + echo "Removing remaining large directories" + rm -rf /usr/share/dotnet/ # Removes ~1GB + rm -rf "$AGENT_TOOLSDIRECTORY" # Removes ~7GB + echo "Disk space after cleanup" + df -h / + + - name: Checkout repository + uses: actions/checkout@v4 + with: + lfs: true # We don't use LFS, but it adds no time, and leave it here in case we do at some point later + + - name: Install Pimax unity package + if: startsWith(matrix.name, 'Windows Pimax') + run: | + # version 0.6.3 + # Same as above, but adapted to work for Pimax instead. + wget -q https://dl.appstore.pimax.com/sdk/Pimax_Platform_Unity_SDK_v0.6.3.zip -O package.zip + unzip package.zip + mkdir tmp + tar -C tmp -xzf PimaxPlatform_v0.6.3.unitypackage + find tmp -type f | xargs chmod a-x + for pn in tmp/*/pathname; do + id=${pn%/*} + id=${id#*/} + p=$(head -1 $pn) + d=${p%/*} + mkdir -p "tmp/$d" + [ -f "tmp/$id/asset" ] && cp -v "tmp/$id/asset" "tmp/$p" + cp "tmp/$id/asset.meta" "tmp/${p}.meta" + done + cp -R tmp/Assets/Pimax Assets/ + rm -rf tmp package.zip PimaxPlatform_v0.6.3.unitypackage Tools + + - name: Install Pico unity package + if: matrix.vrsdk == 'Pico' + run: | + # version 2.1.1 + 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: | + # version 3.0.6; must be updated if the version changes + # This replaces the GUI's "Window -> TextMesh Pro -> Import TMP Essential Resources". I don't know why Unity makes this sort of thing so hard! + mkdir tmp.plugin + wget -q https://download.packages.unity.com/com.unity.textmeshpro/-/com.unity.textmeshpro-3.0.6.tgz -O tmp.plugin/plugin.tgz + tar -C tmp.plugin -xzf tmp.plugin/plugin.tgz + mkdir tmp.package + tar -C tmp.package -xzf 'tmp.plugin/package/Package Resources/TMP Essential Resources.unitypackage' + for pn in tmp.package/*/pathname; do + id=${pn%/*} + id=${id#*/} + p=$(head -1 $pn) + d=${p%/*} + mkdir -p "tmp.package/$d" + [ -f "tmp.package/$id/asset" ] && cp -v "tmp.package/$id/asset" "tmp.package/$p" + cp "tmp.package/$id/asset.meta" "tmp.package/${p}.meta" + done + mkdir -p 'Assets/TextMesh Pro' + 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 + ref: Fusion_v1.1.10_Voice_2 + 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@v4 + env: + SEGMENT_DOWNLOAD_TIMEOUT_MINS: 10 + with: + path: Library + # Some platforms share a cache; it's not a 1:1 mapping of either targetPlatform or vrsdk, so we have a distinct variable for which cache to use + key: Library_${{ matrix.cache }}_${{ env.UNITY_VERSION }} + + - name: Restore Library/PackageCache + id: cache_packagecache + uses: actions/cache/restore@v4 + env: + SEGMENT_DOWNLOAD_TIMEOUT_MINS: 10 + with: + path: Library/PackageCache + key: Library_PackageCache_${{ env.UNITY_VERSION }}_${{ hashFiles('Packages/packages-lock.json') }} + restore-keys: | + Library_PackageCache_${{ env.UNITY_VERSION }} + Library_PackageCache + + - name: Remove problematic packages + if: ${{ matrix.packages_to_remove }} + run: | + cp Packages/manifest.json{,.bak} + cp Packages/packages-lock.json{,.bak} + for PACKAGE in ${{ matrix.packages_to_remove }}; do + cat Packages/manifest.json | jq 'del( .dependencies ["'${PACKAGE}'"] )' > Packages/manifest.json.new + mv Packages/manifest.json.new Packages/manifest.json + cat Packages/packages-lock.json | jq 'del( .dependencies ["'${PACKAGE}'"] )' > Packages/packages-lock.json.new + mv Packages/packages-lock.json.new Packages/packages-lock.json + done + diff -u Packages/manifest.json.bak Packages/manifest.json || true + diff -u Packages/packages-lock.json.bak Packages/packages-lock.json || true + + - name: Set output filename + env: + BASENAME: ${{ needs.configuration.outputs.basename }} + run: | + if [[ "${{ matrix.targetPlatform}}" == "StandaloneWindows64" ]]; then + echo "filename=$BASENAME.exe" >> $GITHUB_ENV + elif [[ "${{ matrix.targetPlatform}}" == "StandaloneLinux64" ]]; then + echo "filename=$BASENAME" >> $GITHUB_ENV + elif [[ "${{ matrix.targetPlatform}}" == "iOS" ]]; then + echo "filename=$BASENAME" >> $GITHUB_ENV + elif [[ "${{ matrix.targetPlatform}}" == "StandaloneOSX" ]]; then + echo "filename=$BASENAME.app" >> $GITHUB_ENV + elif [[ "${{ matrix.targetPlatform}}" == "Android" ]]; then + echo "filename=com.Icosa.$BASENAME.apk" >> $GITHUB_ENV + fi + + - name: Set build stamp + if: ${{ needs.configuration.outputs.stamp }} + # We checkout the merge commit, but for the purpose of the tag, use the version from the PR, not the merge commit, which is rather hard to find later. We skip the version tag, since this comes from the code and can't be easily overwritten + run: | + echo "stamp=-btb-stamp ${{needs.configuration.outputs.stamp}}" >> $GITHUB_ENV + + - name: Enable Development Mode + if: ${{ matrix.flavors.development == true }} + run: | + echo "btbbopts=-btb-bopt Development" >> $GITHUB_ENV + + - name: Update version + env: + VERSION: ${{ needs.configuration.outputs.version}} + run: | + sed -e "s/m_VersionNumber:.*$/m_VersionNumber: $VERSION/" -i Assets/Scenes/Main.unity + sed -e "s/bundleVersion:.*$/bundleVersion: $VERSION/" -i ProjectSettings/ProjectSettings.asset + + - name: Add secure secrets file + env: + SECRETS_ASSET: ${{ secrets.SECRETS_ASSET }} + SECRETS_ASSET_META: ${{ secrets.SECRETS_ASSET_META }} + if: | + env.SECRETS_ASSET != null && + env.SECRETS_ASSET_META != null + run: | + echo "$SECRETS_ASSET" > Assets/Secrets.asset + echo "$SECRETS_ASSET_META" > Assets/Secrets.asset.meta + SECRETS_ASSET_META_GUID=$(grep "guid:" Assets/Secrets.asset.meta | cut -d" " -f2) + sed -e "s/Secrets:.*$/Secrets: {fileID: 11400000, guid: $SECRETS_ASSET_META_GUID, type: 2}/" -i Assets/Scenes/Main.unity + + - name: Enable keystore + run: | + sed -e 's/androidUseCustomKeystore.*$/androidUseCustomKeystore: 1/' -i ProjectSettings/ProjectSettings.asset + + - name: Add PHOTON_PAT specific define + if: ${{ env.PHOTON_PAT }} + run: | + echo -e "\n -define:PHOTON_UNITY_NETWORKING \n-define:PUN_2_0_OR_NEWER \n-define:PUN_2_OR_NEWER \n-define:PUN_2_19_OR_NEWER \n-define:FUSION_WEAVER \n-define:CROSS_PLATFORM_INPUT \n-define:MOBILE_INPUT \n-define:PHOTON_VOICE_DEFINED" | tee -a Assets/csc.rsp + + - name: Update build matrix specific defines in csc.rsp + if: ${{ matrix.extra_defines }} + run: | + for DEFINE in ${{ matrix.extra_defines }}; do + echo -e "\n-define:$DEFINE" | tee -a Assets/csc.rsp + done + + - name: Build project + uses: Wandalen/wretry.action@v3 + env: + VERSION: ${{ needs.configuration.outputs.version}} + UNITY_EMAIL: ${{ fromJSON(format('["unitytest@mikeage.net", "{0}"]', vars.UNITY_EMAIL))[secrets.UNITY_SERIAL != null] }} + UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }} + 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] }} + with: + retry_condition: steps._this.outputs.engineExitCode == 1 + action: game-ci/unity-builder@v4 + with: | + allowDirtyBuild: true # Because of the OVR Update, the build tree might be dirty + unityVersion: ${{ env.UNITY_VERSION }} + targetPlatform: ${{ matrix.targetPlatform }} + customParameters: -btb-target ${{ matrix.targetPlatform }} -btb-display ${{ matrix.vrsdk }} -btb-out /github/workspace/build/${{ matrix.vrsdk }}/${{ matrix.targetPlatform }}/${{ env.filename }} ${{ needs.configuration.outputs.description}} ${{ env.stamp }} ${{ env.btbbopts }} ${{ matrix.extraoptions }} + versioning: Custom + androidVersionCode: "${{ needs.configuration.outputs.androidVersionCode }}${{ matrix.versionSuffix }}" + version: ${{ needs.configuration.outputs.version }} + buildName: ${{ needs.configuration.outputs.basename }} + buildsPath: build/${{ matrix.vrsdk }} + chownFilesTo: ${{ needs.configuration.outputs.uid }}:${{ needs.configuration.outputs.gid }} + buildMethod: BuildTiltBrush.CommandLine + androidKeystoreName: openbrush.keystore + androidKeystoreBase64: ${{ secrets.ANDROID_KEYSTORE_BASE64 || '/u3+7QAAAAIAAAABAAAAAQAWb3BlbmJydXNoLW5vbi1vZmZpY2lhbAAAAX66M2FtAAAFATCCBP0wDgYKKwYBBAEqAhEBAQUABIIE6Wufa9OVstw7Bu/gdATKqoPafXGefygChsN1d4LGY0SMLPORjHXiryEVMKi2rt61kNeXzeLkiM4yIQAam4HZtNTxgjoFQ6KB7uzkqMJYKViBUgg1HCAl2e+QpYjqG+YNJT67CiPgjpsJHNE628CwKAvjJ85FhqFz+MKzNF8BOpS5g5waqFda67oxaE4qO8eAL+F9P7us+ziY5B4O3EJC9s7xpT2GV2ro0m0fZI2dr3OO9UdUO72CYTg5qs250JiSij26Haf4t8Vq28F2S8rTcMUVtN4FRtzeR/wjeeZ3laER+WoxYni4MrZEXhYYCGhfor8Zcfi3p5ka8TJCQxywTKpghpSwgykgMJLn1HksxB0vhIMGTb87c2CTqS4t5Js/OPdcYS4Jnr7mHdQtOGfJCvl3TJC7NJwzLLOzUTmVIogaZCA9GlRballbD7XYbR8mcPxs+jLq5HJJk8/3B8ojAz/YA9vp6ml3RSYDA+yv9fBIefxNniAredJeqAnmH4o9er3+n0rKmpoqiXdzFkp1ywYbDDxrsFTiPrTc0gEiLRbfCERBx8GZ/7zGv6exKW1mc1L7QcFRmT1PRuJo6vRfCOtjdAdp0Mj1bllGGe9oBSKOxqtxs/NFygaVZjMDqryRvObKaJaj5CDhNdwsa21EsQ3+YvQWBzlcs5FTi5S2zG3W4+tMb+HoyV36SEV4yBLtqqrczhVCuPMlZu2p1iFLyODJJOxrWnmZy49BlQiudmiR7wILJoYKIFFvGv1jCJnTl9cI6UGX8IwSHYjGJIdLxaQM6c/7tw15+h+3jPajzZqkIQ7r0fyBp2TxE+QXMCP/knYu/dVzzQoBe5CgnAr5Fj60eEF78mJZbU3m9EjuVglURCTs2hDiyl3eRENgJjTc8p9iho4aK5eT5BVF7v2TAsTkfm+AwOq78chbWfh7J5OYnycG+v6S76LE6T8Yy0Arkk4lOF5SC05SmrDQpFcbRC9B7pR8XwJx3rabt4jvFsdqQtqv7TRasNQs95oROSC8335tzsaQfPwL/sGH4wi4zsH3YZ6As2V9myMEytqVEX5DdGBtzRr1opkx0aisyG48Evtk1UHMR9ROoZmkbNOIFNDUxCBvw7CU20aJSri4GX7kahg8Lj670Lfpx1C9OMwH0xRGUHE4e2ZWaw6Smkjc0Rru7j4YFKel0KtJgQaei2fz2i+6wOv1uz+H4j6f98pVMsf3HODmnh4x+qlUXaJWbNILQEGwv3zVReY123TPHIzkwImNLej62BLaqnEgiPkKr/gp/2MdrgepUEGC8FN0MTPbazDR4aE5XqLtnehhq8/9EfIk3b5WzNh00IAELwFrWnabkob5xmSLORBH8SpS3J6NwWa4jJMADRAGPYOUH7tYUM1/GRUK1HuboNP9v3KAny/k30CrxLvNHwe/zkXgoU9+M+gXVXL8pJJLMawVe/Dg13XyqTTa00UX7TsQFJZGm6lHrgeFIejKBEMLsMXNAIccphZe6sDnycDm/GY8vqmfjg9R05GwJOhBd46vhDi7Ph8YbLjohEoT4KfE5o8+Norzc/VHbRv5Y+G6JCL6hRV72meb3LswLYGUzGYP4nh2Y/yixg+rAtre80xjbXFfdvXVF6CuibKn5gmjCmiRN31rvEfdwVPIQDCaqv19Do2cQYDN+yGCo7yDHAAAAAEABVguNTA5AAADhzCCA4MwggJroAMCAQICBGNtJJswDQYJKoZIhvcNAQELBQAwcTELMAkGA1UEBhMCR0IxFzAVBgNVBAgTDldlc3QgWW9ya3NoaXJlMQ4wDAYDVQQHEwVMZWVkczEOMAwGA1UEChMFSWNvc2ExEzARBgNVBAsTCk9wZW4gQnJ1c2gxFDASBgNVBAMTC01pa2UgTWlsbGVyMCAXDTIyMDIwMjExMzAyNVoYDzIwNzIwMTIxMTEzMDI1WjBxMQswCQYDVQQGEwJHQjEXMBUGA1UECBMOV2VzdCBZb3Jrc2hpcmUxDjAMBgNVBAcTBUxlZWRzMQ4wDAYDVQQKEwVJY29zYTETMBEGA1UECxMKT3BlbiBCcnVzaDEUMBIGA1UEAxMLTWlrZSBNaWxsZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCZOlUSd2Z9VSuVE1NK2AKiKCYR3ADh3f3PN6ipTtqUdxP44l5jJnPVXc5YXJ4DyBsXHGTqCSiL9wiqdRCNTMcRf6vrpcuRWxqwMMu4bid0eDiFBU+wModQl70N0VblMolYZzD/y0NpXWh7VKPSXyA22ZwygeOPQFzxR4j2jRvM/g+9HeJeVN1p5f+6pvceg/9FBSCEOQg5fbDtO+ytZfMiawcyhSSwwlOzEOGT0Dq6d9xIs1/zTA8LxAlGYHLSpQCT/n3X27LNgUMNrCpWgLTtxH/qQ61NU3juqTqBBWT4nzTXl1J9JyPaHH1yzC908YiI5PQSFehX80KTvsf0B65DAgMBAAGjITAfMB0GA1UdDgQWBBTThSJ0yfVNgUC4h3Sa9o8aUmLY3jANBgkqhkiG9w0BAQsFAAOCAQEAUqE9NJA+PaMBrCcVHkxmk32DsVNIVCM/eaTPCyjBM3V5COgxscven160OKGHRn6Xhplr/UDy+StphE9Hwk8MAwSJ4reBdPiNMQvIsDEQ/aXSAyTiKQeIU5Zc+cYuJvHcyxIOVektDe8Er2AITvpXQDK1JRvYU6lFKym3j/CZ4comUwjdolB1C6fzlTkhP3ZuuFMfv543WyuVtb3A1mioLzQ5kfFlbTO0uXqEm+gltkK8AMqU6B5RJDYtQXIJkjR//UzNgpaILVvQ4pyyS6VvBNbUbrHaUKabtP3daDtQ0AQw3gSkCJ+QPpY9joIq38LMcVY5/x5/nbcxTuYvUlHozn/+qtNvA7MtikSNPcblNpmifg4o' }} + androidKeystorePass: ${{ secrets.ANDROID_KEYSTORE_PASS || 'FakeKey' }} + androidKeyaliasName: ${{ secrets.ANDROID_KEYALIAS_NAME || 'openbrush-non-official' }} + androidKeyaliasPass: ${{ secrets.ANDROID_KEYALIAS_PASS || 'FakeKey' }} + + - name: Prepare for packaging (permissions and compression, OSX only) + if: matrix.targetPlatform == 'StandaloneOSX' + run: | + mv build/${{ matrix.vrsdk }}/${{ matrix.targetPlatform }}/Support "build/${{ matrix.vrsdk }}/${{ matrix.targetPlatform }}/${{ env.filename }}/Contents/" + find build -name 'UnityFbxSdkNative.bundle' -delete + # Compress, but skip the top directories + tar -c -v -z -f OpenBrush.tgz -C build/${{ matrix.vrsdk }} ${{ matrix.targetPlatform}} + rm -rf build/${{ matrix.vrsdk }}/* + mv OpenBrush.tgz build/${{ matrix.vrsdk }}/ + + - name: Upload build/ + uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.name }} ${{ matrix.flavors.title }} + path: | + build/${{ matrix.vrsdk }} + !build/Pico/*.symbols.zip + !build/**/*_BackUpThisFolder_ButDontShipItWithYourGame + + - name: Check if packages-lock.json has changed or if it's cacheable + id: check_packagecache + run: | + # Check if there are any changes to the packages-lock.json file + set +e + git diff --exit-code -- Packages/packages-lock.json + CHANGES="$?" + set -e + echo "changes=$CHANGES" >> $GITHUB_OUTPUT + echo "diff returned: $CHANGES" + + - name: Save Library/PackageCache cache + uses: actions/cache/save@v4 + if: github.ref == 'refs/heads/main' && steps.check_packagecache.outputs.changes == 0 && steps.cache_packagecache.outputs.cache-hit != 'true' && ! matrix.packages_to_remove # Ideally, we'd save caches on branches, but they're too big, and branch caches can evict those from main, which is unacceptable. + env: + SEGMENT_DOWNLOAD_TIMEOUT_MINS: 10 + with: + path: Library/PackageCache + key: Library_PackageCache_${{ env.UNITY_VERSION }}_${{ hashFiles('Packages/packages-lock.json') }} + + - name: Clean Library before caching + if: github.ref == 'refs/heads/main' && steps.cache_library.outputs.cache-hit != 'true' # Ideally, we'd save caches on branches, but they're too big, and branch caches can evict those from main, which is unacceptable. + run: | + # Remove the large files from the Library directory that we know we'll rebuild. As our il2cpp caches are huge and barely fit in the Github quota, it's better not to save an unneeded 1GB of space (or so). If a new Unity version is taken, this may need to be updated + # Debugging + echo "Library/ directories" + du -mcsh Library/* + find Library -size +50M -exec ls -altrh {} \; + # chown all files, since some are owned by root after the docker run + docker run -v $(pwd)/Library:/mnt alpine chown $(id -u).$(id -g) -R /mnt/ + # Print the files to be deleted + find Library/Bee/ -name 'symbols.zip' -or -name 'libil2cpp*.so' -or -name 'launcher-release.apk' | tee todelete.txt + cat todelete.txt | xargs -r rm + # The package cache is stored in a separate, shared, cache + rm -rf Library/PackageCache + echo "Final space used" + du -mcsh Library + + - name: Save Library/ cache + uses: actions/cache/save@v4 + if: github.ref == 'refs/heads/main' && steps.cache_library.outputs.cache-hit != 'true' # Ideally, we'd save caches on branches, but they're too big, and branch caches can evict those from main, which is unacceptable. + env: + SEGMENT_DOWNLOAD_TIMEOUT_MINS: 10 + with: + path: Library + # Some platforms share a cache; it's not a 1:1 mapping of either targetPlatform or vrsdk, so we have a distinct variable for which cache to use + key: Library_${{ matrix.cache }}_${{ env.UNITY_VERSION }} + + signmacos: + name: Sign the MacOS .app + needs: [configuration, build] + if: | + github.event_name == 'push' && + github.repository == 'icosa-foundation/open-brush' && + (github.ref == 'refs/heads/main' || contains(github.ref, 'refs/tags/v')) + runs-on: macos-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + lfs: true # We don't use LFS, but it adds no time, and leave it here in case we do at some point later + sparse-checkout: | + Support/macos + + - name: Download Build Artifacts + uses: actions/download-artifact@v4 + with: + name: MacOS + path: build_macos + + # See https://docs.github.com/en/actions/deployment/deploying-xcode-applications/installing-an-apple-certificate-on-macos-runners-for-xcode-development + - name: Install the Apple certificate and provisioning profile + env: + BUILD_CERTIFICATE_BASE64: ${{ secrets.APPLE_BUILD_CERTIFICATE_BASE64 }} + INSTALL_CERTIFICATE_BASE64: ${{ secrets.APPLE_INSTALL_CERTIFICATE_BASE64 }} + P12_PASSWORD: ${{ secrets.APPLE_P12_PASSWORD }} + BUILD_PROVISION_PROFILE_BASE64: ${{ secrets.APPLE_BUILD_PROVISION_PROFILE_BASE64 }} + KEYCHAIN_PASSWORD: ${{ secrets.APPLE_KEYCHAIN_PASSWORD }} + run: | + BUILD_CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12 + INSTALL_CERTIFICATE_PATH=$RUNNER_TEMP/install_certificate.p12 + PP_PATH=$RUNNER_TEMP/openbrushmac.provisionprofile + KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db + + echo -n "$BUILD_CERTIFICATE_BASE64" | base64 --decode -o $BUILD_CERTIFICATE_PATH + echo -n "$INSTALL_CERTIFICATE_BASE64" | base64 --decode -o $INSTALL_CERTIFICATE_PATH + echo -n "$BUILD_PROVISION_PROFILE_BASE64" | base64 --decode -o $PP_PATH + + # create temporary keychain + security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH + security set-keychain-settings -lut 21600 $KEYCHAIN_PATH + security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH + + # import certificate to keychain + security import $BUILD_CERTIFICATE_PATH -P "$P12_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH + security import $INSTALL_CERTIFICATE_PATH -P "$P12_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH + security list-keychain -d user -s $KEYCHAIN_PATH + + mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles + cp $PP_PATH ~/Library/MobileDevice/Provisioning\ Profiles + + - name: Sign the release + env: + VERSION: ${{ needs.configuration.outputs.version }} + run: | + tar xvfz build_macos/*tgz + + export FILENAME=$(basename $(readlink -f StandaloneOSX/OpenBrush*.app)) + + cd StandaloneOSX/ + codesign --deep --force --verify --verbose --timestamp --options runtime --entitlements ../Support/macos/OpenBrush.entitlements --sign "Developer ID Application: Icosa Gallery Ltd (${{ vars.APPLE_TEAM_ID }})" $FILENAME/Contents/Support/ThirdParty/ffmpeg/bin/ffmpeg + for bundle in $FILENAME/Contents/Plugins/*.bundle; do + codesign --deep --force --verify --verbose --timestamp --options runtime --entitlements ../Support/macos/OpenBrush.entitlements --sign "Developer ID Application: Icosa Gallery Ltd (${{ vars.APPLE_TEAM_ID }})" $bundle + done + codesign --deep --force --verify --verbose --timestamp --options runtime --entitlements ../Support/macos/OpenBrush.entitlements --sign "Developer ID Application: Icosa Gallery Ltd (${{ vars.APPLE_TEAM_ID }})" $FILENAME + cd - + + tar -c -v -z -f OpenBrush.tgz StandaloneOSX + - name: Upload signed app + uses: actions/upload-artifact@v4 + with: + name: MacOS (signed) + path: | + OpenBrush.tgz + + createdmg: + name: Create and Notarize DMG + needs: [configuration, signmacos] + if: | + github.event_name == 'push' && + github.repository == 'icosa-foundation/open-brush' && + (github.ref == 'refs/heads/main' || contains(github.ref, 'refs/tags/v')) + + runs-on: macos-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + lfs: true # We don't use LFS, but it adds no time, and leave it here in case we do at some point later + sparse-checkout: | + Support/macos + + - name: Download Build Artifacts + uses: actions/download-artifact@v4 + with: + name: MacOS (signed) + path: build_macos + + - name: Create a notarized DMG + env: + VERSION: ${{ needs.configuration.outputs.version }} + run: | + tar xvfz build_macos/*tgz + + export FILENAME=$(basename $(readlink -f StandaloneOSX/OpenBrush*.app)) + mkdir dist + + cp Support/macos/background.png StandaloneOSX/ + cp Support/macos/background@2x.png StandaloneOSX/ + pip install jinjanator + jinjanate Support/macos/openbrush-dmg.json.j2 > StandaloneOSX/openbrush-dmg.json + pushd StandaloneOSX/ + npx appdmg openbrush-dmg.json ../dist/OpenBrush.dmg + popd + + xcrun notarytool submit \ + --team-id ${{ vars.APPLE_TEAM_ID }} \ + --apple-id ${{ vars.APPLE_ID }} \ + --password ${{ secrets.APPLE_APPLICATION_SPECIFIC_PASSWORD }} \ + --wait \ + dist/OpenBrush.dmg + + xcrun stapler staple dist/OpenBrush.dmg + + - name: Upload notarized dmg + uses: actions/upload-artifact@v4 + with: + name: MacOS (DMG) + path: | + dist/OpenBrush.dmg + + release: + name: Create Github Release + needs: [configuration, build, createdmg] + runs-on: ubuntu-latest + if: | + github.event_name == 'push' && + github.repository == 'icosa-foundation/open-brush' && + (github.ref == 'refs/heads/main' || contains(github.ref, 'refs/tags/v')) + + steps: + - name: "Build Changelog" + id: changelog + uses: mikepenz/release-changelog-builder-action@v5 + with: + fromTag: "${{ needs.configuration.outputs.previousrelease }}" + toTag: "${{ needs.configuration.outputs.currentrelease }}" + configurationJson: | + { + "categories": [ + { + "title": "## 🚀 Features", + "labels": ["feature", "enhancement"] + }, + { + "title": "## 🎨 UI / UX", + "labels": ["ux"] + }, + { + "title": "## 🐛 Fixes", + "labels": ["fix", "bugfix"] + }, + { + "title": "## 🛠️ Infrastructure", + "labels": ["infrastructure"] + }, + { + "title": "## 📦 Dependencies / Maintenance", + "labels": ["dependencies", "maintenance"] + }, + { + "title": "## 💬 Uncategorized", + "labels": [] + } + ], + "pr_template": "- #{{TITLE}} (PR ##{{NUMBER}} by @#{{AUTHOR}})" + } + + - name: Echo Changelog (for debugging purposes) + env: + CHANGELOG: ${{ steps.changelog.outputs.changelog }} + run: echo "$CHANGELOG" + + - name: Download Build Artifacts (Windows OpenXR) + uses: actions/download-artifact@v4 + with: + name: Windows OpenXR + path: build_windows_openxr + + - name: Download Build Artifacts (Windows Rift) + uses: actions/download-artifact@v4 + with: + name: Windows Rift + path: build_windows_rift + + - name: Download Build Artifacts (Android OpenXR) + uses: actions/download-artifact@v4 + with: + name: Android OpenXR + path: build_android_openxr + + - name: Download Build Artifacts (Oculus Quest 2+) + uses: actions/download-artifact@v4 + with: + name: Oculus Quest (2+) + path: build_oculus_quest + + - name: Download Build Artifacts (Pico) + uses: actions/download-artifact@v4 + with: + name: Android Pico + path: build_android_pico + + - name: Download Build Artifacts (Linux) + uses: actions/download-artifact@v4 + with: + name: Linux + path: build_linux + + - name: Download Build Artifacts (Mac) + uses: actions/download-artifact@v4 + with: + name: MacOS (DMG) + path: build_macos + + - name: Package Artifacts for release + env: + VERSION: ${{ needs.configuration.outputs.version }} + run: | + mkdir releases + mv build_oculus_quest/*/com.Icosa.OpenBrush*apk releases/OpenBrush_Quest_$VERSION.apk + mv build_android_openxr/*/com.Icosa.OpenBrush*apk releases/OpenBrush_OpenXR_$VERSION.apk + mv build_android_pico/*/com.Icosa.OpenBrush*apk releases/OpenBrush_Pico_$VERSION.apk + mv build_windows_openxr/StandaloneWindows64/ releases/OpenBrush_Desktop_$VERSION/ + mv build_windows_rift/StandaloneWindows64/ releases/OpenBrush_Rift_$VERSION/ + mv build_macos/*.dmg releases/OpenBrush_Mac_$VERSION.dmg + mv build_linux/StandaloneLinux64/ releases/OpenBrush_Linux_$VERSION/ + cd releases + zip -r OpenBrush_Desktop_$VERSION.zip OpenBrush_Desktop_$VERSION/ + rm -rf OpenBrush_Desktop_$VERSION + zip -r OpenBrush_Rift_$VERSION.zip OpenBrush_Rift_$VERSION/ + rm -rf OpenBrush_Rift_$VERSION + zip -r OpenBrush_Linux_$VERSION.zip OpenBrush_Linux_$VERSION/ + + - name: Publish + uses: softprops/action-gh-release@v2 + with: + body: ${{ steps.changelog.outputs.changelog }} + prerelease: ${{ needs.configuration.outputs.prerelease }} + target_commitish: ${{ needs.configuration.outputs.currentrelease }} + tag_name: ${{ needs.configuration.outputs.version }} + files: releases/* + token: ${{ secrets.RELEASE_TOKEN }} + + publish_gitbook: + name: Publish changelog from last major build to open-brush-docs + needs: [configuration, build] + runs-on: ubuntu-latest + if: | + github.event_name == 'push' && + github.repository == 'icosa-foundation/open-brush' && + (github.ref == 'refs/heads/main' || contains(github.ref, 'refs/tags/v')) + steps: + - name: "Build Changelog" + id: changelog + uses: mikepenz/release-changelog-builder-action@v5 + with: + fromTag: "${{ needs.configuration.outputs.previousfullrelease }}" + toTag: "${{ needs.configuration.outputs.currentrelease }}" + configurationJson: | + { + "categories": [ + { + "title": "## 🚀 Features", + "labels": ["feature", "enhancement"] + }, + { + "title": "## 🎨 UI / UX", + "labels": ["ux"] + }, + { + "title": "## 🐛 Fixes", + "labels": ["fix", "bugfix"] + }, + { + "title": "## 🛠️ Infrastructure", + "labels": ["infrastructure"] + }, + { + "title": "## 📦 Dependencies / Maintenance", + "labels": ["dependencies", "maintenance"] + }, + { + "title": "## 💬 Uncategorized", + "labels": [] + } + ], + "template": "# Changelog since #{{FROM_TAG}}\n\n[Full release details](#{{RELEASE_DIFF}})\n\n#{{CHANGELOG}}\n\n", + "pr_template": "- #{{TITLE}} ([PR ##{{NUMBER}}](#{{URL}}) by @#{{AUTHOR}})\n" + } + + - name: Get the current contents of the docs repository + uses: actions/checkout@v4 + with: + repository: icosa-foundation/open-brush-docs + path: open-brush-docs + ref: master + fetch-depth: 0 + sparse-checkout: | + release-history/ + + - name: Create Changelog file + env: + CHANGELOG: ${{ steps.changelog.outputs.changelog }} + run: | + echo "$CHANGELOG" | tee open-brush-docs/release-history/automatic-changelog.md + + - name: Publish release notes + uses: cpina/github-action-push-to-another-repository@composite-1.5.1 + env: + SSH_DEPLOY_KEY: ${{ secrets.OPENBRUSH_DOCS_SSH_DEPLOY_KEY }} + with: + source-directory: 'open-brush-docs/release-history/' + target-directory: 'release-history/' + destination-github-username: 'icosa-foundation' + destination-repository-name: 'open-brush-docs' + user-name: 'release-note-bot' + user-email: automatic-release@icosa + target-branch: master + + publish_steam: + name: Publish Steam Release + needs: [configuration, build, signmacos] + if: | + github.event_name == 'push' && + github.repository == 'icosa-foundation/open-brush' && + (github.ref == 'refs/heads/main' || contains(github.ref, 'refs/tags/v')) + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + sparse-checkout: | + Support/steam + lfs: true # We don't use LFS, but it adds no time, and leave it here in case we do at some point later + - name: Setup steamcmd + uses: CyberAndrii/setup-steamcmd@v1.2.0 + - name: Restore steam login config + run: | + mkdir -p /home/runner/Steam/config + echo "${{ secrets.STEAM_CONFIG_VDF}}" | base64 -d - | gunzip - > /home/runner/Steam/config/config.vdf + md5sum /home/runner/Steam/config/config.vdf + - name: Download Build Artifacts (Windows OpenXR) + uses: actions/download-artifact@v4 + with: + name: Windows OpenXR + path: build_windows_openxr + - name: Download Build Artifacts (MacOS Signed) + uses: actions/download-artifact@v4 + with: + name: MacOS (signed) + path: build_macos + - name: Download Build Artifacts (Linux) + uses: actions/download-artifact@v4 + with: + name: Linux + path: build_linux + - name: Upload Build + run: | + cd build_macos + tar xvfz OpenBrush.tgz + cd - + pip install -U jinjanator + jinjanate Support/steam/app.vdf.j2 > app.vdf + jinjanate Support/steam/main_depot.win.vdf.j2 > build_windows_openxr/main_depot.vdf + jinjanate Support/steam/main_depot.mac.vdf.j2 > build_macos/main_depot.vdf + jinjanate Support/steam/main_depot.linux.vdf.j2 > build_linux/main_depot.vdf + jinjanate Support/steam/installscript_win.vdf.j2 > build_windows_openxr/installscript_win.vdf + steamcmd +login $STEAM_USERNAME +run_app_build $(pwd)/app.vdf +quit + env: + STEAM_USERNAME: ${{ vars.STEAM_USERNAME }} + STEAM_PASSWORD: ${{ secrets.STEAM_PASSWORD }} + VERSION: ${{ needs.configuration.outputs.version }} + OPEN_BRUSH_APP_ID: ${{ vars.STEAM_APP_ID }} + OPEN_BRUSH_WINDOWS_DEPOT_ID: ${{ vars.STEAM_WINDOWS_DEPOT_ID }} + OPEN_BRUSH_MAC_DEPOT_ID: ${{ vars.STEAM_MAC_DEPOT_ID }} + OPEN_BRUSH_LINUX_DEPOT_ID: ${{ vars.STEAM_LINUX_DEPOT_ID }} + OPEN_BRUSH_WINDOWS_EXECUTABLE: ${{ needs.configuration.outputs.basename}}.exe + CHANNEL: beta + - name: Update steam login secret + run: | + gzip /home/runner/Steam/config/config.vdf -c | base64 | gh secret set --visibility all --org icosa-foundation STEAM_CONFIG_VDF + md5sum /home/runner/Steam/config/config.vdf + env: + GITHUB_TOKEN: ${{ secrets.SECRET_UPDATER_PAT }} + - name: Save logs + uses: actions/upload-artifact@v4 + if: ${{ failure() }} + with: + name: steamcmd logs + path: build_output/ + + publish_pimax: + name: Publish Pimax Release + needs: [configuration, build] + runs-on: windows-latest + env: + PIMAX_APP_ID: ${{ vars.PIMAX_APP_ID }} + PIMAX_USERNAME: ${{ vars.PIMAX_USERNAME }} + PIMAX_PASSWORD: ${{ secrets.PIMAX_PASSWORD }} + VERSION: ${{ needs.configuration.outputs.version}} + if: | + github.event_name == 'push' && + github.repository == 'icosa-foundation/open-brush' && + contains(github.ref, 'refs/tags/v') + + steps: + - name: Download Build Artifacts (Windows Pimax) + uses: actions/download-artifact@v4 + with: + name: Windows Pimax + path: build_windows_pimax + + - name: Publish Pimax Builds + run: | + New-Item "releases" -Type Directory + Move-Item -Path build_windows_pimax/StandaloneWindows64/* releases/ + Set-Location -Path "releases" + Compress-Archive -Path ./* -DestinationPath OpenBrush_Pimax_${Env:VERSION}.zip + Invoke-WebRequest -Uri https://dl.appstore.pimax.com/tools/pimax-dev-util.exe -OutFile pimax-dev-util.exe + ./pimax-dev-util.exe login -u $Env:PIMAX_USERNAME -p $Env:PIMAX_PASSWORD + ./pimax-dev-util.exe upload-pc-build -a $Env:PIMAX_APP_ID -d OpenBrush_Pimax_${Env:VERSION}.zip + + publish_viveport: + name: Publish Viveport Release + needs: [configuration, build] + runs-on: windows-latest + env: + VIVEPORT_EMAIL: ${{ vars.VIVEPORT_EMAIL }} + VIVEPORT_PASSWORD: ${{ secrets.VIVEPORT_PASSWORD }} + VIVEPORT_APP_ID: ${{ vars.VIVEPORT_APP_ID }} + VIVEPORT_ORG_ID: ${{ vars.VIVEPORT_ORG_ID }} + VERSION: ${{ needs.configuration.outputs.version}} + if: | + github.event_name == 'push' && + github.repository == 'icosa-foundation/open-brush' && + contains(github.ref, 'refs/tags/v') + + steps: + - name: Download Build Artifacts (Windows OpenXR) + uses: actions/download-artifact@v4 + with: + name: Windows OpenXR + path: build_windows_openxr + + - name: Publish Viveport Builds + run: | + New-Item "releases" -Type Directory + Move-Item -Path build_windows_openxr/StandaloneWindows64/* releases/ + Set-Location -Path "releases" + Compress-Archive -Path ./* -DestinationPath OpenBrush_Viveport_${Env:VERSION}.zip + Invoke-WebRequest -Uri https://assets-global.viveport.com/static-misc/developer-tool/ViveportUploader.exe -OutFile ViveportUploader.exe + ./ViveportUploader.exe -email ${Env:VIVEPORT_EMAIL} -password ${Env:VIVEPORT_PASSWORD} -filepath OpenBrush_Viveport_${Env:VERSION}.zip -appid ${Env:VIVEPORT_APP_ID} -orgid ${Env:VIVEPORT_ORG_ID} -version ${VERSION} + + publish_itch: + name: Publish Itch.io Release + needs: [configuration, build] + runs-on: ubuntu-latest + env: + ITCH_SUBCHANNEL_NAME: ${{ needs.configuration.outputs.itchchannelname }} + BUTLER_CREDENTIALS: ${{ secrets.BUTLER_CREDENTIALS }} + ITCH_GAME: openbrush + ITCH_USER: openbrush + VERSION: ${{ needs.configuration.outputs.version }} + if: | + github.event_name == 'push' && + github.repository == 'icosa-foundation/open-brush' && + (github.ref == 'refs/heads/main' || contains(github.ref, 'refs/tags/v')) + + steps: + - name: Download Build Artifacts (Windows OpenXR) + uses: actions/download-artifact@v4 + with: + name: Windows OpenXR + path: build_windows_openxr + + - name: Download Build Artifacts (Oculus Quest 2+) + uses: actions/download-artifact@v4 + with: + name: Oculus Quest (2+) + path: build_oculus_quest + + - name: Package Artifacts for release + run: | + mkdir releases + mv build_oculus_quest/*/com.Icosa.OpenBrush*apk releases/OpenBrush_Quest_$VERSION.apk + mv build_windows_openxr/StandaloneWindows64/ releases/OpenBrush_Desktop_$VERSION/ + - name: Publish Windows + uses: josephbmanley/butler-publish-itchio-action@master + env: + CHANNEL: windows-${{ env.ITCH_SUBCHANNEL_NAME }} + PACKAGE: releases/OpenBrush_Desktop_${{ needs.configuration.outputs.version }} + - name: Publish Quest + uses: josephbmanley/butler-publish-itchio-action@master + env: + CHANNEL: android-quest-${{ env.ITCH_SUBCHANNEL_NAME }} + PACKAGE: releases/OpenBrush_Quest_${{ needs.configuration.outputs.version }}.apk + + publish_oculus_quest: + name: Publish Oculus Quest 2+ Release + needs: [configuration, build] + runs-on: macos-latest # the ovr-platform-util tool is only available for Mac and Windows + if: | + github.event_name == 'push' && + github.repository == 'icosa-foundation/open-brush' && + (github.ref == 'refs/heads/main' || contains(github.ref, 'refs/tags/v')) + + steps: + - name: Download Build Artifacts (Oculus Quest 2+) + uses: actions/download-artifact@v4 + with: + name: Oculus Quest (2+) + path: build_oculus_quest + - name: Publish Oculus Builds + env: + VERSION: ${{ needs.configuration.outputs.version }} + PRERELEASE: ${{ needs.configuration.outputs.prerelease }} + RAW_CHANGELOG: ${{ needs.configuration.outputs.rawchangelog }} + OCULUS_QUEST_APP_ID: ${{ vars.OCULUS_QUEST_APP_ID }} + OCULUS_QUEST_APP_SECRET: ${{ secrets.OCULUS_QUEST_APP_SECRET }} + run: | + mkdir releases + mv build_oculus_quest/*/com.Icosa.OpenBrush*apk releases/OpenBrush_Quest_$VERSION.apk + mv build_oculus_quest/*/com.Icosa.OpenBrush*.symbols.zip releases/symbols.zip + + cd releases + unzip symbols.zip + curl -L 'https://www.oculus.com/download_app/?id=1462426033810370' -o ovr-platform-util + chmod 755 ovr-platform-util + + if [ "$PRERELEASE" == "false" ] + then + ./ovr-platform-util upload-quest-build --app-id ${OCULUS_QUEST_APP_ID} --app-secret ${OCULUS_QUEST_APP_SECRET} --apk OpenBrush_Quest_$VERSION.apk --channel LIVE:quest2+ --debug_symbols_dir ./arm64-v8a/ --debug-symbols-pattern '*.so' --age-group MIXED_AGES + else + CHANGELOG="${RAW_CHANGELOG}" + ./ovr-platform-util upload-quest-build --app-id ${OCULUS_QUEST_APP_ID} --app-secret ${OCULUS_QUEST_APP_SECRET} --apk OpenBrush_Quest_$VERSION.apk --channel Beta:quest2+ --debug_symbols_dir ./arm64-v8a/ --debug-symbols-pattern '*.so' --notes "${CHANGELOG}" --age-group MIXED_AGES + fi + + publish_oculus_quest1: + name: Publish Oculus Quest 1 Release + needs: [configuration, build] + runs-on: macos-latest # the ovr-platform-util tool is only available for Mac and Windows + if: | + github.event_name == 'push' && + github.repository == 'icosa-foundation/open-brush' && + (github.ref == 'refs/heads/main' || contains(github.ref, 'refs/tags/v')) + + steps: + - name: Download Build Artifacts (Oculus Quest 1) + uses: actions/download-artifact@v4 + with: + name: Oculus Quest (1) + path: build_oculus_quest1 + - name: Publish Oculus Builds + env: + VERSION: ${{ needs.configuration.outputs.version }} + PRERELEASE: ${{ needs.configuration.outputs.prerelease }} + RAW_CHANGELOG: ${{ needs.configuration.outputs.rawchangelog }} + OCULUS_QUEST_APP_ID: ${{ vars.OCULUS_QUEST_APP_ID }} + OCULUS_QUEST_APP_SECRET: ${{ secrets.OCULUS_QUEST_APP_SECRET }} + run: | + mkdir releases1 + mv build_oculus_quest1/*/com.Icosa.OpenBrush*apk releases1/OpenBrush_Quest1_$VERSION.apk + mv build_oculus_quest1/*/com.Icosa.OpenBrush*.symbols.zip releases1/symbols.zip + + cd releases1 + unzip symbols.zip + curl -L 'https://www.oculus.com/download_app/?id=1462426033810370' -o ovr-platform-util + chmod 755 ovr-platform-util + + if [ "$PRERELEASE" == "false" ] + then + ./ovr-platform-util upload-quest-build --app-id ${OCULUS_QUEST_APP_ID} --app-secret ${OCULUS_QUEST_APP_SECRET} --apk OpenBrush_Quest1_$VERSION.apk --channel LIVE:quest1only --debug_symbols_dir ./arm64-v8a/ --debug-symbols-pattern '*.so' --age-group MIXED_AGES + else + CHANGELOG="${RAW_CHANGELOG}" + ./ovr-platform-util upload-quest-build --app-id ${OCULUS_QUEST_APP_ID} --app-secret ${OCULUS_QUEST_APP_SECRET} --apk OpenBrush_Quest1_$VERSION.apk --channel Beta:quest1only --debug_symbols_dir ./arm64-v8a/ --debug-symbols-pattern '*.so' --notes "${CHANGELOG}" --age-group MIXED_AGES + fi + + publish_oculus_rift: + name: Publish Oculus Rift Release + needs: [configuration, build] + runs-on: macos-latest # the ovr-platform-util tool is only available for Mac and Windows + if: | + github.event_name == 'push' && + github.repository == 'icosa-foundation/open-brush' && + (github.ref == 'refs/heads/main' || contains(github.ref, 'refs/tags/v')) + + steps: + - name: Download Build Artifacts (Windows Rift) + uses: actions/download-artifact@v4 + with: + name: Windows Rift + path: build_windows_rift + - name: Publish Oculus Builds + env: + VERSION: ${{ needs.configuration.outputs.version }} + PRERELEASE: ${{ needs.configuration.outputs.prerelease }} + RAW_CHANGELOG: ${{ needs.configuration.outputs.rawchangelog }} + OCULUS_RIFT_APP_ID: ${{ vars.OCULUS_RIFT_APP_ID }} + OCULUS_RIFT_APP_SECRET: ${{ secrets.OCULUS_RIFT_APP_SECRET }} + run: | + mkdir releases + mv build_windows_rift/StandaloneWindows64/ releases/OpenBrush_Rift_$VERSION/ + cd releases + zip -r OpenBrush_Rift_$VERSION.zip OpenBrush_Rift_$VERSION/ + curl -L 'https://www.oculus.com/download_app/?id=1462426033810370' -o ovr-platform-util + chmod 755 ovr-platform-util + + if [ "$PRERELEASE" == "false" ] + then + ./ovr-platform-util upload-rift-build --app-id ${OCULUS_RIFT_APP_ID} --app-secret ${OCULUS_RIFT_APP_SECRET} --build-dir OpenBrush_Rift_$VERSION --launch-file OpenBrush.exe --channel LIVE --version $VERSION --firewall_exceptions true --redistributables 822786567843179,1675031999409058,2657209094360789 + else + CHANGELOG="${RAW_CHANGELOG}" + ./ovr-platform-util upload-rift-build --app-id ${OCULUS_RIFT_APP_ID} --app-secret ${OCULUS_RIFT_APP_SECRET} --build-dir OpenBrush_Rift_$VERSION --launch-file OpenBrush.exe --channel BETA --version $VERSION --firewall_exceptions true --redistributables 822786567843179,1675031999409058,2657209094360789 --notes "${CHANGELOG}" + fi + + publish_pico: + name: Publish Pico Releases + needs: [configuration, build] + runs-on: ubuntu-latest # the ovr-platform-util tool is only available for Mac and Windows + if: | + github.event_name == 'push' && + github.repository == 'icosa-foundation/open-brush' && + (github.ref == 'refs/heads/main' || contains(github.ref, 'refs/tags/v')) + + steps: + - name: Download Build Artifacts (Android Pico) + uses: actions/download-artifact@v4 + with: + name: Android Pico + path: build_android_pico + - name: Download Build Artifacts (Android Pico CN) + uses: actions/download-artifact@v4 + with: + name: Android Pico (CN) + path: build_android_pico_cn + - name: Publish Pico Builds + env: + VERSION: ${{ needs.configuration.outputs.version }} + PRERELEASE: ${{ needs.configuration.outputs.prerelease }} + RAW_CHANGELOG: ${{ needs.configuration.outputs.rawchangelog }} + PICO_APP_ID: ${{ vars.PICO_APP_ID }} + PICO_APP_SECRET: ${{ secrets.PICO_APP_SECRET }} + run: | + mkdir releases + mv build_android_pico/*/com.Icosa.OpenBrush*apk releases/OpenBrush_Pico_$VERSION.apk + mv build_android_pico_cn/*/com.Icosa.OpenBrush*apk releases/OpenBrush_Pico_CN_$VERSION.apk + + cd releases + # pico-cli v1.0.3 + curl -L 'https://p16-platform-static-va.ibyteimg.com/tos-maliva-i-jo6vmmv194-us/linux-noncn/202304111056/pico-cli?r=1681181814847245000' -o pico-cli + chmod 755 pico-cli + + if [ "$PRERELEASE" == "false" ] + then + # The order here matters, because the Chinese build has a slightly higher version code due to the suffix of 1 vs 0 + ./pico-cli upload-build --app-id $PICO_APP_ID --app-secret $PICO_APP_SECRET --region noncn --apk OpenBrush_Pico_$VERSION.apk --channel 2 --notes-en "Version $VERSION" --device 'PICO Neo3,PICO Neo3 Pro,PICO Neo3 Eye,PICO 4' + ./pico-cli upload-build --app-id $PICO_APP_ID --app-secret $PICO_APP_SECRET --region noncn --apk OpenBrush_Pico_CN_$VERSION.apk --channel 1 --notes-en "Version $VERSION" --device 'PICO Neo3,PICO Neo3 Pro,PICO Neo3 Eye,PICO 4' + else + # For Pico, Beta channels can only get one build, not a separate China / non-China build + ./pico-cli upload-build --app-id $PICO_APP_ID --app-secret $PICO_APP_SECRET --region noncn --apk OpenBrush_Pico_$VERSION.apk --channel 3 --notes-en "Version $VERSION" --device 'PICO Neo3,PICO Neo3 Pro,PICO Neo3 Eye,PICO 4' + fi + + publish_ios_zapbox: + name: Publish Zapbox iOS + needs: [configuration, build] + runs-on: macos-latest + if: | + github.event_name == 'push' && + github.repository == 'icosa-foundation/open-brush' && + (github.ref == 'refs/heads/main' || contains(github.ref, 'refs/tags/v')) + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + sparse-checkout: | + Gemfile + Gemfile.lock + fastlane + + - name: Free extra space + # As of 02/08/2024, this increases free space from 21GB to 47GB + run: | + echo "Initial free space" + df -h + rm -rf "$AGENT_TOOLSDIRECTORY" + echo "Disk space after cleanup of \$AGENT_TOOLSDIRECTORY" + df -h + echo "Deleting all Xcode versions except 15.4" + find /Applications/Xcode_* -maxdepth 0 -type d ! -name 'Xcode_15.4.app' -exec rm -rf {} \; + df -h + find /Applications/Xcode* -name "*.app" -exec du -mcsh {} \; # Shows Xcode app sizes + + - name: Download iOS Artifact + uses: actions/download-artifact@v4 + with: + name: iOS Zapbox + path: build + + - name: Fix File Permissions + run: | + export FILENAME=$(basename $(readlink -f build/iOS/OpenBrush*)) + export IOS_BUILD_PATH=$(pwd)/build/iOS/${FILENAME} + + find $IOS_BUILD_PATH -type f -name "*.sh" -exec chmod +x {} \; + + - name: Run fastlane + env: + APPLE_CONNECT_EMAIL: ${{ secrets.APPLE_CONNECT_EMAIL }} + APPLE_DEVELOPER_EMAIL: ${{ secrets.APPLE_DEVELOPER_EMAIL }} + APPLE_TEAM_ID: ${{ vars.APPLE_TEAM_ID }} + + MATCH_REPOSITORY: ${{ secrets.MATCH_REPOSITORY }} + MATCH_DEPLOY_KEY: ${{ secrets.MATCH_DEPLOY_KEY }} + MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }} + + APPSTORE_ISSUER_ID: ${{ secrets.APPSTORE_ISSUER_ID }} + APPSTORE_KEY_ID: ${{ secrets.APPSTORE_KEY_ID }} + APPSTORE_P8: ${{ secrets.APPSTORE_P8 }} + + IOS_BUNDLE_ID: ${{ vars.IOS_ZAPBOX_BUNDLE_ID }} + PROJECT_NAME: Open Brush for Zapbox + FASTLANE_LANE: ${{ needs.configuration.outputs.fastlanelane }} + run: | + eval "$(ssh-agent -s)" + ssh-add - <<< "${MATCH_DEPLOY_KEY}" + export FILENAME=$(basename $(readlink -f build/iOS/OpenBrush*)) + export IOS_BUILD_PATH=$(pwd)/build/iOS/${FILENAME} + + bundle install + bundle exec fastlane ios ${FASTLANE_LANE} + + - name: Save logs + uses: actions/upload-artifact@v4 + if: ${{ failure() }} + with: + name: fastlane logs + path: /Users/runner/Library/Logs/gym/ diff --git a/.github/workflows/delete_branch_cache.yml b/.github/workflows/delete_branch_cache.yml index 756bdf3475..4436190127 100644 --- a/.github/workflows/delete_branch_cache.yml +++ b/.github/workflows/delete_branch_cache.yml @@ -1,40 +1,40 @@ ---- -# yamllint disable rule:line-length -# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json -# Taken from https://github.com/actions/cache/blob/main/tips-and-workarounds.md#force-deletion-of-caches-overriding-default-cache-eviction-policy -name: cleanup caches by a branch -on: # yamllint disable-line rule:truthy - pull_request: - types: - - closed - workflow_dispatch: - -jobs: - cleanup: - runs-on: ubuntu-latest - steps: - - name: Cleanup - run: | - gh extension install actions/gh-actions-cache - - set -x - REPO=${{ github.repository }} - # The github.ref should be refs/pull/XXX/merge, but for some reason, it was set to 'main' in an actual run. Debugging this would be a ginormous pain, since it can apparently only be properly done on an actual merge/close (when I tried testing it locally with a close, it *did* work!), and so we'll just set this manually and ignore it for now. Accidentally deleting the caches on main is unacceptable. - # BRANCH=${{ github.ref }} - BRANCH=${PR_TARGET_NAME} - - echo "Fetching list of cache key" - cacheKeysForPR=$(gh actions-cache list -R $REPO -B $BRANCH | cut -f 1 ) - - ## Setting this to not fail the workflow while deleting cache keys. - set +e - echo "Deleting caches..." - for cacheKey in $cacheKeysForPR - do - gh actions-cache delete $cacheKey -R $REPO -B $BRANCH --confirm - done - echo "Done" - env: - # Note that this can only run on branches from the repo; otherwise, we don't have write permissions so we can't delete the caches - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - PR_TARGET_NAME: ${{ format('refs/pull/{0}/merge', github.event.number) }} +--- +# yamllint disable rule:line-length +# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json +# Taken from https://github.com/actions/cache/blob/main/tips-and-workarounds.md#force-deletion-of-caches-overriding-default-cache-eviction-policy +name: cleanup caches by a branch +on: # yamllint disable-line rule:truthy + pull_request: + types: + - closed + workflow_dispatch: + +jobs: + cleanup: + runs-on: ubuntu-latest + steps: + - name: Cleanup + run: | + gh extension install actions/gh-actions-cache + + set -x + REPO=${{ github.repository }} + # The github.ref should be refs/pull/XXX/merge, but for some reason, it was set to 'main' in an actual run. Debugging this would be a ginormous pain, since it can apparently only be properly done on an actual merge/close (when I tried testing it locally with a close, it *did* work!), and so we'll just set this manually and ignore it for now. Accidentally deleting the caches on main is unacceptable. + # BRANCH=${{ github.ref }} + BRANCH=${PR_TARGET_NAME} + + echo "Fetching list of cache key" + cacheKeysForPR=$(gh actions-cache list -R $REPO -B $BRANCH | cut -f 1 ) + + ## Setting this to not fail the workflow while deleting cache keys. + set +e + echo "Deleting caches..." + for cacheKey in $cacheKeysForPR + do + gh actions-cache delete $cacheKey -R $REPO -B $BRANCH --confirm + done + echo "Done" + env: + # Note that this can only run on branches from the repo; otherwise, we don't have write permissions so we can't delete the caches + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PR_TARGET_NAME: ${{ format('refs/pull/{0}/merge', github.event.number) }} diff --git a/.github/workflows/export_secrets.yml b/.github/workflows/export_secrets.yml index 841cfa07f4..034f50b68b 100644 --- a/.github/workflows/export_secrets.yml +++ b/.github/workflows/export_secrets.yml @@ -1,33 +1,33 @@ ---- -# yamllint disable rule:line-length -# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json -name: Backup secrets (to OpenSSL encrypted file) -on: # yamllint disable-line rule:truthy - workflow_dispatch: - -jobs: - backup_secrets: - runs-on: ubuntu-latest - steps: - - name: Backup secrets - env: - SECRETS: ${{ toJSON(secrets) }} - VARS: ${{ toJSON(vars) }} - OPENSSL_ITER: 1000 - OPENSSL_PASS: ${{ secrets.SECRET_EXPORT_OPENSSL_PASSWORD }} - run: | - echo "$SECRETS" | tee secrets.txt - echo "$VARS" | tee vars.txt - openssl enc -aes-256-cbc -md sha512 -pbkdf2 -iter $OPENSSL_ITER -salt -in secrets.txt -out secrets.enc.txt -pass pass:$OPENSSL_PASS - openssl enc -aes-256-cbc -md sha512 -pbkdf2 -iter $OPENSSL_ITER -salt -in vars.txt -out vars.enc.txt -pass pass:$OPENSSL_PASS - echo "To decrypt the secrets, use the following command(s):" - echo "openssl enc -aes-256-cbc -d -md sha512 -pbkdf2 -iter $OPENSSL_ITER -salt -in secrets.enc.txt -out secrets.txt -pass pass:" - echo "openssl enc -aes-256-cbc -d -md sha512 -pbkdf2 -iter $OPENSSL_ITER -salt -in vars.enc.txt -out vars.txt -pass pass:" - - - name: Upload encrypted secrets - uses: actions/upload-artifact@v4 - with: - name: exports - path: | - secrets.enc.txt - vars.enc.txt +--- +# yamllint disable rule:line-length +# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json +name: Backup secrets (to OpenSSL encrypted file) +on: # yamllint disable-line rule:truthy + workflow_dispatch: + +jobs: + backup_secrets: + runs-on: ubuntu-latest + steps: + - name: Backup secrets + env: + SECRETS: ${{ toJSON(secrets) }} + VARS: ${{ toJSON(vars) }} + OPENSSL_ITER: 1000 + OPENSSL_PASS: ${{ secrets.SECRET_EXPORT_OPENSSL_PASSWORD }} + run: | + echo "$SECRETS" | tee secrets.txt + echo "$VARS" | tee vars.txt + openssl enc -aes-256-cbc -md sha512 -pbkdf2 -iter $OPENSSL_ITER -salt -in secrets.txt -out secrets.enc.txt -pass pass:$OPENSSL_PASS + openssl enc -aes-256-cbc -md sha512 -pbkdf2 -iter $OPENSSL_ITER -salt -in vars.txt -out vars.enc.txt -pass pass:$OPENSSL_PASS + echo "To decrypt the secrets, use the following command(s):" + echo "openssl enc -aes-256-cbc -d -md sha512 -pbkdf2 -iter $OPENSSL_ITER -salt -in secrets.enc.txt -out secrets.txt -pass pass:" + echo "openssl enc -aes-256-cbc -d -md sha512 -pbkdf2 -iter $OPENSSL_ITER -salt -in vars.enc.txt -out vars.txt -pass pass:" + + - name: Upload encrypted secrets + uses: actions/upload-artifact@v4 + with: + name: exports + path: | + secrets.enc.txt + vars.enc.txt diff --git a/.github/workflows/generate_certs.yml b/.github/workflows/generate_certs.yml index ba143640e3..ba20edbb87 100644 --- a/.github/workflows/generate_certs.yml +++ b/.github/workflows/generate_certs.yml @@ -1,47 +1,47 @@ ---- -# yamllint disable rule:line-length -# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json -name: Generate iOS Certs - -on: # yamllint disable-line rule:truthy - workflow_run: - workflows: ['iOS One-Time Setup'] - types: - - completed - workflow_dispatch: - -jobs: - generate_certs: - if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }} - runs-on: macos-latest - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - sparse-checkout: | - Gemfile - Gemfile.lock - fastlane - - uses: ruby/setup-ruby@v1 - with: - ruby-version: 3.2 - bundler-cache: true - - - name: Build iOS - shell: bash - run: | - eval "$(ssh-agent -s)" - ssh-add - <<< "${MATCH_DEPLOY_KEY}" - bundle exec fastlane ios sync_certificates - env: - APPSTORE_ISSUER_ID: ${{ secrets.APPSTORE_ISSUER_ID }} - APPSTORE_KEY_ID: ${{ secrets.APPSTORE_KEY_ID }} - APPSTORE_P8: ${{ secrets.APPSTORE_P8 }} - - IOS_BUNDLE_ID: ${{ vars.IOS_ZAPBOX_BUNDLE_ID }} - - GH_PAT: ${{ secrets.MATCH_PAT }} - GITHUB_REPOSITORY: ${{ env.GITHUB_REPOSITORY }} - MATCH_REPOSITORY: ${{ secrets.MATCH_REPOSITORY }} - MATCH_DEPLOY_KEY: ${{ secrets.MATCH_DEPLOY_KEY }} - MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }} +--- +# yamllint disable rule:line-length +# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json +name: Generate iOS Certs + +on: # yamllint disable-line rule:truthy + workflow_run: + workflows: ['iOS One-Time Setup'] + types: + - completed + workflow_dispatch: + +jobs: + generate_certs: + if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }} + runs-on: macos-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + sparse-checkout: | + Gemfile + Gemfile.lock + fastlane + - uses: ruby/setup-ruby@v1 + with: + ruby-version: 3.2 + bundler-cache: true + + - name: Build iOS + shell: bash + run: | + eval "$(ssh-agent -s)" + ssh-add - <<< "${MATCH_DEPLOY_KEY}" + bundle exec fastlane ios sync_certificates + env: + APPSTORE_ISSUER_ID: ${{ secrets.APPSTORE_ISSUER_ID }} + APPSTORE_KEY_ID: ${{ secrets.APPSTORE_KEY_ID }} + APPSTORE_P8: ${{ secrets.APPSTORE_P8 }} + + IOS_BUNDLE_ID: ${{ vars.IOS_ZAPBOX_BUNDLE_ID }} + + GH_PAT: ${{ secrets.MATCH_PAT }} + GITHUB_REPOSITORY: ${{ env.GITHUB_REPOSITORY }} + MATCH_REPOSITORY: ${{ secrets.MATCH_REPOSITORY }} + MATCH_DEPLOY_KEY: ${{ secrets.MATCH_DEPLOY_KEY }} + MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }} diff --git a/.github/workflows/get_license.yml b/.github/workflows/get_license.yml index e4beed0c57..a86886bf28 100644 --- a/.github/workflows/get_license.yml +++ b/.github/workflows/get_license.yml @@ -1,22 +1,22 @@ ---- -name: Acquire activation file -on: - workflow_dispatch: - -jobs: - activation: - name: Request manual activation file 🔑 - runs-on: ubuntu-latest - steps: - # Request manual activation file - - name: Request manual activation file - id: getManualLicenseFile - uses: game-ci/unity-request-activation-file@v2 - with: - unityVersion: 2019.4.25f1 - - name: Expose as artifact - # Upload artifact (Unity_v20XX.X.XXXX.alf) - uses: actions/upload-artifact@v4 - with: - name: ${{ steps.getManualLicenseFile.outputs.filePath }} - path: ${{ steps.getManualLicenseFile.outputs.filePath }} +--- +name: Acquire activation file +on: + workflow_dispatch: + +jobs: + activation: + name: Request manual activation file 🔑 + runs-on: ubuntu-latest + steps: + # Request manual activation file + - name: Request manual activation file + id: getManualLicenseFile + uses: game-ci/unity-request-activation-file@v2 + with: + unityVersion: 2019.4.25f1 + - name: Expose as artifact + # Upload artifact (Unity_v20XX.X.XXXX.alf) + uses: actions/upload-artifact@v4 + with: + name: ${{ steps.getManualLicenseFile.outputs.filePath }} + path: ${{ steps.getManualLicenseFile.outputs.filePath }} diff --git a/.github/workflows/ios_setup.yml b/.github/workflows/ios_setup.yml index 0fc7a7eb1b..b46f2bd7b7 100644 --- a/.github/workflows/ios_setup.yml +++ b/.github/workflows/ios_setup.yml @@ -1,36 +1,36 @@ ---- -# yamllint disable rule:line-length -# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json -name: iOS One-Time Setup - -on: workflow_dispatch - -jobs: - setup: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - sparse-checkout: | - Gemfile - Gemfile.lock - fastlane - - uses: ruby/setup-ruby@v1 - with: - ruby-version: 3.2 - bundler-cache: true - - # Note that this job cannot be rerun because of https://github.com/joshdholtz/fastlane-plugin-github_action/issues/4 - - name: Build iOS - shell: bash - run: | - bundle exec fastlane ios init_ci - env: - APPSTORE_ISSUER_ID: ${{ secrets.APPSTORE_ISSUER_ID }} - APPSTORE_KEY_ID: ${{ secrets.APPSTORE_KEY_ID }} - APPSTORE_P8: ${{ secrets.APPSTORE_P8 }} - - GH_PAT: ${{ secrets.MATCH_PAT }} - GITHUB_REPOSITORY: ${{ env.GITHUB_REPOSITORY }} - MATCH_REPOSITORY: ${{ secrets.MATCH_REPOSITORY }} +--- +# yamllint disable rule:line-length +# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json +name: iOS One-Time Setup + +on: workflow_dispatch + +jobs: + setup: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + sparse-checkout: | + Gemfile + Gemfile.lock + fastlane + - uses: ruby/setup-ruby@v1 + with: + ruby-version: 3.2 + bundler-cache: true + + # Note that this job cannot be rerun because of https://github.com/joshdholtz/fastlane-plugin-github_action/issues/4 + - name: Build iOS + shell: bash + run: | + bundle exec fastlane ios init_ci + env: + APPSTORE_ISSUER_ID: ${{ secrets.APPSTORE_ISSUER_ID }} + APPSTORE_KEY_ID: ${{ secrets.APPSTORE_KEY_ID }} + APPSTORE_P8: ${{ secrets.APPSTORE_P8 }} + + GH_PAT: ${{ secrets.MATCH_PAT }} + GITHUB_REPOSITORY: ${{ env.GITHUB_REPOSITORY }} + MATCH_REPOSITORY: ${{ secrets.MATCH_REPOSITORY }} diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index 55640652f3..e527be76a2 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -1,39 +1,39 @@ ---- -# yamllint disable rule:line-length -name: pre-commit - -on: # yamllint disable-line rule:truthy - pull_request: - push: - branches: - - main # We never expect this to fail, since it must have passed on the pull request, but this will let us create a cache on main that other PRs can use, speeding up the process - -jobs: - pre-commit: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5.3.0 - with: - python-version: '3.12' - - uses: actions/setup-dotnet@v4.1.0 - with: - dotnet-version: '8.0.x' - - name: Install pre-commit - run: python -m pip install pre-commit - shell: bash - - name: Cache pre-commit environments - uses: actions/cache@v4 - with: - path: ~/.cache/pre-commit - key: pre-commit-3|${{ env.pythonLocation }}|${{ hashFiles('.pre-commit-config.yaml') }} - - name: Setup pre-commit environments - run: pre-commit run - - name: Run pre-commit dotnet-format, with retries - uses: Wandalen/wretry.action@v3 - with: - command: pre-commit run dotnet-format --show-diff-on-failure --color=always --all-files || { git checkout -- . ; exit 1 ; } # In case dotnet-format fails, reset the changes it made. This way, we can differentiate between a NuGet failure and a real formatting issue - - name: Remove dotnet-format from the list of pre-commit jobs to run (since we already ran it) - run: yq eval 'del(.repos[] | select(.hooks[].id == "dotnet-format"))' -i .pre-commit-config.yaml - - name: Run the rest of pre-commit - run: pre-commit run --show-diff-on-failure --color=always --all-files +--- +# yamllint disable rule:line-length +name: pre-commit + +on: # yamllint disable-line rule:truthy + pull_request: + push: + branches: + - main # We never expect this to fail, since it must have passed on the pull request, but this will let us create a cache on main that other PRs can use, speeding up the process + +jobs: + pre-commit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5.3.0 + with: + python-version: '3.12' + - uses: actions/setup-dotnet@v4.1.0 + with: + dotnet-version: '8.0.x' + - name: Install pre-commit + run: python -m pip install pre-commit + shell: bash + - name: Cache pre-commit environments + uses: actions/cache@v4 + with: + path: ~/.cache/pre-commit + key: pre-commit-3|${{ env.pythonLocation }}|${{ hashFiles('.pre-commit-config.yaml') }} + - name: Setup pre-commit environments + run: pre-commit run + - name: Run pre-commit dotnet-format, with retries + uses: Wandalen/wretry.action@v3 + with: + command: pre-commit run dotnet-format --show-diff-on-failure --color=always --all-files || { git checkout -- . ; exit 1 ; } # In case dotnet-format fails, reset the changes it made. This way, we can differentiate between a NuGet failure and a real formatting issue + - name: Remove dotnet-format from the list of pre-commit jobs to run (since we already ran it) + run: yq eval 'del(.repos[] | select(.hooks[].id == "dotnet-format"))' -i .pre-commit-config.yaml + - name: Run the rest of pre-commit + run: pre-commit run --show-diff-on-failure --color=always --all-files diff --git a/.github/workflows/test_unity_credentials.yml b/.github/workflows/test_unity_credentials.yml index 418b2b5a42..3ee00c3f52 100644 --- a/.github/workflows/test_unity_credentials.yml +++ b/.github/workflows/test_unity_credentials.yml @@ -1,22 +1,22 @@ ---- -# yamllint disable rule:line-length -# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json -name: Test Unity Credentials -on: - workflow_dispatch: - -env: - UNITY_VERSION: "2021.3.30f1" - UNITY_EMAIL: ${{ vars.UNITY_EMAIL }} - UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }} - UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }} - -jobs: - test_license: - runs-on: ubuntu-latest - steps: - - name: Unity - Activate - uses: game-ci/unity-activate@v2 - - name: Unity - Return License - uses: game-ci/unity-return-license@v2 - if: always() +--- +# yamllint disable rule:line-length +# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json +name: Test Unity Credentials +on: + workflow_dispatch: + +env: + UNITY_VERSION: "2021.3.30f1" + UNITY_EMAIL: ${{ vars.UNITY_EMAIL }} + UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }} + UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }} + +jobs: + test_license: + runs-on: ubuntu-latest + steps: + - name: Unity - Activate + uses: game-ci/unity-activate@v2 + - name: Unity - Return License + uses: game-ci/unity-return-license@v2 + if: always() diff --git a/.github/workflows/third_party_notices.yml b/.github/workflows/third_party_notices.yml index 8b0d4b09e8..c5814b4c96 100644 --- a/.github/workflows/third_party_notices.yml +++ b/.github/workflows/third_party_notices.yml @@ -1,19 +1,19 @@ ---- -# yamllint disable rule:line-length -name: Check for updated Third Party Notices - -on: # yamllint disable-line rule:truthy - pull_request: - -jobs: - third-party-notices: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5.3.0 - with: - python-version: '3.12' - - name: Run the generator - run: | - python Support/Python/unitybuild/generate_notice.py - git diff --exit-code +--- +# yamllint disable rule:line-length +name: Check for updated Third Party Notices + +on: # yamllint disable-line rule:truthy + pull_request: + +jobs: + third-party-notices: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5.3.0 + with: + python-version: '3.12' + - name: Run the generator + run: | + python Support/Python/unitybuild/generate_notice.py + git diff --exit-code diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index bc8ba20e11..506bf6203a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,48 +1,48 @@ ---- -repos: - - repo: https://github.com/adrienverge/yamllint.git - rev: v1.35.1 - hooks: - - id: yamllint - - repo: https://github.com/psf/black - rev: 24.3.0 - hooks: - - id: black - files: ^Support/ - language_version: python3 - - repo: https://github.com/pycqa/flake8 - rev: 7.0.0 - hooks: - - id: flake8 - files: ^Support/ - - repo: https://github.com/PyCQA/pylint.git - rev: v3.1.0 - hooks: - - id: pylint - name: pylint - files: ^Support/ - language_version: python3 - additional_dependencies: - - typing_extensions - args: - - --load-plugins=pylint.extensions.redefined_variable_type,pylint.extensions.bad_builtin - - --disable=import-error - - repo: https://github.com/google/yamlfmt - rev: v0.11.0 - hooks: - - id: yamlfmt - args: - - -conf - - .yamlfmt - - repo: local - hooks: - # Use dotnet format already installed on your machine - - id: dotnet-format - name: dotnet-format - language: system - entry: dotnet format whitespace - types_or: [c#, vb] - exclude: ^(Assets/ThirdParty)|(Packages/)|(Assets/Photon/) - args: - - --folder - - --include +--- +repos: + - repo: https://github.com/adrienverge/yamllint.git + rev: v1.35.1 + hooks: + - id: yamllint + - repo: https://github.com/psf/black + rev: 24.3.0 + hooks: + - id: black + files: ^Support/ + language_version: python3 + - repo: https://github.com/pycqa/flake8 + rev: 7.0.0 + hooks: + - id: flake8 + files: ^Support/ + - repo: https://github.com/PyCQA/pylint.git + rev: v3.1.0 + hooks: + - id: pylint + name: pylint + files: ^Support/ + language_version: python3 + additional_dependencies: + - typing_extensions + args: + - --load-plugins=pylint.extensions.redefined_variable_type,pylint.extensions.bad_builtin + - --disable=import-error + - repo: https://github.com/google/yamlfmt + rev: v0.11.0 + hooks: + - id: yamlfmt + args: + - -conf + - .yamlfmt + - repo: local + hooks: + # Use dotnet format already installed on your machine + - id: dotnet-format + name: dotnet-format + language: system + entry: dotnet format whitespace + types_or: [c#, vb] + exclude: ^(Assets/ThirdParty)|(Packages/)|(Assets/Photon/) + args: + - --folder + - --include diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs index d29ee5dd14..9635e49dbd 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs @@ -1,17 +1,17 @@ -// 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. - +// 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; diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonVoiceManager.cs b/Assets/Scripts/Multiplayer/Photon/PhotonVoiceManager.cs index e231dcc6e1..361b744ec5 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonVoiceManager.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonVoiceManager.cs @@ -1,17 +1,17 @@ -// 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. - +// 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 PHOTON_UNITY_NETWORKING && PHOTON_VOICE_DEFINED using Fusion; From 981175a2671bc8c1cff7ec4b9e4ae15653b44578 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Thu, 31 Oct 2024 10:35:00 +0000 Subject: [PATCH 067/174] Changes to MultiplayerManager Fixing incorrect conditions for executing networked commands. --- .yamlfmt | 13 +++++++------ Assets/Scripts/Multiplayer/MultiplayerManager.cs | 3 +-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.yamlfmt b/.yamlfmt index fd7281cdd4..babb835d82 100644 --- a/.yamlfmt +++ b/.yamlfmt @@ -1,6 +1,7 @@ ---- -formatter: - include_document_start: true - indent: 2 - retain_line_breaks_single: true - pad_line_comments: 2 +--- +line_ending: lf +formatter: + include_document_start: true + indent: 2 + retain_line_breaks_single: true + pad_line_comments: 2 diff --git a/Assets/Scripts/Multiplayer/MultiplayerManager.cs b/Assets/Scripts/Multiplayer/MultiplayerManager.cs index 826610d6bb..58ca86d35e 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerManager.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerManager.cs @@ -357,10 +357,9 @@ private async void OnCommandPerformed(BaseCommand command) { if (State == ConnectionState.IN_ROOM) { - return; + await m_Manager.PerformCommand(command); } - 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. From d66509c9c8586ab2338502eb600dfabf38524f7c Mon Sep 17 00:00:00 2001 From: Mike Miller Date: Thu, 31 Oct 2024 13:00:16 +0200 Subject: [PATCH 068/174] more dos2unix --- .yamlfmt | 14 +- Assets/Editor/MultiplayerManagerEditor.cs | 178 +- Assets/OculusMR/OculusMRController.cs | 178 +- Assets/Scripts/GUI/MultiplayerPanel.cs | 460 +- .../Multiplayer/MultiplayerDataStructs.cs | 156 +- .../Multiplayer/MultiplayerInterfaces.cs | 152 +- .../Scripts/Multiplayer/MultiplayerManager.cs | 894 +- .../Multiplayer/Photon/PhotonManager.cs | 820 +- .../Multiplayer/Photon/PhotonVoiceManager.cs | 592 +- Assets/Scripts/SketchControlsScript.cs | 10464 ++++++++-------- 10 files changed, 6954 insertions(+), 6954 deletions(-) diff --git a/.yamlfmt b/.yamlfmt index babb835d82..dad45d7e1f 100644 --- a/.yamlfmt +++ b/.yamlfmt @@ -1,7 +1,7 @@ ---- -line_ending: lf -formatter: - include_document_start: true - indent: 2 - retain_line_breaks_single: true - pad_line_comments: 2 +--- +line_ending: lf +formatter: + include_document_start: true + indent: 2 + retain_line_breaks_single: true + pad_line_comments: 2 diff --git a/Assets/Editor/MultiplayerManagerEditor.cs b/Assets/Editor/MultiplayerManagerEditor.cs index 192b9c9e69..cb4a7e07c2 100644 --- a/Assets/Editor/MultiplayerManagerEditor.cs +++ b/Assets/Editor/MultiplayerManagerEditor.cs @@ -1,89 +1,89 @@ -// MultiplayerManagerInspector.cs -using UnityEditor; -using UnityEngine; -using OpenBrush.Multiplayer; -using System.Threading.Tasks; - -#if UNITY_EDITOR -[CustomEditor(typeof(MultiplayerManager))] -public class MultiplayerManagerInspector : Editor -{ - private MultiplayerManager multiplayerManager; - private string roomName = "1234"; - private bool isPrivate = false; - private int maxPlayers = 4; - private bool voiceDisabled = false; - - public override void OnInspectorGUI() - { - // Get the target object (MultiplayerManager) - multiplayerManager = (MultiplayerManager)target; - - GUILayout.Label("Multiplayer Manager Controls", EditorStyles.boldLabel); - - // Room data input fields - roomName = EditorGUILayout.TextField("Room Name", roomName); - - // Button to join the lobby - if (GUILayout.Button("Join Lobby") ) - { - ConnectToLobby(); - EditorUtility.SetDirty(target); // Mark the object as dirty to recognize state changes - } - - // Button to join the room - if (GUILayout.Button("Join Room")) - { - ConnectToRoom(); - EditorUtility.SetDirty(target); // Update inspector on state change - } - - // Button to exit the room - if (GUILayout.Button("Exit Room") ) - { - DisconnectFromRoom(); - EditorUtility.SetDirty(target); // Update inspector on state change - } - - // Force the inspector to repaint to reflect the latest state - Repaint(); - - // Draw default inspector below - DrawDefaultInspector(); - } - - private async void ConnectToLobby() - { - if (multiplayerManager != null) - { - bool success = await multiplayerManager.Connect(); - } - } - - private async void ConnectToRoom() - { - if (multiplayerManager != null) - { - RoomCreateData roomData = new RoomCreateData - { - roomName = roomName, - @private = isPrivate, - maxPlayers = maxPlayers, - voiceDisabled = voiceDisabled - }; - - bool success = await multiplayerManager.JoinRoom(roomData); - - } - } - - private async void DisconnectFromRoom() - { - if (multiplayerManager != null) - { - bool success = await multiplayerManager.LeaveRoom(); - - } - } -} -#endif +// MultiplayerManagerInspector.cs +using UnityEditor; +using UnityEngine; +using OpenBrush.Multiplayer; +using System.Threading.Tasks; + +#if UNITY_EDITOR +[CustomEditor(typeof(MultiplayerManager))] +public class MultiplayerManagerInspector : Editor +{ + private MultiplayerManager multiplayerManager; + private string roomName = "1234"; + private bool isPrivate = false; + private int maxPlayers = 4; + private bool voiceDisabled = false; + + public override void OnInspectorGUI() + { + // Get the target object (MultiplayerManager) + multiplayerManager = (MultiplayerManager)target; + + GUILayout.Label("Multiplayer Manager Controls", EditorStyles.boldLabel); + + // Room data input fields + roomName = EditorGUILayout.TextField("Room Name", roomName); + + // Button to join the lobby + if (GUILayout.Button("Join Lobby") ) + { + ConnectToLobby(); + EditorUtility.SetDirty(target); // Mark the object as dirty to recognize state changes + } + + // Button to join the room + if (GUILayout.Button("Join Room")) + { + ConnectToRoom(); + EditorUtility.SetDirty(target); // Update inspector on state change + } + + // Button to exit the room + if (GUILayout.Button("Exit Room") ) + { + DisconnectFromRoom(); + EditorUtility.SetDirty(target); // Update inspector on state change + } + + // Force the inspector to repaint to reflect the latest state + Repaint(); + + // Draw default inspector below + DrawDefaultInspector(); + } + + private async void ConnectToLobby() + { + if (multiplayerManager != null) + { + bool success = await multiplayerManager.Connect(); + } + } + + private async void ConnectToRoom() + { + if (multiplayerManager != null) + { + RoomCreateData roomData = new RoomCreateData + { + roomName = roomName, + @private = isPrivate, + maxPlayers = maxPlayers, + voiceDisabled = voiceDisabled + }; + + bool success = await multiplayerManager.JoinRoom(roomData); + + } + } + + private async void DisconnectFromRoom() + { + if (multiplayerManager != null) + { + bool success = await multiplayerManager.LeaveRoom(); + + } + } +} +#endif diff --git a/Assets/OculusMR/OculusMRController.cs b/Assets/OculusMR/OculusMRController.cs index 78860d21ec..0e67eeecab 100644 --- a/Assets/OculusMR/OculusMRController.cs +++ b/Assets/OculusMR/OculusMRController.cs @@ -1,89 +1,89 @@ -// 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 - { -#if OCULUS_SUPPORTED - 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.JoinRoom(new RoomCreateData() - { - roomName = "OculusMRRoom", - maxPlayers = 12 - }); - } - else - { - MultiplayerManager.m_Instance.JoinRoom(new RoomCreateData() - { - roomName = "OculusMRRoom", - maxPlayers = 12 - }); - } - } - - public async void RemoteSyncToAnchor(string uuid) - { - await m_SpatialAnchorManager.SyncToRemoteAnchor(uuid, OVRSpace.StorageLocation.Cloud); - - if (!loadedScene) - { - ovrSceneManager.LoadSceneModel(); - loadedScene = true; - } - } -#endif // OCULUS_SUPPORTED - } -} - +// 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 + { +#if OCULUS_SUPPORTED + 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.JoinRoom(new RoomCreateData() + { + roomName = "OculusMRRoom", + maxPlayers = 12 + }); + } + else + { + MultiplayerManager.m_Instance.JoinRoom(new RoomCreateData() + { + roomName = "OculusMRRoom", + maxPlayers = 12 + }); + } + } + + public async void RemoteSyncToAnchor(string uuid) + { + await m_SpatialAnchorManager.SyncToRemoteAnchor(uuid, OVRSpace.StorageLocation.Cloud); + + if (!loadedScene) + { + ovrSceneManager.LoadSceneModel(); + loadedScene = true; + } + } +#endif // OCULUS_SUPPORTED + } +} + diff --git a/Assets/Scripts/GUI/MultiplayerPanel.cs b/Assets/Scripts/GUI/MultiplayerPanel.cs index 749e525e8b..286156114a 100644 --- a/Assets/Scripts/GUI/MultiplayerPanel.cs +++ b/Assets/Scripts/GUI/MultiplayerPanel.cs @@ -1,230 +1,230 @@ -// 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 OpenBrush.Multiplayer; -using System; -using System.Collections.Generic; -using TMPro; -using UnityEngine; - -namespace TiltBrush -{ - public class MultiplayerPanel : BasePanel - { - - [SerializeField] private TextMeshPro m_State; - [SerializeField] private TextMeshPro m_RoomNumber; - [SerializeField] private TextMeshPro m_Nickname; - [SerializeField] private TextMeshPro m_AlertsErrors; - - public string RoomName - { - get { return data.roomName; } - set - { - data.roomName = value; - UpdateDisplay(); - } - } - - public string NickName - { - get - { - - if (MultiplayerManager.m_Instance != null) return MultiplayerManager.m_Instance.UserInfo.Nickname; - return ""; - } - set - { - ConnectionUserInfo ui = new ConnectionUserInfo - { - Nickname = value, - UserId = MultiplayerManager.m_Instance.UserInfo.UserId, - Role = MultiplayerManager.m_Instance.UserInfo.Role - }; - MultiplayerManager.m_Instance.UserInfo = ui; - UpdateDisplay(); - } - } - - private RoomCreateData data; - - private List>> alertChecks; - - public void Awake() - { - data = new RoomCreateData - { - roomName = GenerateUniqueRoomName(), - @private = false, - maxPlayers = 4, - voiceDisabled = false - }; - - alertChecks = new List>> - { - CheckAdvancedModeActive, - CheckMultiplayerManagerErrors, - }; - - if (MultiplayerManager.m_Instance != null) MultiplayerManager.m_Instance.StateUpdated += OnStateUpdated; - - } - - protected override void OnEnablePanel() - { - base.OnEnablePanel(); - UpdateDisplay(); - } - - private static string GenerateUniqueRoomName() - { - string roomName; - do - { - roomName = GenerateRandomRoomName(); - } while (MultiplayerManager.m_Instance != null && MultiplayerManager.m_Instance.DoesRoomNameExist(roomName)); - - return roomName; - } - - private static string GenerateRandomRoomName() - { - System.Random random = new System.Random(); - return random.Next(100000, 999999).ToString(); - } - - - private void UpdateDisplay() - { - if (m_RoomNumber) m_RoomNumber.text = "RoomName: " + data.roomName; - if (m_Nickname) m_Nickname.text = "Nickname: " + NickName; - Alerts(); - } - - private async void Connect() - { - if (MultiplayerManager.m_Instance != null) - { - await MultiplayerManager.m_Instance.Connect(); - } - } - - private async void JoinRoom() - { - - if (MultiplayerManager.m_Instance != null) - { - await MultiplayerManager.m_Instance.JoinRoom(data); - } - } - - private async void LeaveRoom() - { - if (MultiplayerManager.m_Instance != null) - { - await MultiplayerManager.m_Instance.LeaveRoom(false); - } - } - - private async void Disconnect() - { - if (MultiplayerManager.m_Instance != null) - { - await MultiplayerManager.m_Instance.Disconnect(); - } - } - - private void OnStateUpdated(ConnectionState newState) - { - m_State.text = "State: " + newState.ToString(); - UpdateDisplay(); - } - - private Tuple CheckAdvancedModeActive() - { - bool isAdvancedModeActive = PanelManager.m_Instance.AdvancedModeActive(); - return Tuple.Create(isAdvancedModeActive, "Switch to beginner mode to Join Room"); - } - - private Tuple CheckMultiplayerManagerErrors() - { - - if (MultiplayerManager.m_Instance != null) - { - if (MultiplayerManager.m_Instance.State == ConnectionState.ERROR) - return Tuple.Create(true, MultiplayerManager.m_Instance.LastError); - } - - return Tuple.Create(false, ""); - - } - - private void Alerts() - { - if (m_AlertsErrors) - { - bool shouldShowAlert = false; - string alertMessage = ""; - - foreach (Func> check in alertChecks) - { - var (isTriggered, message) = check.Invoke(); - if (isTriggered) - { - shouldShowAlert = true; - alertMessage += message + "/n"; - break; - } - } - m_AlertsErrors.gameObject.GetComponent().text = alertMessage; - m_AlertsErrors.gameObject.SetActive(shouldShowAlert); - } - } - - public void OnMultiplayerPanelButtonPressed(MultiplayerPanelButton button) - { - switch (button.m_Command) - { - - case SketchControlsScript.GlobalCommands.Null: - //UpdateMode(Mode.Disconnected); - break; - case SketchControlsScript.GlobalCommands.MultiplayerConnect: - Connect(); - break; - case SketchControlsScript.GlobalCommands.MultiplayerPanelOptions: - //switch ((Mode)button.m_CommandParam) - //{ - // case Mode.Lobby: - // UpdateMode(Mode.Lobby); - // break; - // default: - // break; - //} - break; - case SketchControlsScript.GlobalCommands.MultiplayerJoinRoom: - JoinRoom(); - break; - case SketchControlsScript.GlobalCommands.MultiplayerLeaveRoom: - LeaveRoom(); - break; - case SketchControlsScript.GlobalCommands.MultiplayerDisconnect: - Disconnect(); - break; - } - } - } -} // namespace TiltBrush +// 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 OpenBrush.Multiplayer; +using System; +using System.Collections.Generic; +using TMPro; +using UnityEngine; + +namespace TiltBrush +{ + public class MultiplayerPanel : BasePanel + { + + [SerializeField] private TextMeshPro m_State; + [SerializeField] private TextMeshPro m_RoomNumber; + [SerializeField] private TextMeshPro m_Nickname; + [SerializeField] private TextMeshPro m_AlertsErrors; + + public string RoomName + { + get { return data.roomName; } + set + { + data.roomName = value; + UpdateDisplay(); + } + } + + public string NickName + { + get + { + + if (MultiplayerManager.m_Instance != null) return MultiplayerManager.m_Instance.UserInfo.Nickname; + return ""; + } + set + { + ConnectionUserInfo ui = new ConnectionUserInfo + { + Nickname = value, + UserId = MultiplayerManager.m_Instance.UserInfo.UserId, + Role = MultiplayerManager.m_Instance.UserInfo.Role + }; + MultiplayerManager.m_Instance.UserInfo = ui; + UpdateDisplay(); + } + } + + private RoomCreateData data; + + private List>> alertChecks; + + public void Awake() + { + data = new RoomCreateData + { + roomName = GenerateUniqueRoomName(), + @private = false, + maxPlayers = 4, + voiceDisabled = false + }; + + alertChecks = new List>> + { + CheckAdvancedModeActive, + CheckMultiplayerManagerErrors, + }; + + if (MultiplayerManager.m_Instance != null) MultiplayerManager.m_Instance.StateUpdated += OnStateUpdated; + + } + + protected override void OnEnablePanel() + { + base.OnEnablePanel(); + UpdateDisplay(); + } + + private static string GenerateUniqueRoomName() + { + string roomName; + do + { + roomName = GenerateRandomRoomName(); + } while (MultiplayerManager.m_Instance != null && MultiplayerManager.m_Instance.DoesRoomNameExist(roomName)); + + return roomName; + } + + private static string GenerateRandomRoomName() + { + System.Random random = new System.Random(); + return random.Next(100000, 999999).ToString(); + } + + + private void UpdateDisplay() + { + if (m_RoomNumber) m_RoomNumber.text = "RoomName: " + data.roomName; + if (m_Nickname) m_Nickname.text = "Nickname: " + NickName; + Alerts(); + } + + private async void Connect() + { + if (MultiplayerManager.m_Instance != null) + { + await MultiplayerManager.m_Instance.Connect(); + } + } + + private async void JoinRoom() + { + + if (MultiplayerManager.m_Instance != null) + { + await MultiplayerManager.m_Instance.JoinRoom(data); + } + } + + private async void LeaveRoom() + { + if (MultiplayerManager.m_Instance != null) + { + await MultiplayerManager.m_Instance.LeaveRoom(false); + } + } + + private async void Disconnect() + { + if (MultiplayerManager.m_Instance != null) + { + await MultiplayerManager.m_Instance.Disconnect(); + } + } + + private void OnStateUpdated(ConnectionState newState) + { + m_State.text = "State: " + newState.ToString(); + UpdateDisplay(); + } + + private Tuple CheckAdvancedModeActive() + { + bool isAdvancedModeActive = PanelManager.m_Instance.AdvancedModeActive(); + return Tuple.Create(isAdvancedModeActive, "Switch to beginner mode to Join Room"); + } + + private Tuple CheckMultiplayerManagerErrors() + { + + if (MultiplayerManager.m_Instance != null) + { + if (MultiplayerManager.m_Instance.State == ConnectionState.ERROR) + return Tuple.Create(true, MultiplayerManager.m_Instance.LastError); + } + + return Tuple.Create(false, ""); + + } + + private void Alerts() + { + if (m_AlertsErrors) + { + bool shouldShowAlert = false; + string alertMessage = ""; + + foreach (Func> check in alertChecks) + { + var (isTriggered, message) = check.Invoke(); + if (isTriggered) + { + shouldShowAlert = true; + alertMessage += message + "/n"; + break; + } + } + m_AlertsErrors.gameObject.GetComponent().text = alertMessage; + m_AlertsErrors.gameObject.SetActive(shouldShowAlert); + } + } + + public void OnMultiplayerPanelButtonPressed(MultiplayerPanelButton button) + { + switch (button.m_Command) + { + + case SketchControlsScript.GlobalCommands.Null: + //UpdateMode(Mode.Disconnected); + break; + case SketchControlsScript.GlobalCommands.MultiplayerConnect: + Connect(); + break; + case SketchControlsScript.GlobalCommands.MultiplayerPanelOptions: + //switch ((Mode)button.m_CommandParam) + //{ + // case Mode.Lobby: + // UpdateMode(Mode.Lobby); + // break; + // default: + // break; + //} + break; + case SketchControlsScript.GlobalCommands.MultiplayerJoinRoom: + JoinRoom(); + break; + case SketchControlsScript.GlobalCommands.MultiplayerLeaveRoom: + LeaveRoom(); + break; + case SketchControlsScript.GlobalCommands.MultiplayerDisconnect: + Disconnect(); + break; + } + } + } +} // namespace TiltBrush diff --git a/Assets/Scripts/Multiplayer/MultiplayerDataStructs.cs b/Assets/Scripts/Multiplayer/MultiplayerDataStructs.cs index 7561ec2014..52642af439 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerDataStructs.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerDataStructs.cs @@ -1,78 +1,78 @@ -// 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; - } - - [System.Serializable] - public struct RoomCreateData - { - public string roomName; - public string roomPassword; - public bool @private; - public int maxPlayers; - public bool voiceDisabled; - } - - [System.Serializable] - public struct RoomData - { - public string roomName; - public bool @private; - public int numPlayers; - public int maxPlayers; - public bool voiceDisabled; - } - - - [System.Serializable] - public struct ConnectionUserInfo - { - public string UserId; - public string Nickname; - public string Role; - } -} +// 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; + } + + [System.Serializable] + public struct RoomCreateData + { + public string roomName; + public string roomPassword; + public bool @private; + public int maxPlayers; + public bool voiceDisabled; + } + + [System.Serializable] + public struct RoomData + { + public string roomName; + public bool @private; + public int numPlayers; + public int maxPlayers; + public bool voiceDisabled; + } + + + [System.Serializable] + public struct ConnectionUserInfo + { + public string UserId; + public string Nickname; + public string Role; + } +} diff --git a/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs b/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs index 079d36e4ce..2f61908e5d 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs @@ -1,76 +1,76 @@ -// 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.Threading.Tasks; -using TiltBrush; - -namespace OpenBrush.Multiplayer -{ - public interface IConnectionHandler - { - Task Connect(); - Task JoinRoom(RoomCreateData data); - Task LeaveRoom(bool force = false); - Task Disconnect(); - ConnectionState State { get; } - ConnectionUserInfo UserInfo { get; set; } - string LastError { get; } - } - - public interface IDataConnectionHandler : IConnectionHandler - { - - void Update(); - - Task PerformCommand(BaseCommand command); - Task UndoCommand(BaseCommand command); - Task RedoCommand(BaseCommand command); - Task RpcSyncToSharedAnchor(string uuid); - - event Action Disconnected; - - } - - public interface IVoiceConnectionHandler : IConnectionHandler - { - - bool StartSpeaking(); - bool StopSpeaking(); - - } - - public enum ConnectionState - { - INITIALISING = 0, - INITIALIZED = 1, - DISCONNECTED = 2, - DISCONNECTING = 3, - CONNECTING = 4, - AUTHENTICATING = 5, - IN_LOBBY = 6, - JOINING_ROOM = 7, - IN_ROOM = 8, - RECONNECTING = 9, - ERROR = 10, - LEAVING_ROOM = 11 - } - - public interface ITransientData - { - int PlayerId { get; set; } - void TransmitData(T data); - T RecieveData(); - } -} +// 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.Threading.Tasks; +using TiltBrush; + +namespace OpenBrush.Multiplayer +{ + public interface IConnectionHandler + { + Task Connect(); + Task JoinRoom(RoomCreateData data); + Task LeaveRoom(bool force = false); + Task Disconnect(); + ConnectionState State { get; } + ConnectionUserInfo UserInfo { get; set; } + string LastError { get; } + } + + public interface IDataConnectionHandler : IConnectionHandler + { + + void Update(); + + Task PerformCommand(BaseCommand command); + Task UndoCommand(BaseCommand command); + Task RedoCommand(BaseCommand command); + Task RpcSyncToSharedAnchor(string uuid); + + event Action Disconnected; + + } + + public interface IVoiceConnectionHandler : IConnectionHandler + { + + bool StartSpeaking(); + bool StopSpeaking(); + + } + + public enum ConnectionState + { + INITIALISING = 0, + INITIALIZED = 1, + DISCONNECTED = 2, + DISCONNECTING = 3, + CONNECTING = 4, + AUTHENTICATING = 5, + IN_LOBBY = 6, + JOINING_ROOM = 7, + IN_ROOM = 8, + RECONNECTING = 9, + ERROR = 10, + LEAVING_ROOM = 11 + } + + public interface ITransientData + { + int PlayerId { get; set; } + void TransmitData(T data); + T RecieveData(); + } +} diff --git a/Assets/Scripts/Multiplayer/MultiplayerManager.cs b/Assets/Scripts/Multiplayer/MultiplayerManager.cs index 58ca86d35e..4372a862c6 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerManager.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerManager.cs @@ -1,447 +1,447 @@ -// 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 System.Linq; -using System.Threading.Tasks; -using UnityEngine; - -#if OCULUS_SUPPORTED -using OVRPlatform = Oculus.Platform; -#endif -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; - public event Action Disconnected; - - private IDataConnectionHandler m_Manager; - private IVoiceConnectionHandler m_VoiceManager; - - private ITransientData m_LocalPlayer; - private List> m_RemotePlayers; - - public Action> localPlayerJoined; - public Action> remotePlayerJoined; - public Action playerLeft; - public Action> roomDataRefreshed; - public event Action StateUpdated; - private List m_RoomData = new List(); - - ulong myOculusUserId; - - List oculusPlayerIds; - internal string UserId; - [HideInInspector] public string CurrentRoomName; - - //public ConnectionState State => m_Manager?.State ?? ConnectionState.DISCONNECTED; - private ConnectionState _state; - public ConnectionState State - { - get => _state; - private set - { - if (_state != value) - { - _state = value; - StateUpdated?.Invoke(_state); // Trigger the event when the state changes - } - } - } - public string LastError { get; private set; } - - public ConnectionUserInfo UserInfo - { - get => m_Manager?.UserInfo ?? default; - set - { - if (m_Manager != null) - { - m_Manager.UserInfo = value; - } - } - } - - public RoomCreateData data; - - 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 - - State = ConnectionState.INITIALISING; - switch (m_MultiplayerType) - { - case MultiplayerType.Photon: -#if FUSION_WEAVER - m_Manager = new PhotonManager(this); - m_Manager.Disconnected += OnConnectionHandlerDisconnected; - if (m_Manager != null) ControllerConsoleScript.m_Instance.AddNewLine("PhotonManager Loaded"); - else ControllerConsoleScript.m_Instance.AddNewLine("PhotonManager Not Loaded"); -#endif -#if PHOTON_UNITY_NETWORKING && PHOTON_VOICE_DEFINED - m_VoiceManager = new PhotonVoiceManager(this); - if (m_VoiceManager != null) ControllerConsoleScript.m_Instance.AddNewLine("PhotonVoiceManager Loaded"); - else ControllerConsoleScript.m_Instance.AddNewLine("PhotonVoiceManager Not Loaded"); -#endif - break; - default: - return; - } - if (m_VoiceManager != null && m_Manager != null) State = ConnectionState.INITIALIZED; - - localPlayerJoined += OnLocalPlayerJoined; - remotePlayerJoined += OnRemotePlayerJoined; - playerLeft += OnPlayerLeft; - SketchMemoryScript.m_Instance.CommandPerformed += OnCommandPerformed; - SketchMemoryScript.m_Instance.CommandUndo += OnCommandUndo; - SketchMemoryScript.m_Instance.CommandRedo += OnCommandRedo; - } - - void OnDestroy() - { - localPlayerJoined -= OnLocalPlayerJoined; - remotePlayerJoined -= OnRemotePlayerJoined; - playerLeft -= OnPlayerLeft; - SketchMemoryScript.m_Instance.CommandPerformed -= OnCommandPerformed; - SketchMemoryScript.m_Instance.CommandUndo -= OnCommandUndo; - SketchMemoryScript.m_Instance.CommandRedo -= OnCommandRedo; - } - - public async Task Connect() - { - State = ConnectionState.CONNECTING; - - var successData = false; - if (m_Manager != null) successData = await m_Manager.Connect(); - - var successVoice = false; - if (m_VoiceManager != null) successVoice = await m_VoiceManager.Connect(); - - if (!successData) - { - State = ConnectionState.ERROR; - LastError = m_Manager.LastError; - } - else if (!successVoice) - { - State = ConnectionState.ERROR; - LastError = m_VoiceManager.LastError; - } - else State = ConnectionState.IN_LOBBY; - - - return successData & successVoice; - } - - public async Task JoinRoom(RoomCreateData RoomData) - { - State = ConnectionState.JOINING_ROOM; - - bool successData = false; - if (m_Manager != null) successData = await m_Manager.JoinRoom(RoomData); - - bool successVoice = false; - if (m_VoiceManager != null) successVoice = await m_VoiceManager.JoinRoom(RoomData); - m_VoiceManager?.StartSpeaking(); - - if (!successData) - { - State = ConnectionState.ERROR; - LastError = m_Manager.LastError; - } - else if (!successVoice) - { - State = ConnectionState.ERROR; - LastError = m_VoiceManager.LastError; - } - else State = ConnectionState.IN_ROOM; - - return successData & successVoice; - } - - public async Task LeaveRoom(bool force = false) - { - State = ConnectionState.LEAVING_ROOM; - - bool successData = false; - if (m_Manager != null) successData = await m_Manager.LeaveRoom(); - - bool successVoice = false; - m_VoiceManager?.StopSpeaking(); - if (m_VoiceManager != null) successVoice = await m_VoiceManager.LeaveRoom(); - - if (!successData) - { - State = ConnectionState.ERROR; - LastError = m_Manager.LastError; - } - else if (!successVoice) - { - State = ConnectionState.ERROR; - LastError = m_VoiceManager.LastError; - } - else State = ConnectionState.IN_LOBBY; - - return successData & successVoice; - } - - public async Task Disconnect() - { - State = ConnectionState.DISCONNECTING; - - bool successData = false; - if (m_Manager != null) successData = await m_Manager.Disconnect(); - - bool successVoice = false; - if (m_VoiceManager != null) successVoice = await m_VoiceManager.Disconnect(); - - if (!successData) - { - State = ConnectionState.ERROR; - LastError = m_Manager.LastError; - } - else if (!successVoice) - { - State = ConnectionState.ERROR; - LastError = m_VoiceManager.LastError; - } - else State = ConnectionState.DISCONNECTED; - - return successData & successVoice; - } - - public bool DoesRoomNameExist(string roomName) - { - return m_RoomData.Any(room => room.roomName == roomName); - } - - void OnRoomDataRefreshed(List rooms) - { - m_RoomData = rooms; - } - - 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(); -#if OCULUS_SUPPORTED - // 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; - } -#endif // OCULUS_SUPPORTED - } - - if (newUser) - { - ShareAnchors(); - } - } - - void OnLocalPlayerJoined(int id, ITransientData playerData) - { - m_LocalPlayer = playerData; - } - - void OnRemotePlayerJoined(int id, ITransientData playerData) - { - Debug.Log("Adding new player to track."); - playerData.PlayerId = id; - m_RemotePlayers.Add(playerData); - } - - void OnPlayerLeft(int id) - { - if (m_LocalPlayer.PlayerId == id) - { - m_LocalPlayer = null; - Debug.Log("Possible to get here!"); - return; - } - var copy = m_RemotePlayers.ToList(); - foreach (var player in copy) - { - if (player.PlayerId == id) - { - m_RemotePlayers.Remove(player); - } - } - } - - private async void OnCommandPerformed(BaseCommand command) - { - if (State == ConnectionState.IN_ROOM) - { - 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 (State == ConnectionState.IN_ROOM) - { - m_Manager.UndoCommand(command); - } - } - - private void OnCommandRedo(BaseCommand command) - { - if (State == ConnectionState.IN_ROOM) - { - m_Manager.RedoCommand(command); - } - } - - async void ShareAnchors() - { -#if OCULUS_SUPPORTED - 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); - } - } -#endif // OCULUS_SUPPORTED - } - - private void OnConnectionHandlerDisconnected() - { - // Clean up local player reference - m_LocalPlayer = null; - - // Invoke the Disconnected event - Disconnected?.Invoke(); - } - - public void StartSpeaking() - { - m_VoiceManager?.StartSpeaking(); - } - - public void StopSpeaking() - { - m_VoiceManager?.StopSpeaking(); - } - - public bool IsDisconnectable() - { - - return State == ConnectionState.IN_ROOM || State == ConnectionState.IN_LOBBY; - } - - public bool IsConnectable() - { - return State == ConnectionState.INITIALIZED || State == ConnectionState.DISCONNECTED; - } - - public bool CanJoinRoom() - { - return State == ConnectionState.IN_LOBBY; - } - - public bool CanLeaveRoom() - { - return State == ConnectionState.IN_ROOM; - } - } -} +// 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 System.Linq; +using System.Threading.Tasks; +using UnityEngine; + +#if OCULUS_SUPPORTED +using OVRPlatform = Oculus.Platform; +#endif +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; + public event Action Disconnected; + + private IDataConnectionHandler m_Manager; + private IVoiceConnectionHandler m_VoiceManager; + + private ITransientData m_LocalPlayer; + private List> m_RemotePlayers; + + public Action> localPlayerJoined; + public Action> remotePlayerJoined; + public Action playerLeft; + public Action> roomDataRefreshed; + public event Action StateUpdated; + private List m_RoomData = new List(); + + ulong myOculusUserId; + + List oculusPlayerIds; + internal string UserId; + [HideInInspector] public string CurrentRoomName; + + //public ConnectionState State => m_Manager?.State ?? ConnectionState.DISCONNECTED; + private ConnectionState _state; + public ConnectionState State + { + get => _state; + private set + { + if (_state != value) + { + _state = value; + StateUpdated?.Invoke(_state); // Trigger the event when the state changes + } + } + } + public string LastError { get; private set; } + + public ConnectionUserInfo UserInfo + { + get => m_Manager?.UserInfo ?? default; + set + { + if (m_Manager != null) + { + m_Manager.UserInfo = value; + } + } + } + + public RoomCreateData data; + + 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 + + State = ConnectionState.INITIALISING; + switch (m_MultiplayerType) + { + case MultiplayerType.Photon: +#if FUSION_WEAVER + m_Manager = new PhotonManager(this); + m_Manager.Disconnected += OnConnectionHandlerDisconnected; + if (m_Manager != null) ControllerConsoleScript.m_Instance.AddNewLine("PhotonManager Loaded"); + else ControllerConsoleScript.m_Instance.AddNewLine("PhotonManager Not Loaded"); +#endif +#if PHOTON_UNITY_NETWORKING && PHOTON_VOICE_DEFINED + m_VoiceManager = new PhotonVoiceManager(this); + if (m_VoiceManager != null) ControllerConsoleScript.m_Instance.AddNewLine("PhotonVoiceManager Loaded"); + else ControllerConsoleScript.m_Instance.AddNewLine("PhotonVoiceManager Not Loaded"); +#endif + break; + default: + return; + } + if (m_VoiceManager != null && m_Manager != null) State = ConnectionState.INITIALIZED; + + localPlayerJoined += OnLocalPlayerJoined; + remotePlayerJoined += OnRemotePlayerJoined; + playerLeft += OnPlayerLeft; + SketchMemoryScript.m_Instance.CommandPerformed += OnCommandPerformed; + SketchMemoryScript.m_Instance.CommandUndo += OnCommandUndo; + SketchMemoryScript.m_Instance.CommandRedo += OnCommandRedo; + } + + void OnDestroy() + { + localPlayerJoined -= OnLocalPlayerJoined; + remotePlayerJoined -= OnRemotePlayerJoined; + playerLeft -= OnPlayerLeft; + SketchMemoryScript.m_Instance.CommandPerformed -= OnCommandPerformed; + SketchMemoryScript.m_Instance.CommandUndo -= OnCommandUndo; + SketchMemoryScript.m_Instance.CommandRedo -= OnCommandRedo; + } + + public async Task Connect() + { + State = ConnectionState.CONNECTING; + + var successData = false; + if (m_Manager != null) successData = await m_Manager.Connect(); + + var successVoice = false; + if (m_VoiceManager != null) successVoice = await m_VoiceManager.Connect(); + + if (!successData) + { + State = ConnectionState.ERROR; + LastError = m_Manager.LastError; + } + else if (!successVoice) + { + State = ConnectionState.ERROR; + LastError = m_VoiceManager.LastError; + } + else State = ConnectionState.IN_LOBBY; + + + return successData & successVoice; + } + + public async Task JoinRoom(RoomCreateData RoomData) + { + State = ConnectionState.JOINING_ROOM; + + bool successData = false; + if (m_Manager != null) successData = await m_Manager.JoinRoom(RoomData); + + bool successVoice = false; + if (m_VoiceManager != null) successVoice = await m_VoiceManager.JoinRoom(RoomData); + m_VoiceManager?.StartSpeaking(); + + if (!successData) + { + State = ConnectionState.ERROR; + LastError = m_Manager.LastError; + } + else if (!successVoice) + { + State = ConnectionState.ERROR; + LastError = m_VoiceManager.LastError; + } + else State = ConnectionState.IN_ROOM; + + return successData & successVoice; + } + + public async Task LeaveRoom(bool force = false) + { + State = ConnectionState.LEAVING_ROOM; + + bool successData = false; + if (m_Manager != null) successData = await m_Manager.LeaveRoom(); + + bool successVoice = false; + m_VoiceManager?.StopSpeaking(); + if (m_VoiceManager != null) successVoice = await m_VoiceManager.LeaveRoom(); + + if (!successData) + { + State = ConnectionState.ERROR; + LastError = m_Manager.LastError; + } + else if (!successVoice) + { + State = ConnectionState.ERROR; + LastError = m_VoiceManager.LastError; + } + else State = ConnectionState.IN_LOBBY; + + return successData & successVoice; + } + + public async Task Disconnect() + { + State = ConnectionState.DISCONNECTING; + + bool successData = false; + if (m_Manager != null) successData = await m_Manager.Disconnect(); + + bool successVoice = false; + if (m_VoiceManager != null) successVoice = await m_VoiceManager.Disconnect(); + + if (!successData) + { + State = ConnectionState.ERROR; + LastError = m_Manager.LastError; + } + else if (!successVoice) + { + State = ConnectionState.ERROR; + LastError = m_VoiceManager.LastError; + } + else State = ConnectionState.DISCONNECTED; + + return successData & successVoice; + } + + public bool DoesRoomNameExist(string roomName) + { + return m_RoomData.Any(room => room.roomName == roomName); + } + + void OnRoomDataRefreshed(List rooms) + { + m_RoomData = rooms; + } + + 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(); +#if OCULUS_SUPPORTED + // 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; + } +#endif // OCULUS_SUPPORTED + } + + if (newUser) + { + ShareAnchors(); + } + } + + void OnLocalPlayerJoined(int id, ITransientData playerData) + { + m_LocalPlayer = playerData; + } + + void OnRemotePlayerJoined(int id, ITransientData playerData) + { + Debug.Log("Adding new player to track."); + playerData.PlayerId = id; + m_RemotePlayers.Add(playerData); + } + + void OnPlayerLeft(int id) + { + if (m_LocalPlayer.PlayerId == id) + { + m_LocalPlayer = null; + Debug.Log("Possible to get here!"); + return; + } + var copy = m_RemotePlayers.ToList(); + foreach (var player in copy) + { + if (player.PlayerId == id) + { + m_RemotePlayers.Remove(player); + } + } + } + + private async void OnCommandPerformed(BaseCommand command) + { + if (State == ConnectionState.IN_ROOM) + { + 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 (State == ConnectionState.IN_ROOM) + { + m_Manager.UndoCommand(command); + } + } + + private void OnCommandRedo(BaseCommand command) + { + if (State == ConnectionState.IN_ROOM) + { + m_Manager.RedoCommand(command); + } + } + + async void ShareAnchors() + { +#if OCULUS_SUPPORTED + 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); + } + } +#endif // OCULUS_SUPPORTED + } + + private void OnConnectionHandlerDisconnected() + { + // Clean up local player reference + m_LocalPlayer = null; + + // Invoke the Disconnected event + Disconnected?.Invoke(); + } + + public void StartSpeaking() + { + m_VoiceManager?.StartSpeaking(); + } + + public void StopSpeaking() + { + m_VoiceManager?.StopSpeaking(); + } + + public bool IsDisconnectable() + { + + return State == ConnectionState.IN_ROOM || State == ConnectionState.IN_LOBBY; + } + + public bool IsConnectable() + { + return State == ConnectionState.INITIALIZED || State == ConnectionState.DISCONNECTED; + } + + public bool CanJoinRoom() + { + return State == ConnectionState.IN_LOBBY; + } + + public bool CanLeaveRoom() + { + return State == ConnectionState.IN_ROOM; + } + } +} diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs index 9635e49dbd..0a295062e7 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs @@ -12,413 +12,413 @@ // 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.Linq; -using System.Threading.Tasks; -using UnityEngine; -using Fusion; -using Fusion.Photon.Realtime; -using Fusion.Sockets; -using TiltBrush; - - -namespace OpenBrush.Multiplayer -{ - public class PhotonManager : IDataConnectionHandler, INetworkRunnerCallbacks - { - - private NetworkRunner m_Runner; - - private MultiplayerManager m_Manager; - - private List m_PlayersSpawning; - - private PhotonPlayerRig m_LocalPlayer; - - private AppSettings m_PhotonAppSettings; - - public event Action Disconnected; - - public ConnectionUserInfo UserInfo { get; set; } - public ConnectionState State { get; private set; } - public string LastError { get; private set; } - - public PhotonManager(MultiplayerManager manager) - { - m_Manager = manager; - m_PlayersSpawning = new List(); - - Init(); - - m_PhotonAppSettings = new AppSettings - { - AppIdFusion = App.Config.PhotonFusionSecrets.ClientId, - FixedRegion = "", - }; - } - - public async Task Init() - { - try - { - State = ConnectionState.INITIALISING; - var runnerGO = new GameObject("Photon Network Components"); - m_Runner = runnerGO.AddComponent(); - m_Runner.gameObject.AddComponent(); - m_Runner.ProvideInput = true; - m_Runner.AddCallbacks(this); - - } - catch (Exception ex) - { - State = ConnectionState.ERROR; - LastError = $"[PhotonManager] Failed to Initialize lobby: {ex.Message}"; - ControllerConsoleScript.m_Instance.AddNewLine(LastError); - return false; - } - - ControllerConsoleScript.m_Instance.AddNewLine("[PhotonManager] Runner Initialized"); - State = ConnectionState.INITIALIZED; - return true; - } - - public async Task Connect() - { - State = ConnectionState.CONNECTING; - - await Task.Yield(); - //return true; - var result = await m_Runner.JoinSessionLobby(SessionLobby.Shared, customAppSettings: m_PhotonAppSettings); - - if (result.Ok) - { - State = ConnectionState.IN_LOBBY; - ControllerConsoleScript.m_Instance.AddNewLine("[PhotonManager] Connected to lobby"); - } - else - { - State = ConnectionState.ERROR; - LastError = $"[PhotonManager] Failed to join lobby: {result.ErrorMessage}"; - ControllerConsoleScript.m_Instance.AddNewLine(LastError); - } - - return result.Ok; - } - - public async Task JoinRoom(RoomCreateData roomCreateData) - { - - if (m_Runner == null) Init(); - - State = ConnectionState.JOINING_ROOM; - - var args = new StartGameArgs() - { - GameMode = GameMode.Shared, - SessionName = roomCreateData.roomName, - CustomPhotonAppSettings = m_PhotonAppSettings, - PlayerCount = roomCreateData.maxPlayers != 0 ? roomCreateData.maxPlayers : null, - SceneManager = m_Runner.gameObject.GetComponent(), - Scene = UnityEngine.SceneManagement.SceneManager.GetActiveScene().buildIndex, - }; - - var result = await m_Runner.StartGame(args); - - if (result.Ok) - { - State = ConnectionState.IN_ROOM; - ControllerConsoleScript.m_Instance.AddNewLine("[PhotonManager] Joined Room"); - UserInfo = new ConnectionUserInfo { UserId = m_Runner.UserId }; - } - else - { - State = ConnectionState.ERROR; - LastError = $"[PhotonManager] Failed to join Room: {result.ErrorMessage}"; - ControllerConsoleScript.m_Instance.AddNewLine(LastError); - } - - return result.Ok; - - } - - public async Task Disconnect() - { - State = ConnectionState.DISCONNECTING; - - if (m_Runner != null) - { - - if (m_LocalPlayer != null) - { - m_Runner.Despawn(m_LocalPlayer.Object); - m_LocalPlayer = null; - } - - await m_Runner.Shutdown(forceShutdownProcedure: false); - GameObject.Destroy(m_Runner.gameObject); - - if (m_Runner.IsShutdown) - { - State = ConnectionState.DISCONNECTED; - ControllerConsoleScript.m_Instance.AddNewLine("[PhotonManager] Left Room"); - UserInfo = new ConnectionUserInfo { UserId = m_Runner.UserId }; - } - else - { - State = ConnectionState.ERROR; - LastError = $"[PhotonManager] Failed to disconnect"; - ControllerConsoleScript.m_Instance.AddNewLine(LastError); - } - - return m_Runner.IsShutdown; - } - return true; - } - - public async Task LeaveRoom(bool force) - { - - if (m_Runner != null) - { - bool success = await Disconnect(); - if (!success) return false; - success = await Connect(); - if (!success) return false; - return true; - } - return false; - - } - - 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(player.PlayerId, 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 SwitchEnvironmentCommand: - success = CommandSwitchEnvironment(command as SwitchEnvironmentCommand); - 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; - int maxPointsPerChunk = NetworkingConstants.MaxControlPointsPerChunk; - - - if (stroke.m_ControlPoints.Length > maxPointsPerChunk) - { - // Split and Send - int numSplits = stroke.m_ControlPoints.Length / maxPointsPerChunk; - - var firstStroke = new Stroke(stroke) - { - m_ControlPoints = stroke.m_ControlPoints.Take(maxPointsPerChunk).ToArray(), - m_ControlPointsToDrop = stroke.m_ControlPointsToDrop.Take(maxPointsPerChunk).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 * maxPointsPerChunk).Take(maxPointsPerChunk).ToArray(); - var dropPoints = stroke.m_ControlPointsToDrop.Skip(rounds * maxPointsPerChunk).Take(maxPointsPerChunk).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 * maxPointsPerChunk, 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; - } - - private bool CommandSwitchEnvironment(SwitchEnvironmentCommand command) - { - Guid environmentGuid = command.m_NextEnvironment.m_Guid; - PhotonRPC.RPC_SwitchEnvironment(m_Runner, environmentGuid, 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) - { - Debug.Log($"OnPlayerJoined called. PlayerRef: {player.PlayerId}"); - - try - { - - 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(player.PlayerId, m_LocalPlayer); - } - else - { - m_PlayersSpawning.Add(player); - } - } - catch (Exception ex) - { - Debug.LogError($"Exception in OnPlayerJoined: {ex.Message}"); - } - } - - public void OnPlayerLeft(NetworkRunner runner, PlayerRef player) - { - m_Manager.playerLeft?.Invoke(player.PlayerId); - } - - public void OnSessionListUpdated(NetworkRunner runner, List sessionList) - { - var roomData = new List(); - foreach (var session in sessionList) - { - RoomData data = new RoomData() - { - roomName = session.Name, - @private = session.IsOpen, - numPlayers = session.PlayerCount, - maxPlayers = session.MaxPlayers - }; - - roomData.Add(data); - } - - m_Manager.roomDataRefreshed?.Invoke(roomData); - } - #endregion - - #region Unused Photon Callbacks - public void OnShutdown(NetworkRunner runner, ShutdownReason shutdownReason) - { - Disconnected?.Invoke(); - } - 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 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 +#if FUSION_WEAVER + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using UnityEngine; +using Fusion; +using Fusion.Photon.Realtime; +using Fusion.Sockets; +using TiltBrush; + + +namespace OpenBrush.Multiplayer +{ + public class PhotonManager : IDataConnectionHandler, INetworkRunnerCallbacks + { + + private NetworkRunner m_Runner; + + private MultiplayerManager m_Manager; + + private List m_PlayersSpawning; + + private PhotonPlayerRig m_LocalPlayer; + + private AppSettings m_PhotonAppSettings; + + public event Action Disconnected; + + public ConnectionUserInfo UserInfo { get; set; } + public ConnectionState State { get; private set; } + public string LastError { get; private set; } + + public PhotonManager(MultiplayerManager manager) + { + m_Manager = manager; + m_PlayersSpawning = new List(); + + Init(); + + m_PhotonAppSettings = new AppSettings + { + AppIdFusion = App.Config.PhotonFusionSecrets.ClientId, + FixedRegion = "", + }; + } + + public async Task Init() + { + try + { + State = ConnectionState.INITIALISING; + var runnerGO = new GameObject("Photon Network Components"); + m_Runner = runnerGO.AddComponent(); + m_Runner.gameObject.AddComponent(); + m_Runner.ProvideInput = true; + m_Runner.AddCallbacks(this); + + } + catch (Exception ex) + { + State = ConnectionState.ERROR; + LastError = $"[PhotonManager] Failed to Initialize lobby: {ex.Message}"; + ControllerConsoleScript.m_Instance.AddNewLine(LastError); + return false; + } + + ControllerConsoleScript.m_Instance.AddNewLine("[PhotonManager] Runner Initialized"); + State = ConnectionState.INITIALIZED; + return true; + } + + public async Task Connect() + { + State = ConnectionState.CONNECTING; + + await Task.Yield(); + //return true; + var result = await m_Runner.JoinSessionLobby(SessionLobby.Shared, customAppSettings: m_PhotonAppSettings); + + if (result.Ok) + { + State = ConnectionState.IN_LOBBY; + ControllerConsoleScript.m_Instance.AddNewLine("[PhotonManager] Connected to lobby"); + } + else + { + State = ConnectionState.ERROR; + LastError = $"[PhotonManager] Failed to join lobby: {result.ErrorMessage}"; + ControllerConsoleScript.m_Instance.AddNewLine(LastError); + } + + return result.Ok; + } + + public async Task JoinRoom(RoomCreateData roomCreateData) + { + + if (m_Runner == null) Init(); + + State = ConnectionState.JOINING_ROOM; + + var args = new StartGameArgs() + { + GameMode = GameMode.Shared, + SessionName = roomCreateData.roomName, + CustomPhotonAppSettings = m_PhotonAppSettings, + PlayerCount = roomCreateData.maxPlayers != 0 ? roomCreateData.maxPlayers : null, + SceneManager = m_Runner.gameObject.GetComponent(), + Scene = UnityEngine.SceneManagement.SceneManager.GetActiveScene().buildIndex, + }; + + var result = await m_Runner.StartGame(args); + + if (result.Ok) + { + State = ConnectionState.IN_ROOM; + ControllerConsoleScript.m_Instance.AddNewLine("[PhotonManager] Joined Room"); + UserInfo = new ConnectionUserInfo { UserId = m_Runner.UserId }; + } + else + { + State = ConnectionState.ERROR; + LastError = $"[PhotonManager] Failed to join Room: {result.ErrorMessage}"; + ControllerConsoleScript.m_Instance.AddNewLine(LastError); + } + + return result.Ok; + + } + + public async Task Disconnect() + { + State = ConnectionState.DISCONNECTING; + + if (m_Runner != null) + { + + if (m_LocalPlayer != null) + { + m_Runner.Despawn(m_LocalPlayer.Object); + m_LocalPlayer = null; + } + + await m_Runner.Shutdown(forceShutdownProcedure: false); + GameObject.Destroy(m_Runner.gameObject); + + if (m_Runner.IsShutdown) + { + State = ConnectionState.DISCONNECTED; + ControllerConsoleScript.m_Instance.AddNewLine("[PhotonManager] Left Room"); + UserInfo = new ConnectionUserInfo { UserId = m_Runner.UserId }; + } + else + { + State = ConnectionState.ERROR; + LastError = $"[PhotonManager] Failed to disconnect"; + ControllerConsoleScript.m_Instance.AddNewLine(LastError); + } + + return m_Runner.IsShutdown; + } + return true; + } + + public async Task LeaveRoom(bool force) + { + + if (m_Runner != null) + { + bool success = await Disconnect(); + if (!success) return false; + success = await Connect(); + if (!success) return false; + return true; + } + return false; + + } + + 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(player.PlayerId, 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 SwitchEnvironmentCommand: + success = CommandSwitchEnvironment(command as SwitchEnvironmentCommand); + 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; + int maxPointsPerChunk = NetworkingConstants.MaxControlPointsPerChunk; + + + if (stroke.m_ControlPoints.Length > maxPointsPerChunk) + { + // Split and Send + int numSplits = stroke.m_ControlPoints.Length / maxPointsPerChunk; + + var firstStroke = new Stroke(stroke) + { + m_ControlPoints = stroke.m_ControlPoints.Take(maxPointsPerChunk).ToArray(), + m_ControlPointsToDrop = stroke.m_ControlPointsToDrop.Take(maxPointsPerChunk).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 * maxPointsPerChunk).Take(maxPointsPerChunk).ToArray(); + var dropPoints = stroke.m_ControlPointsToDrop.Skip(rounds * maxPointsPerChunk).Take(maxPointsPerChunk).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 * maxPointsPerChunk, 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; + } + + private bool CommandSwitchEnvironment(SwitchEnvironmentCommand command) + { + Guid environmentGuid = command.m_NextEnvironment.m_Guid; + PhotonRPC.RPC_SwitchEnvironment(m_Runner, environmentGuid, 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) + { + Debug.Log($"OnPlayerJoined called. PlayerRef: {player.PlayerId}"); + + try + { + + 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(player.PlayerId, m_LocalPlayer); + } + else + { + m_PlayersSpawning.Add(player); + } + } + catch (Exception ex) + { + Debug.LogError($"Exception in OnPlayerJoined: {ex.Message}"); + } + } + + public void OnPlayerLeft(NetworkRunner runner, PlayerRef player) + { + m_Manager.playerLeft?.Invoke(player.PlayerId); + } + + public void OnSessionListUpdated(NetworkRunner runner, List sessionList) + { + var roomData = new List(); + foreach (var session in sessionList) + { + RoomData data = new RoomData() + { + roomName = session.Name, + @private = session.IsOpen, + numPlayers = session.PlayerCount, + maxPlayers = session.MaxPlayers + }; + + roomData.Add(data); + } + + m_Manager.roomDataRefreshed?.Invoke(roomData); + } + #endregion + + #region Unused Photon Callbacks + public void OnShutdown(NetworkRunner runner, ShutdownReason shutdownReason) + { + Disconnected?.Invoke(); + } + 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 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/PhotonVoiceManager.cs b/Assets/Scripts/Multiplayer/Photon/PhotonVoiceManager.cs index 361b744ec5..27ddb492fb 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonVoiceManager.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonVoiceManager.cs @@ -12,300 +12,300 @@ // See the License for the specific language governing permissions and // limitations under the License. -#if PHOTON_UNITY_NETWORKING && PHOTON_VOICE_DEFINED - -using Fusion; -using OpenBrush.Multiplayer; -using Photon.Realtime; -using Photon.Voice.Unity; -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using TiltBrush; -using UnityEngine; - -public class PhotonVoiceManager : IVoiceConnectionHandler, IConnectionCallbacks, IMatchmakingCallbacks -{ - private VoiceConnection m_VoiceConnection; - private MultiplayerManager m_Manager; - private AppSettings m_PhotonVoiceAppSettings; - private Recorder m_Recorder; - private bool ConnectedToMaster = false; - - public ConnectionUserInfo UserInfo { get; set; } - public ConnectionState State { get; private set; } - public string LastError { get; private set; } - - public PhotonVoiceManager(MultiplayerManager manager) - { - m_Manager = manager; - Init(); - } - - public async Task Init() - { - - try - { - State = ConnectionState.INITIALISING; - m_VoiceConnection = GameObject.FindFirstObjectByType(); - if (m_VoiceConnection == null) throw new Exception("[PhotonVoiceManager] VoiceConnection component not found in scene"); - - m_VoiceConnection.Settings = new AppSettings - { - AppIdVoice = App.Config.PhotonVoiceSecrets.ClientId, - FixedRegion = "", - }; - - m_VoiceConnection.Client.AddCallbackTarget(this); - - } - catch (Exception ex) - { - State = ConnectionState.ERROR; - LastError = $"[PhotonVoiceManager] Failed to Initialize lobby: {ex.Message}"; - ControllerConsoleScript.m_Instance.AddNewLine(LastError); - return false; - } - - ControllerConsoleScript.m_Instance.AddNewLine("[PhotonVoiceManager] Succesfully initialized"); - State = ConnectionState.INITIALIZED; - return true; - - } - - public async Task Connect() - { - State = ConnectionState.CONNECTING; - - m_VoiceConnection.Client.UserId = m_Manager.UserInfo.UserId; - - if (!m_VoiceConnection.Client.IsConnected) - { - //ControllerConsoleScript.m_Instance.AddNewLine("[PhotonVoiceManager] Attempting to connect Voice Server..."); - m_VoiceConnection.ConnectUsingSettings(); - while (!ConnectedToMaster) - { - //ControllerConsoleScript.m_Instance.AddNewLine("Waiting for Voice Connection to establish..."); - await Task.Delay(100); - } - } - - if (ConnectedToMaster) - { - State = ConnectionState.IN_LOBBY; - ControllerConsoleScript.m_Instance.AddNewLine("[PhotonVoiceManager] Connection successfully established."); - } - else - { - State = ConnectionState.ERROR; - LastError = $"[PhotonVoiceManager] Failed to connect."; - ControllerConsoleScript.m_Instance.AddNewLine(LastError); - } - - return ConnectedToMaster; - } - - public async Task JoinRoom(RoomCreateData RoomData) - { - State = ConnectionState.JOINING_ROOM; - - if (!m_VoiceConnection.Client.IsConnected) - { - bool connected = await Connect(); - if (!connected) - { - return false; - } - } - - var RoomParameters = new EnterRoomParams { RoomName = RoomData.roomName }; - bool roomJoined = m_VoiceConnection.Client.OpJoinOrCreateRoom(RoomParameters); - - if (roomJoined) - { - State = ConnectionState.IN_ROOM; - ControllerConsoleScript.m_Instance.AddNewLine($"[PhotonVoiceManager] Successfully joined room"); - } - else - { - State = ConnectionState.ERROR; - LastError = $"[PhotonVoiceManager] Failed to join or create room"; - ControllerConsoleScript.m_Instance.AddNewLine(LastError); - } - - return roomJoined; - } - - public async Task LeaveRoom(bool force) - { - State = ConnectionState.DISCONNECTING; - - if (!m_VoiceConnection.Client.InRoom) return false; - - bool leftRoom = m_VoiceConnection.Client.OpLeaveRoom(false); - - if (!leftRoom) - { - //ControllerConsoleScript.m_Instance.AddNewLine("[PhotonVoiceManager] Failed to initiate leaving the room."); - return false; - } - - //ControllerConsoleScript.m_Instance.AddNewLine("Initiated leaving the room..."); - - while (m_VoiceConnection.ClientState != ClientState.ConnectedToMasterServer) - { - await Task.Delay(100); - } - - if (m_VoiceConnection.ClientState == ClientState.ConnectedToMasterServer) - { - State = ConnectionState.DISCONNECTED; - ControllerConsoleScript.m_Instance.AddNewLine("[PhotonVoiceManager] Successfully left the room."); - return true; - } - else - { - State = ConnectionState.ERROR; - ControllerConsoleScript.m_Instance.AddNewLine("[PhotonVoiceManager] Failed to leave the room."); - return false; - } - } - - public async Task Disconnect() - { - - State = ConnectionState.DISCONNECTING; - - if (!m_VoiceConnection.Client.IsConnected) - { - //ControllerConsoleScript.m_Instance.AddNewLine("[PhotonVoiceManager] Voice Server is already disconnected."); - return true; - } - - m_VoiceConnection.Client.Disconnect(); - - while (m_VoiceConnection.ClientState != ClientState.Disconnected) - { - await Task.Delay(100); - //ControllerConsoleScript.m_Instance.AddNewLine("Disconnecting."); - } - - if (m_VoiceConnection.ClientState == ClientState.Disconnected) - { - State = ConnectionState.DISCONNECTED; - ControllerConsoleScript.m_Instance.AddNewLine("[PhotonVoiceManager] Successfully disconnected from Server."); - return true; - } - else - { - State = ConnectionState.ERROR; - ControllerConsoleScript.m_Instance.AddNewLine("[PhotonVoiceManager] Failed to disconnect from Server."); - return false; - } - } - - public bool StartSpeaking() - { - m_Recorder = m_VoiceConnection.PrimaryRecorder; - if (m_Recorder == null) - { - ControllerConsoleScript.m_Instance.AddNewLine("Recorder not found! Ensure it's attached to a GameObject."); - return false; - } - - // m_Recorder.DebugEchoMode = true; - m_Recorder.TransmitEnabled = true; - return true; - } - - public bool StopSpeaking() - { - if (m_Recorder != null) - { - m_Recorder.TransmitEnabled = false; - return true; - } - return false; - } - - - #region MatchmakingCallbacks - - public void OnCreatedRoom() - { - - } - - public void OnCreateRoomFailed(short returnCode, string message) - { - Debug.LogErrorFormat("OnCreateRoomFailed errorCode={0} errorMessage={1}", returnCode, message); - } - - public void OnFriendListUpdate(List friendList) - { - - } - - public void OnJoinedRoom() - { - } - - public void OnJoinRandomFailed(short returnCode, string message) - { - Debug.LogErrorFormat("OnJoinRandomFailed errorCode={0} errorMessage={1}", returnCode, message); - } - - public void OnJoinRoomFailed(short returnCode, string message) - { - Debug.LogErrorFormat("OnJoinRoomFailed errorCode={1} errorMessage={2}", returnCode, message); - } - - public void OnLeftRoom() - { - - } - - #endregion - - #region ConnectionCallbacks - - public void OnConnected() - { - - } - - public void OnConnectedToMaster() - { - ConnectedToMaster = true; - } - - public void OnDisconnected(DisconnectCause cause) - { - if (cause == DisconnectCause.None || cause == DisconnectCause.DisconnectByClientLogic || cause == DisconnectCause.ApplicationQuit) - { - return; - } - Debug.LogErrorFormat("OnDisconnected cause={0}", cause); - } - - public void OnRegionListReceived(RegionHandler regionHandler) - { - - } - - public void OnCustomAuthenticationResponse(Dictionary data) - { - - } - - public void OnCustomAuthenticationFailed(string debugMessage) - { - - } - - - #endregion - -} +#if PHOTON_UNITY_NETWORKING && PHOTON_VOICE_DEFINED + +using Fusion; +using OpenBrush.Multiplayer; +using Photon.Realtime; +using Photon.Voice.Unity; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using TiltBrush; +using UnityEngine; + +public class PhotonVoiceManager : IVoiceConnectionHandler, IConnectionCallbacks, IMatchmakingCallbacks +{ + private VoiceConnection m_VoiceConnection; + private MultiplayerManager m_Manager; + private AppSettings m_PhotonVoiceAppSettings; + private Recorder m_Recorder; + private bool ConnectedToMaster = false; + + public ConnectionUserInfo UserInfo { get; set; } + public ConnectionState State { get; private set; } + public string LastError { get; private set; } + + public PhotonVoiceManager(MultiplayerManager manager) + { + m_Manager = manager; + Init(); + } + + public async Task Init() + { + + try + { + State = ConnectionState.INITIALISING; + m_VoiceConnection = GameObject.FindFirstObjectByType(); + if (m_VoiceConnection == null) throw new Exception("[PhotonVoiceManager] VoiceConnection component not found in scene"); + + m_VoiceConnection.Settings = new AppSettings + { + AppIdVoice = App.Config.PhotonVoiceSecrets.ClientId, + FixedRegion = "", + }; + + m_VoiceConnection.Client.AddCallbackTarget(this); + + } + catch (Exception ex) + { + State = ConnectionState.ERROR; + LastError = $"[PhotonVoiceManager] Failed to Initialize lobby: {ex.Message}"; + ControllerConsoleScript.m_Instance.AddNewLine(LastError); + return false; + } + + ControllerConsoleScript.m_Instance.AddNewLine("[PhotonVoiceManager] Succesfully initialized"); + State = ConnectionState.INITIALIZED; + return true; + + } + + public async Task Connect() + { + State = ConnectionState.CONNECTING; + + m_VoiceConnection.Client.UserId = m_Manager.UserInfo.UserId; + + if (!m_VoiceConnection.Client.IsConnected) + { + //ControllerConsoleScript.m_Instance.AddNewLine("[PhotonVoiceManager] Attempting to connect Voice Server..."); + m_VoiceConnection.ConnectUsingSettings(); + while (!ConnectedToMaster) + { + //ControllerConsoleScript.m_Instance.AddNewLine("Waiting for Voice Connection to establish..."); + await Task.Delay(100); + } + } + + if (ConnectedToMaster) + { + State = ConnectionState.IN_LOBBY; + ControllerConsoleScript.m_Instance.AddNewLine("[PhotonVoiceManager] Connection successfully established."); + } + else + { + State = ConnectionState.ERROR; + LastError = $"[PhotonVoiceManager] Failed to connect."; + ControllerConsoleScript.m_Instance.AddNewLine(LastError); + } + + return ConnectedToMaster; + } + + public async Task JoinRoom(RoomCreateData RoomData) + { + State = ConnectionState.JOINING_ROOM; + + if (!m_VoiceConnection.Client.IsConnected) + { + bool connected = await Connect(); + if (!connected) + { + return false; + } + } + + var RoomParameters = new EnterRoomParams { RoomName = RoomData.roomName }; + bool roomJoined = m_VoiceConnection.Client.OpJoinOrCreateRoom(RoomParameters); + + if (roomJoined) + { + State = ConnectionState.IN_ROOM; + ControllerConsoleScript.m_Instance.AddNewLine($"[PhotonVoiceManager] Successfully joined room"); + } + else + { + State = ConnectionState.ERROR; + LastError = $"[PhotonVoiceManager] Failed to join or create room"; + ControllerConsoleScript.m_Instance.AddNewLine(LastError); + } + + return roomJoined; + } + + public async Task LeaveRoom(bool force) + { + State = ConnectionState.DISCONNECTING; + + if (!m_VoiceConnection.Client.InRoom) return false; + + bool leftRoom = m_VoiceConnection.Client.OpLeaveRoom(false); + + if (!leftRoom) + { + //ControllerConsoleScript.m_Instance.AddNewLine("[PhotonVoiceManager] Failed to initiate leaving the room."); + return false; + } + + //ControllerConsoleScript.m_Instance.AddNewLine("Initiated leaving the room..."); + + while (m_VoiceConnection.ClientState != ClientState.ConnectedToMasterServer) + { + await Task.Delay(100); + } + + if (m_VoiceConnection.ClientState == ClientState.ConnectedToMasterServer) + { + State = ConnectionState.DISCONNECTED; + ControllerConsoleScript.m_Instance.AddNewLine("[PhotonVoiceManager] Successfully left the room."); + return true; + } + else + { + State = ConnectionState.ERROR; + ControllerConsoleScript.m_Instance.AddNewLine("[PhotonVoiceManager] Failed to leave the room."); + return false; + } + } + + public async Task Disconnect() + { + + State = ConnectionState.DISCONNECTING; + + if (!m_VoiceConnection.Client.IsConnected) + { + //ControllerConsoleScript.m_Instance.AddNewLine("[PhotonVoiceManager] Voice Server is already disconnected."); + return true; + } + + m_VoiceConnection.Client.Disconnect(); + + while (m_VoiceConnection.ClientState != ClientState.Disconnected) + { + await Task.Delay(100); + //ControllerConsoleScript.m_Instance.AddNewLine("Disconnecting."); + } + + if (m_VoiceConnection.ClientState == ClientState.Disconnected) + { + State = ConnectionState.DISCONNECTED; + ControllerConsoleScript.m_Instance.AddNewLine("[PhotonVoiceManager] Successfully disconnected from Server."); + return true; + } + else + { + State = ConnectionState.ERROR; + ControllerConsoleScript.m_Instance.AddNewLine("[PhotonVoiceManager] Failed to disconnect from Server."); + return false; + } + } + + public bool StartSpeaking() + { + m_Recorder = m_VoiceConnection.PrimaryRecorder; + if (m_Recorder == null) + { + ControllerConsoleScript.m_Instance.AddNewLine("Recorder not found! Ensure it's attached to a GameObject."); + return false; + } + + // m_Recorder.DebugEchoMode = true; + m_Recorder.TransmitEnabled = true; + return true; + } + + public bool StopSpeaking() + { + if (m_Recorder != null) + { + m_Recorder.TransmitEnabled = false; + return true; + } + return false; + } + + + #region MatchmakingCallbacks + + public void OnCreatedRoom() + { + + } + + public void OnCreateRoomFailed(short returnCode, string message) + { + Debug.LogErrorFormat("OnCreateRoomFailed errorCode={0} errorMessage={1}", returnCode, message); + } + + public void OnFriendListUpdate(List friendList) + { + + } + + public void OnJoinedRoom() + { + } + + public void OnJoinRandomFailed(short returnCode, string message) + { + Debug.LogErrorFormat("OnJoinRandomFailed errorCode={0} errorMessage={1}", returnCode, message); + } + + public void OnJoinRoomFailed(short returnCode, string message) + { + Debug.LogErrorFormat("OnJoinRoomFailed errorCode={1} errorMessage={2}", returnCode, message); + } + + public void OnLeftRoom() + { + + } + + #endregion + + #region ConnectionCallbacks + + public void OnConnected() + { + + } + + public void OnConnectedToMaster() + { + ConnectedToMaster = true; + } + + public void OnDisconnected(DisconnectCause cause) + { + if (cause == DisconnectCause.None || cause == DisconnectCause.DisconnectByClientLogic || cause == DisconnectCause.ApplicationQuit) + { + return; + } + Debug.LogErrorFormat("OnDisconnected cause={0}", cause); + } + + public void OnRegionListReceived(RegionHandler regionHandler) + { + + } + + public void OnCustomAuthenticationResponse(Dictionary data) + { + + } + + public void OnCustomAuthenticationFailed(string debugMessage) + { + + } + + + #endregion + +} #endif \ No newline at end of file diff --git a/Assets/Scripts/SketchControlsScript.cs b/Assets/Scripts/SketchControlsScript.cs index 474c8adac0..da6cc58de1 100644 --- a/Assets/Scripts/SketchControlsScript.cs +++ b/Assets/Scripts/SketchControlsScript.cs @@ -1,5232 +1,5232 @@ -// Copyright 2020 The Tilt 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 OpenBrush.Multiplayer; -using System; -using System.Collections; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using TiltBrush.Layers; -using UnityEngine; -using UnityEngine.InputSystem; -using SymmetryMode = TiltBrush.PointerManager.SymmetryMode; - -namespace TiltBrush -{ - - public class SketchControlsScript : MonoBehaviour - { - public const string kRemoveHeadsetFyi = "Remove headset to view."; - const string kTiltBrushGalleryUrl = "https://poly.google.com/tiltbrush"; - const string kBlocksGalleryUrl = "https://poly.google.com/blocks"; - const string kPolyMainPageUri = "https://poly.google.com"; - - static public SketchControlsScript m_Instance; - static bool sm_enableGrabHaptics = true; - - // ------------------------------------------------------------ - // Constants and types - // ------------------------------------------------------------ - - public enum GlobalCommands - { - Null, - Save, - SaveNew, - Load, - NewSketch, - StraightEdge, - AutoOrient, - Undo, - Redo, - Tiltasaurus, - LightingHdr, - AudioVisualization, - ResetAllPanels, - SketchOrigin, - SymmetryPlane, - MultiMirror, - ViewOnly, - SaveGallery, - LightingLdr, - ShowSketchFolder, - About, - LoadNamedFile, // iParam1 : (optional) - send through a LoadSpeed as int - DropCam, - CuratedGallery, - Unused_UploadToCloud, - AnalyticsEnabled_Deprecated, - Credits, - LogOutOfGenericCloud, - DraftingVisibility, - DeleteSketch, - ShowWindowGUI, - MorePanels, - Cameras, - FAQ, - ExportRaw, - IRC, - YouTubeChat, - CameraOptions, - StencilsDisabled, - AdvancedTools, - FloatingPanelsMode, - StraightEdgeMeterDisplay, - Sketchbook, - ExportAll, - Lights, - SaveAndUpload, - StraightEdgeShape, - SaveOptions, - SketchbookMenu, - Disco, - ViewOnlineGallery, - CancelUpload, - AdvancedPanelsToggle, - Music, - Duplicate, - ToggleGroupStrokesAndWidgets, - SaveModel, - ViewPolyPage, - ViewPolyGallery, - ExportListed, - RenderCameraPath, - ToggleProfiling, - DoAutoProfile, - DoAutoProfileAndQuit, - ToggleSettings, - SummonMirror, - InvertSelection, - SelectAll, - FlipSelection, - ToggleBrushLab, - ReleaseNotes, - ToggleCameraPostEffects, - ToggleWatermark, - AccountInfo, - // LoadConfirmUnsaved -> LoadWaitOnDownload -> LoadConfirmComplex -> LoadComplexHigh -> Load - LoadConfirmUnsaved, - LoadConfirmComplex, - MemoryWarning, - MemoryExceeded, - ViewLastUpload, - LoadConfirmComplexHigh, - ShowTos, - ShowPrivacy, - ShowQuestSideLoading, - AshleysSketch, - UnloadReferenceImageCatalog, - SaveOnLocalChanges, - ToggleCameraPathVisuals, - ToggleCameraPathPreview, - DeleteCameraPath, - RecordCameraPath, - SelectCameraPath, - ToggleAutosimplification, - ShowGoogleDrive, - GoogleDriveSync_Folder, // iParam1: folder id as DriveSync.SyncedFolderType - GoogleDriveSync, - LoginToGenericCloud, // iParam1: Cloud enum - UploadToGenericCloud, // iParam1: Cloud enum - LoadWaitOnDownload, - SignOutConfirm, - ReadOnlyNotice, - ShowContribution, - - // Open Brush Reserved Enums 1000-1999 - LanguagePopup = 1000, - MultiplayerTogglePanel = 1001, - MultiplayerPanelOptions = 1002, // iParam1: Popup options - MultiplayerJoinRoom = 1004, - EditMultiplayerRoomName = 1005, - MultiplayerLeaveRoom = 1006, - MultiplayerConnect = 1007, - MultiplayerDisconnect = 1008, - EditMultiplayerNickName = 1009, - - RenameSketch = 5200, - OpenLayerOptionsPopup = 5201, - RenameLayer = 5202, - OpenDirectorChooserPopup = 5800, - OpenScriptsCommandsList = 6000, - OpenScriptsList = 6001, - OpenExampleScriptsList = 6002, - SymmetryTwoHanded = 6003, - OpenColorOptionsPopup = 7000, - ChangeSnapAngle = 8000, - MergeBrushStrokes = 10000, - RepaintOptions = 11500, - OpenNumericInputPopup = 12000 - } - - public enum ControlsType - { - KeyboardMouse, - SixDofControllers, - ViewingOnly - } - - public enum DraftingVisibilityOption - { - Visible, - Transparent, - Hidden - } - - public enum InputState - { - Standard, - Pan, - Rotation, - HeadLock, - ControllerLock, - PushPull, - BrushSize, - Save, - Load, - Num - } - - public enum LoadSpeed - { - Normal = -1, - Quick = 1, - } - - const float kControlPointHistoryMaxTime = 0.1f; - - class GazeResult - { - public bool m_HitWithGaze; - public bool m_HitWithController; - // ReSharper disable once NotAccessedField.Local - public bool m_WithinView; - public float m_ControllerDistance; - public Vector3 m_GazePosition; - public Vector3 m_ControllerPosition; - public InputManager.ControllerName m_ControllerName; - } - - class GrabWidgetControllerInfo - { - public InputManager.ControllerName m_Name; - /// Transform of controller at the time the grab started - public TrTransform m_BaseControllerXf; - /// "local" transform of widget (relative to controller), at the time the grab started. - /// The widget isn't parented to the controller, but if it were, this would be its transform. - public TrTransform m_BaseWidgetXf_LS; - } - - struct GrabWidgetHoldPoint - { - // ReSharper disable once NotAccessedField.Local - public InputManager.ControllerName m_Name; - public float m_BirthTime; - public Vector3 m_Pos; // where controller is holding the widget - public Quaternion m_Rot; - } - - class InputStateConfig - { - public bool m_AllowDrawing; - public bool m_AllowMovement; - public bool m_ShowGizmo; - } - - enum FadeState - { - None, - FadeOn, - FadeOff - } - - enum GrabWidgetState - { - None, - OneHand, - TwoHands - } - - enum GrabWorldState - { - Normal, - ResettingTransform, - ResetDone - } - - private enum WorldTransformResetState - { - Default, - Requested, - FadingToBlack, - FadingToScene, - } - - enum RotationType - { - All, - RollOnly - } - - enum GrabIntersectionState - { - RequestIntersections, - ReadBrush, - ReadWand - } - - // ------------------------------------------------------------ - // Inspector data (read-only even if public) - // ------------------------------------------------------------ - - public GameObject m_SketchSurface; - public SketchMemoryScript.PlaybackMode m_DefaultSketchPlaybackMode; - public float m_GazeMaxAngleFromPointing = 85.0f; - public float m_GazeMaxAngleFacingToForward = 80.0f; - - [SerializeField] bool m_AtlasIconTextures; - - [SerializeField] SaveIconTool m_SaveIconTool; - [SerializeField] DropCamWidget m_DropCam; - [SerializeField] string m_CreditsSketchFilename; - [SerializeField] string m_AshleysSketchFilename; - [SerializeField] float m_DefaultSketchLoadSpeed; - [SerializeField] GameObject m_TransformGizmoPrefab; - - [SerializeField] GameObject m_RotationIconPrefab; - [SerializeField] float m_GazeMaxAngleFromFacing = 70.0f; - [SerializeField] float m_GazeMaxDistance = 10.0f; - [SerializeField] float m_GazeControllerPointingDistance; - [SerializeField] float m_GazePanelDectivationDelay = 0.25f; - - [SerializeField] GameObject m_UIReticle; - [SerializeField] GameObject m_UIReticleMobile; - [SerializeField] GameObject m_UIReticleSixDofController; - - [SerializeField] float m_DoubleTapWindow; - [SerializeField] float m_PushPullScale; - [SerializeField] RotationCursorScript m_RotationCursor; - [SerializeField] float m_RotationMaxAngle; - - [SerializeField] float m_RotationScalar; - [SerializeField] float m_RotationRollScalar; - [SerializeField] float m_PanScalar; - - [SerializeField] float m_AdjustToolSizeScalar; - - [SerializeField] GameObject m_IRCChatPrefab; - [SerializeField] GameObject m_YouTubeChatPrefab; - [SerializeField] GameObject m_Decor; - [SerializeField] BaseTool.ToolType m_InitialTool = BaseTool.ToolType.SketchSurface; - [SerializeField] string m_ReleaseNotesURL; - [SerializeField] string m_HelpCenterURL; - [SerializeField] string m_ThirdPartyNoticesURL; - [SerializeField] string m_TosURL; - [SerializeField] string m_PrivacyURL; - [SerializeField] string m_QuestSideLoadingHowToURL; - - [Multiline] - [SerializeField] string m_ContributionPromoText; - [SerializeField] string m_ContributionURL; - - [SerializeField] float m_WorldTransformMinScale = .1f; - [SerializeField] float m_WorldTransformMaxScale = 10.0f; - - [Header("Undo/Redo Hold")] - [SerializeField] float m_UndoRedoHold_DurationBeforeStart; - [SerializeField] float m_UndoRedoHold_RepeatInterval; - - [Header("Pin Cushion")] - [SerializeField] GameObject m_PinCushionPrefab; - - [Header("Grabbing and tossing")] - [SerializeField] float m_GrabWorldFadeSpeed = 8.0f; - [SerializeField] Color m_GrabWorldGridColor = new Color(0.0f, 1.0f, 1.0f, 0.2f); - [SerializeField] ControllerGrabVisuals m_ControllerGrabVisuals; - [SerializeField] float m_WidgetGpuIntersectionRadius; - - [Header("Saving")] - [SerializeField] int m_NumStrokesForSaveIcon = 50; - - [NonSerialized] public Color m_GrabHighlightActiveColor; - [NonSerialized] public bool m_DisableWorldGrabbing = false; - - /// Throwing an object faster than this means it's a "toss". Units are m/s. - public float m_TossThresholdMeters = 3f; - /// Angular motion contributes more towards the toss velocity the larger the object is; - /// or rather, the larger the distance between the grab point and the object's center. - /// To prevent large objects from being too-easily-tossed, bound that distance. - public float m_TossMaxPivotDistMeters = 0.33f; - - // ------------------------------------------------------------ - // Internal data - // ------------------------------------------------------------ - - private SketchSurfacePanel m_SketchSurfacePanel; - private SketchMemoryScript.PlaybackMode m_SketchPlaybackMode; - private GameObject m_TransformGizmo; - private TransformGizmoScript m_TransformGizmoScript; - private GameObject m_RotationIcon; - private float m_MouseDeltaX; - private float m_MouseDeltaY; - private float m_MouseDeltaXScaled; - private float m_MouseDeltaYScaled; - private float m_PositionOffsetResetTapTime; - private bool m_EatToolScaleInput; - - private PanelManager m_PanelManager; - private WidgetManager m_WidgetManager; - private PinCushion m_PinCushion; - private bool m_EatPinCushionInput; - - // This is the gaze that was used to compute m_CurrentGazeHitPoint. - // It is not a general substitute for ViewpointScript.Gaze. - private Ray m_CurrentGazeRay; - private Quaternion m_CurrentHeadOrientation; - private GazeResult[] m_GazeResults; - private int m_CurrentGazeObject; - private bool m_EatInputGazeObject; - private Vector3 m_CurrentGazeHitPoint; - private Ray m_GazeControllerRay; - private Ray m_GazeControllerRayActivePanel; - private bool m_ForcePanelActivation = false; - private float m_GazePanelDectivationCountdown; - private bool m_PanelsVisibilityRequested; - - // Previously Experimental-Model only - private bool m_HeadOffset; - - float m_UndoHold_Timer; - float m_RedoHold_Timer; - - // Grab world member variables. - struct GrabState - { - public InputManager.ControllerName name; - public TrTransform grabTransform; - public bool grabbingWorld; - public bool grabbingGroup; - public bool startedGrabInsideWidget; - public bool eatInput; - private GrabWidget lastWidgetIntersect; - - public void SetHadBestGrabAndTriggerHaptics(GrabWidgetData data) - { - bool dormant = WidgetManager.m_Instance.WidgetsDormant; - if (data != null && !data.m_WidgetScript.AllowDormancy) - { - dormant = false; - } - GrabWidget newInsideWidget = (data != null && !dormant) ? data.m_WidgetScript : null; - if (sm_enableGrabHaptics && newInsideWidget != lastWidgetIntersect) - { - // state changed - if (newInsideWidget != null) - { - // transitioning in - InputManager.m_Instance.TriggerHaptics(name, data.m_WidgetScript.HapticDuration); - } - else - { - // transitioning out - InputManager.m_Instance.TriggerHaptics(name, 0.03f); - } - } - lastWidgetIntersect = newInsideWidget; - } - - public void ClearInsideWidget() - { - lastWidgetIntersect = null; - } - } - private GrabState m_GrabBrush = new GrabState { name = InputManager.ControllerName.Brush }; - private GrabState m_GrabWand = new GrabState { name = InputManager.ControllerName.Wand }; - - private WorldTransformResetState m_WorldTransformResetState = WorldTransformResetState.Default; - private TrTransform m_WorldTransformResetXf = TrTransform.identity; // set when reset requested - private GrabWorldState m_GrabWorldState = GrabWorldState.Normal; - private float m_WorldTransformFadeAmount; - private bool m_AllowWorldTransformLastFrame = false; - private bool m_WorldBeingGrabbed; - private TrTransform m_xfDropCamReset_RS; - - struct GpuIntersectionResult - { - public GpuIntersector.FutureModelResult result; - public List resultList; - } - private Queue m_BrushResults; - private Queue m_WandResults; - private int m_WidgetGpuIntersectionLayer; - - private GrabWidget m_CurrentGrabWidget; - private GrabWidget m_MaybeDriftingGrabWidget; // use only to clear drift - - // References to widgets, cached in the UpdateGrab_None, to be used by helper functions - // for the remainder of the frame. - private GrabWidget m_PotentialGrabWidgetBrush; - private GrabWidget m_PotentialGrabWidgetWand; - - // Flags for the explaining if the m_PotentialGrabWidget_x widgets are able to be interacted with. - // Cached in the UpdateGrab_None, used for the remainder of the frame. - private bool m_PotentialGrabWidgetBrushValid; - private bool m_PotentialGrabWidgetWandValid; - - // References to widget metadata, cached in UpdateGrab_None, to be re-used on "off frames" - // when the GPU intersector is not refreshing the nearest widget to the respective controller. - private GrabWidgetData m_BackupBrushGrabData; - private GrabWidgetData m_BackupWandGrabData; - - private GrabWidgetState m_GrabWidgetState; - private GrabWidgetControllerInfo m_GrabWidgetOneHandInfo; - private TrTransform m_GrabWidgetTwoHandBrushPrev; - private TrTransform m_GrabWidgetTwoHandWandPrev; - private Queue m_GrabWidgetHoldHistory; - - private Quaternion m_RotationOrigin; - private Vector2 m_RotationCursorOffset; - - private bool m_RotationRollActive; - private float m_RotationResetTapTime; - - private RotationType m_CurrentRotationType; - private bool m_AutoOrientAfterRotation; - - private Vector3 m_SurfaceForward; - private Vector3 m_SurfaceRight; - private Vector3 m_SurfaceUp; - - private Vector3 m_SurfaceLockOffset; - private Vector3 m_SurfaceLockBaseSurfacePosition; - private Vector3 m_SurfaceLockBaseControllerPosition; - private Quaternion m_SurfaceLockBaseHeadRotation; - private Quaternion m_SurfaceLockBaseControllerRotation; - private Quaternion m_SurfaceLockBaseSurfaceRotation; - private InputManager.ControllerName m_SurfaceLockActingController; - private float m_SurfaceLockControllerBaseScalar; - private float m_SurfaceLockControllerScalar; - - private bool m_PositioningPanelWithHead; - private Quaternion m_PositioningPanelBaseHeadRotation; - private Vector3 m_PositioningPanelOffset; - private float m_PositioningTimer; - private float m_PositioningSpeed; - - private DraftingVisibilityOption m_DraftingVisibility = DraftingVisibilityOption.Visible; - - private Vector3 m_SketchOrigin; - - private ControlsType m_ControlsType; - private GrabWidget m_IRCChatWidget; - private GrabWidget m_YouTubeChatWidget; - private MultiCamCaptureRig m_MultiCamCaptureRig; - private CameraPathCaptureRig m_CameraPathCaptureRig; - - private bool m_ViewOnly = false; - - private InputState m_CurrentInputState; - private InputStateConfig[] m_InputStateConfigs; - - private GrabIntersectionState m_CurrentGrabIntersectionState; - - private float m_WorldTransformSpeedSmoothed; - - // ------------------------------------------------------------ - // Properties and events - // ------------------------------------------------------------ - - public MultiCamCaptureRig MultiCamCaptureRig - { - get { return m_MultiCamCaptureRig; } - } - - public CameraPathCaptureRig CameraPathCaptureRig - { - get { return m_CameraPathCaptureRig; } - } - - public ControllerGrabVisuals ControllerGrabVisuals - { - get { return m_ControllerGrabVisuals; } - } - - public SketchMemoryScript.PlaybackMode SketchPlaybackMode - { - get { return m_SketchPlaybackMode; } - set { m_SketchPlaybackMode = value; } - } - - public Transform m_Canvas - { - get { return App.Instance.m_CanvasTransform; } - } - - public ControlsType ActiveControlsType - { - get { return m_ControlsType; } - set { m_ControlsType = value; } - } - - public float WorldTransformMinScale - { - get - { - return App.UserConfig.Flags.UnlockScale ? m_WorldTransformMinScale * 0.01f : - m_WorldTransformMinScale; - } - } - - public float WorldTransformMaxScale - { - get - { - return App.UserConfig.Flags.UnlockScale ? m_WorldTransformMaxScale * 10.0f : - m_WorldTransformMaxScale; - } - } - - public void SetInitialTool(BaseTool.ToolType rType) - { - m_InitialTool = rType; - } - - public void SetInFreePaintMode(bool bFreePaint) - { - m_SketchSurfacePanel.SetInFreePaintMode(bFreePaint); - } - - public float GazeMaxDistance - { - get { return m_GazeMaxDistance; } - } - - public InputManager.ControllerName OneHandGrabController - { - get - { - return m_CurrentGrabWidget != null ? - m_GrabWidgetOneHandInfo.m_Name : - InputManager.ControllerName.None; - } - } - - public InputManager.ControllerName PotentialOneHandGrabController(GrabWidget widget) - { - if (m_PotentialGrabWidgetBrush == widget) - { - return InputManager.ControllerName.Brush; - } - else if (m_PotentialGrabWidgetWand == widget) - { - return InputManager.ControllerName.Wand; - } - return OneHandGrabController; - } - - public Vector3 GetSurfaceForward() { return m_SurfaceForward; } - public Vector3 GetSurfaceUp() { return m_SurfaceUp; } - public Vector3 GetSurfaceRight() { return m_SurfaceRight; } - public Vector3 GetSketchOrigin() { return m_SketchOrigin; } - public float GetDefaultSketchLoadSpeed() { return m_DefaultSketchLoadSpeed; } - public Quaternion GetCurrentHeadOrientation() { return m_CurrentHeadOrientation; } - public Vector3 GetUIReticlePos() { return m_UIReticle.transform.position; } - public Vector3 GetSweetSpotPos() { return m_PanelManager.m_SweetSpot.transform.position; } - public void SetSketchOrigin(Vector3 vOrigin) { m_SketchOrigin = vOrigin; } - - public void EatGazeObjectInput() - { - m_EatInputGazeObject = true; - m_GazePanelDectivationCountdown = 0.0f; - PointerManager.m_Instance.EatLineEnabledInput(); - SketchSurfacePanel.m_Instance.EatToolsInput(); - } - public void EatToolScaleInput() { m_EatToolScaleInput = true; } - public void EatGrabInput() - { - m_GrabWand.eatInput = true; - m_GrabBrush.eatInput = true; - } - - public bool ShouldRespondToPadInput(InputManager.ControllerName name) - { - if (name == InputManager.ControllerName.Brush && m_CurrentGazeObject != -1) - { - return m_PanelManager.GetPanel(m_CurrentGazeObject).BrushPadAnimatesOnHover(); - } - return !m_EatToolScaleInput && SketchSurfacePanel.m_Instance.CanAdjustToolSize(); - } - public void ForcePanelActivation(bool bForce) - { - m_ForcePanelActivation = bForce; - if (m_ForcePanelActivation) - { - m_GazePanelDectivationCountdown = m_GazePanelDectivationDelay; - } - } - public bool IsUserInteractingWithUI() - { - return (m_CurrentGazeObject != -1) || (m_GazePanelDectivationCountdown > 0.0f); - } - public bool IsUIBlockingUndoRedo() - { - if (m_CurrentGazeObject != -1) - { - return m_PanelManager.GetPanel(m_CurrentGazeObject).UndoRedoBlocked(); - } - return false; - } - public bool IsUserAbleToInteractWithAnyWidget() - { - return IsUserInteractingWithAnyWidget() || - (m_PotentialGrabWidgetBrush != null && m_PotentialGrabWidgetBrushValid) || - (m_PotentialGrabWidgetWand != null && m_PotentialGrabWidgetWandValid); - } - public bool IsUserInteractingWithAnyWidget() { return m_CurrentGrabWidget != null; } - public bool IsUserGrabbingAnyPanel() - { - return (m_CurrentGrabWidget != null && m_CurrentGrabWidget is PanelWidget); - } - public bool IsUsersBrushIntersectingWithSelectionWidget() - { - return (m_PotentialGrabWidgetBrush != null && - m_PotentialGrabWidgetBrushValid && - m_PotentialGrabWidgetBrush is SelectionWidget); - } - public bool IsUserIntersectingWithSelectionWidget() - { - return IsUsersBrushIntersectingWithSelectionWidget() || - (m_PotentialGrabWidgetWand != null && - m_PotentialGrabWidgetWandValid && - m_PotentialGrabWidgetWand is SelectionWidget); - } - public bool IsUserInteractingWithSelectionWidget() - { - return (m_CurrentGrabWidget != null && m_CurrentGrabWidget is SelectionWidget); - } - - public bool IsUserGrabbingWorld() { return m_GrabWand.grabbingWorld || m_GrabBrush.grabbingWorld; } - public bool IsUserGrabbingWorldWithBrushHand() { return m_GrabBrush.grabbingWorld; } - public bool IsUserTransformingWorld() { return m_GrabWand.grabbingWorld && m_GrabBrush.grabbingWorld; } - public float GetGazePanelActivationRatio() { return m_GazePanelDectivationCountdown / m_GazePanelDectivationDelay; } - public bool IsCurrentGrabWidgetPinned() { return IsUserInteractingWithAnyWidget() && m_CurrentGrabWidget.Pinned; } - public bool CanCurrentGrabWidgetBePinned() { return IsUserInteractingWithAnyWidget() && m_CurrentGrabWidget.AllowPinning; } - public bool DidUserGrabWithBothInside() { return m_GrabBrush.startedGrabInsideWidget && m_GrabWand.startedGrabInsideWidget; } - public bool IsUserGrabbingWidget(GrabWidget widget) { return widget == m_CurrentGrabWidget; } - public bool IsUserTwoHandGrabbingWidget() { return m_GrabWidgetState == GrabWidgetState.TwoHands; } - public bool IsPinCushionShowing() { return m_PinCushion.IsShowing(); } - public bool IsUserLookingAtPanel(BasePanel panel) - { - return m_CurrentGazeObject > -1 && - m_PanelManager.GetAllPanels()[m_CurrentGazeObject].m_Panel == panel; - } - - public SaveIconTool GetSaveIconTool() - { - return m_SaveIconTool; - } - - public DropCamWidget GetDropCampWidget() - { - return m_DropCam; - } - - public bool IsGrabWorldStateStable() - { - return m_GrabWorldState == GrabWorldState.Normal; - } - - // Internal: modify Coords.ScenePose or Coords.CanvasPose depending on the - // state of m_InTransformCanvasMode - TrTransform GrabbedPose - { - get - { - return App.Scene.Pose; - } - set - { - App.Scene.Pose = value; - } - } - - public Transform GazeObjectTransform() - { - if (m_CurrentGazeObject != -1) - { - return m_PanelManager.GetPanel(m_CurrentGazeObject).transform; - } - return null; - } - - public void ForceShowUIReticle(bool bVisible) - { - m_UIReticle.SetActive(bVisible); - } - - public void SetUIReticleTransform(Vector3 vPos, Vector3 vForward) - { - m_UIReticle.transform.position = vPos; - m_UIReticle.transform.forward = vForward; - } - - public bool AtlasIconTextures - { - get { return m_AtlasIconTextures; } - } - - public IconTextureAtlas IconTextureAtlas - { - get { return GetComponent(); } - } - public GrabWidget CurrentGrabWidget => m_CurrentGrabWidget; - - void DismissPopupOnCurrentGazeObject(bool force) - { - if (m_CurrentGazeObject != -1) - { - m_PanelManager.GetPanel(m_CurrentGazeObject).CloseActivePopUp(force); - } - } - - void Awake() - { - m_Instance = this; - - BrushController.m_Instance.BrushSetToDefault += OnBrushSetToDefault; - - IconTextureAtlas.Init(); - - m_MultiCamCaptureRig = GetComponentInChildren(true); - m_MultiCamCaptureRig.Init(); - - m_CameraPathCaptureRig = GetComponentInChildren(true); - m_CameraPathCaptureRig.Init(); - - m_SketchSurfacePanel = m_SketchSurface.GetComponent(); - m_PanelManager = GetComponent(); - m_PanelManager.Init(); - InitGazePanels(); - - m_WidgetManager = GetComponent(); - m_WidgetManager.Init(); - - m_InputStateConfigs = new InputStateConfig[(int)InputState.Num]; - for (int i = 0; i < (int)InputState.Num; ++i) - { - m_InputStateConfigs[i] = new InputStateConfig(); - m_InputStateConfigs[i].m_AllowDrawing = false; - m_InputStateConfigs[i].m_AllowMovement = true; - m_InputStateConfigs[i].m_ShowGizmo = false; - } - - m_InputStateConfigs[(int)InputState.Standard].m_AllowDrawing = true; - m_InputStateConfigs[(int)InputState.Pan].m_AllowDrawing = true; - m_InputStateConfigs[(int)InputState.HeadLock].m_AllowDrawing = true; - m_InputStateConfigs[(int)InputState.ControllerLock].m_AllowDrawing = true; - m_InputStateConfigs[(int)InputState.PushPull].m_AllowDrawing = true; - - m_InputStateConfigs[(int)InputState.Pan].m_AllowMovement = false; - m_InputStateConfigs[(int)InputState.Rotation].m_AllowMovement = false; - m_InputStateConfigs[(int)InputState.ControllerLock].m_AllowMovement = false; - m_InputStateConfigs[(int)InputState.PushPull].m_AllowMovement = false; - m_InputStateConfigs[(int)InputState.BrushSize].m_AllowMovement = false; - - m_InputStateConfigs[(int)InputState.Pan].m_ShowGizmo = true; - m_InputStateConfigs[(int)InputState.Rotation].m_ShowGizmo = true; - m_InputStateConfigs[(int)InputState.HeadLock].m_ShowGizmo = true; - m_InputStateConfigs[(int)InputState.PushPull].m_ShowGizmo = true; - - m_CurrentGazeRay = new Ray(Vector3.zero, Vector3.forward); - m_GazeControllerRay = new Ray(Vector3.zero, Vector3.forward); - m_GazeControllerRayActivePanel = new Ray(Vector3.zero, Vector3.forward); - - m_GrabWidgetHoldHistory = new Queue(); - m_GrabWidgetOneHandInfo = new GrabWidgetControllerInfo(); - - // Initialize world grip members. - m_GrabBrush.grabTransform = TrTransform.identity; - m_GrabWand.grabTransform = TrTransform.identity; - - m_BrushResults = new Queue(); - m_WandResults = new Queue(); - m_WidgetGpuIntersectionLayer = LayerMask.NameToLayer("GpuIntersection"); - m_CurrentGrabIntersectionState = GrabIntersectionState.RequestIntersections; - } - - public void InitGazePanels() - { - // Find all gaze panels. - int iNumGazePanels = m_PanelManager.GetAllPanels().Count; - m_GazeResults = new GazeResult[iNumGazePanels]; - for (int i = 0; i < iNumGazePanels; ++i) - { - m_GazeResults[i] = new GazeResult(); - m_GazeResults[i].m_HitWithGaze = false; - m_GazeResults[i].m_HitWithController = false; - m_GazeResults[i].m_WithinView = false; - m_GazeResults[i].m_GazePosition = new Vector3(); - } - } - - public void OnEnable() - { - // This needs to run before other tools initialize, which is why it's running in OnEnable. - // The sequence is Awake(), OnEnable(), Start(). - if (App.VrSdk.GetControllerDof() == VrSdk.DoF.Six) - { - SetInFreePaintMode(true); - SetInitialTool(BaseTool.ToolType.FreePaintTool); - } - } - - void Start() - { - m_TransformGizmo = (GameObject)Instantiate(m_TransformGizmoPrefab); - m_TransformGizmo.transform.parent = transform; - m_TransformGizmoScript = m_TransformGizmo.GetComponent(); - m_TransformGizmo.SetActive(false); - - m_RotationIcon = (GameObject)Instantiate(m_RotationIconPrefab); - m_RotationIcon.transform.position = m_SketchSurface.transform.position; - m_RotationIcon.transform.parent = m_SketchSurface.transform; - m_RotationIcon.SetActive(false); - - GameObject pinCushionObj = (GameObject)Instantiate(m_PinCushionPrefab); - m_PinCushion = pinCushionObj.GetComponent(); - - m_PositionOffsetResetTapTime = 0.0f; - - m_UndoHold_Timer = m_UndoRedoHold_DurationBeforeStart; - m_RedoHold_Timer = m_UndoRedoHold_DurationBeforeStart; - - m_AutoOrientAfterRotation = true; - m_RotationCursor.gameObject.SetActive(false); - - ResetGrabbedPose(); - m_SketchOrigin = m_SketchSurface.transform.position; - - m_PanelManager.InitPanels(m_ControlsType == ControlsType.SixDofControllers); - - m_UIReticleMobile.SetActive(m_ControlsType == ControlsType.ViewingOnly); - m_UIReticleSixDofController.SetActive(m_ControlsType != ControlsType.ViewingOnly); - - m_PositioningPanelWithHead = false; - m_PositioningSpeed = 16.0f; - - m_CurrentRotationType = RotationType.All; - m_RotationResetTapTime = 0.0f; - - m_CurrentInputState = InputState.Standard; - - m_SketchSurfacePanel.EnableSpecificTool(m_InitialTool); - m_SurfaceLockControllerBaseScalar = m_SketchSurfacePanel.m_PanelSensitivity; - - //after initializing, start with gaze objects hidden - m_CurrentGazeObject = -1; - m_EatInputGazeObject = false; - - // Previously set to 0 in experimental builds - int hidePanelsDelay = 1; - - StartCoroutine(DelayedHidePanels(hidePanelsDelay)); - - m_DropCam.Show(false); - - m_GrabWidgetState = GrabWidgetState.None; - - UpdateDraftingVisibility(); - - m_DisableWorldGrabbing = false; - } - - private IEnumerator DelayedHidePanels(int frames) - { - int stall = frames; - while (stall-- > 0) - { - yield return null; - } - - m_PanelManager.HidePanelsForStartup(); - RequestPanelsVisibility(false); - } - - void Update() - { - // TODO: we need to figure out what transform to pass in here! - // Maybe best _just for now_ to use the scene transform? - TrTransform scenePose = App.Scene.Pose; - Shader.SetGlobalMatrix("xf_CS", scenePose.ToMatrix4x4()); - Shader.SetGlobalMatrix("xf_I_CS", scenePose.inverse.ToMatrix4x4()); - } - - void LateUpdate() - { - // Gracefully exits if we're not recording a video. - VideoRecorderUtils.SerializerNewUsdFrame(); - } - - public bool IsFreepaintToolReady() - { - return - !m_PinCushion.IsShowing() && - !PointerManager.m_Instance.IsStraightEdgeProxyActive() && - !InputManager.m_Instance.ControllersAreSwapping() && - (m_SketchSurfacePanel.IsSketchSurfaceToolActive() || - (m_SketchSurfacePanel.GetCurrentToolType() == BaseTool.ToolType.FreePaintTool)) - ; - } - - public void UpdateControls() - { - UnityEngine.Profiling.Profiler.BeginSample("SketchControlsScript.UpdateControls"); - m_SketchSurfacePanel.m_UpdatedToolThisFrame = false; - - // Verify controllers are available and prune state if they're not. - if ((App.VrSdk.GetControllerDof() == VrSdk.DoF.Six && - App.VrSdk.IsInitializingUnityXR) && App.VrSdk.IsHmdInitialized()) - { - m_PanelManager.SetVisible(false); - PointerManager.m_Instance.RequestPointerRendering(false); - return; - } - - //mouse movement - Vector2 mv = InputManager.m_Instance.GetMouseMoveDelta(); - m_MouseDeltaX = mv.x; - m_MouseDeltaY = mv.y; - - UpdateGazeObjectsAnimationState(); - UpdateCurrentGazeRay(); - m_SketchSurfacePanel.SetBacksideActive(m_CurrentGazeRay.origin); - m_PanelManager.UpdatePanels(); - - m_MouseDeltaXScaled = m_MouseDeltaX * GetAppropriateMovementScalar(); - m_MouseDeltaYScaled = m_MouseDeltaY * GetAppropriateMovementScalar(); - - //this is used for one-shot inputs that don't require state and do not change state - UpdateBaseInput(); - - UpdatePinCushionVisibility(); - - //if the pointer manager is processing, we don't want to respond to input - if (!PointerManager.m_Instance.IsMainPointerProcessingLine()) - { - - //see if we're grabbing a widget - UpdateGrab(); - - //see if we're looking at a gaze object - RefreshCurrentGazeObject(); - - // Tools allowed when widgets aren't grabbed. - bool bWidgetGrabOK = m_GrabWidgetState == GrabWidgetState.None; - - // If we don't have a widget held and we're not grabbing the world with the brush controller, - // update tools. - if (bWidgetGrabOK && !m_GrabBrush.grabbingWorld) - { - if (m_CurrentGazeObject != -1 && !m_WorldBeingGrabbed) - { - UpdateActiveGazeObject(); - - // Allow for standard input (like Undo / Redo) even when gazing at a panel. - if (m_CurrentInputState == InputState.Standard) - { - UpdateStandardInput(); - } - } - else - { - //standard input, no gaze object - if (m_InputStateConfigs[(int)m_CurrentInputState].m_AllowMovement) - { - m_SketchSurfacePanel.UpdateReticleOffset(m_MouseDeltaX, m_MouseDeltaY); - } - - switch (m_CurrentInputState) - { - case InputState.Standard: - UpdateStandardInput(); - break; - case InputState.Pan: - UpdatePanInput(); - break; - case InputState.Rotation: - UpdateRotationInput(); - break; - case InputState.HeadLock: - UpdateHeadLockInput(); - break; - case InputState.ControllerLock: - UpdateControllerLock(); - break; - case InputState.PushPull: - UpdatePushPullInput(); - break; - case InputState.Save: - UpdateSaveInput(); - break; - case InputState.Load: - UpdateLoadInput(); - break; - } - - //keep pointer locked in the right spot, even if it's hidden - if (m_SketchSurfacePanel.ActiveTool.LockPointerToSketchSurface()) - { - Vector3 vPointerPos = Vector3.zero; - Vector3 vPointerForward = Vector3.zero; - m_SketchSurfacePanel.GetReticleTransform(out vPointerPos, out vPointerForward, - (m_ControlsType == ControlsType.ViewingOnly)); - PointerManager.m_Instance.SetMainPointerPosition(vPointerPos); - PointerManager.m_Instance.SetMainPointerForward(vPointerForward); - } - - m_SketchSurfacePanel.AllowDrawing(m_InputStateConfigs[(int)m_CurrentInputState].m_AllowDrawing); - m_SketchSurfacePanel.UpdateCurrentTool(); - - PointerManager.m_Instance.AllowPointerPreviewLine(IsFreepaintToolReady()); - //keep transform gizmo at sketch surface pos - m_TransformGizmo.transform.position = m_SketchSurface.transform.position; - bool bGizmoActive = m_InputStateConfigs[(int)m_CurrentInputState].m_ShowGizmo && m_SketchSurfacePanel.ShouldShowTransformGizmo(); - m_TransformGizmo.SetActive(bGizmoActive); - } - } - } - - // Update any transition to a scene transform reset. - UpdateWorldTransformReset(); - - //update our line after all input and tools have chimed in on the state of it - PointerManager.m_Instance.UpdateLine(); - UnityEngine.Profiling.Profiler.EndSample(); - } - - public void UpdateControlsPostIntro() - { - m_PanelManager.UpdatePanels(); - UpdateCurrentGazeRay(); - UpdateGazeObjectsAnimationState(); - RefreshCurrentGazeObject(); - UpdateSwapControllers(); - if (m_CurrentGazeObject > -1) - { - UpdateActiveGazeObject(); - } - } - - public void UpdateControlsForLoading() - { - UpdateCurrentGazeRay(); - m_PanelManager.UpdatePanels(); - UpdateGazeObjectsAnimationState(); - UpdateGrab(); - UpdateWorldTransformReset(); - - if (m_GrabWidgetState == GrabWidgetState.None && m_CurrentGazeObject == -1 && - m_SketchSurfacePanel.ActiveTool.AvailableDuringLoading() && - !m_GrabBrush.grabbingWorld) - { - m_SketchSurfacePanel.UpdateCurrentTool(); - } - } - - public void UpdateControlsForReset() - { - UpdateGrab(); - UpdateCurrentGazeRay(); - UpdatePinCushionVisibility(); - m_PanelManager.UpdatePanels(); - UpdateGazeObjectsAnimationState(); - PointerManager.m_Instance.UpdateLine(); - } - - public void UpdateControlsForUploading() - { - UpdateCurrentGazeRay(); - UpdatePinCushionVisibility(); - m_PanelManager.UpdatePanels(); - UpdateGazeObjectsAnimationState(); - } - - public void UpdateControlsForMemoryExceeded() - { - UpdateGrab(); - m_SketchSurfacePanel.m_UpdatedToolThisFrame = false; - m_PanelManager.UpdatePanels(); - UpdateCurrentGazeRay(); - UpdateGazeObjectsAnimationState(); - RefreshCurrentGazeObject(); - if (m_CurrentGazeObject > -1) - { - UpdateActiveGazeObject(); - } - } - - void UpdatePinCushionVisibility() - { - // If the pin cushion is showing and the user cancels, eat the input. - // if (m_PinCushion.IsShowing()) - // { - // if (InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Activate) || - // InputManager.Brush.GetControllerGrip() || - // InputManager.Wand.GetControllerGrip() || - // IsUserInteractingWithAnyWidget() || - // IsUserInteractingWithUI()) - // { - // m_EatPinCushionInput = true; - // } - // } - - // If our tool wants the input blocked, maintain the input eat state until - // after the user has let off input. - if (m_SketchSurfacePanel.ActiveTool.BlockPinCushion() || !CanUsePinCushion()) - { - m_EatPinCushionInput = true; - } - - bool show = - InputManager.m_Instance.GetCommand(InputManager.SketchCommands.ShowPinCushion); - m_PinCushion.ShowPinCushion(show && !m_EatPinCushionInput); - m_EatPinCushionInput = m_EatPinCushionInput && show; - } - - bool CanUsePinCushion() - { - return (m_ControlsType == ControlsType.SixDofControllers) && - m_PanelManager.AdvancedModeActive() && - !InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Activate) && - !InputManager.Brush.GetControllerGrip() && - !InputManager.Wand.GetControllerGrip() && - !IsUserInteractingWithAnyWidget() && - !IsUserInteractingWithUI() && - !m_SketchSurfacePanel.ActiveTool.BlockPinCushion() && - App.Instance.IsInStateThatAllowsPainting(); - } - - void UpdateCurrentGazeRay() - { - var head = ViewpointScript.Head; - m_CurrentGazeRay = new Ray(head.position, head.forward); - m_CurrentHeadOrientation = head.rotation; - - // We use the gaze ray for certain shader effects - like edge falloff. - Shader.SetGlobalVector("_WorldSpaceRootCameraPosition", m_CurrentGazeRay.origin); - bool hasController = m_ControlsType == ControlsType.SixDofControllers; - if (hasController) - { - if (InputManager.Brush.IsTrackedObjectValid) - { - Transform rAttachPoint = InputManager.m_Instance.GetBrushControllerAttachPoint(); - m_GazeControllerRay.direction = rAttachPoint.forward; - m_GazeControllerRay.origin = rAttachPoint.position; - } - else - { - // If the brush controller isn't tracked, put our controller ray out of the way. - float fBig = 9999999.0f; - m_GazeControllerRay.direction = Vector3.one; - m_GazeControllerRay.origin = new Vector3(fBig, fBig, fBig); - } - - m_GazeControllerRayActivePanel.direction = m_GazeControllerRay.direction; - m_GazeControllerRayActivePanel.origin = m_GazeControllerRay.origin; - m_GazeControllerRayActivePanel.origin -= (m_GazeControllerRayActivePanel.direction * 0.5f); - } - } - - public void UpdateGazeObjectsAnimationState() - { - // Are the panels allowed to be visible? - bool isSixDof = m_ControlsType == ControlsType.SixDofControllers; - if ((!isSixDof) || - (InputManager.Wand.IsTrackedObjectValid && - !m_SketchSurfacePanel.ActiveTool.HidePanels() && - !App.Instance.IsLoading())) - { - // Transition panels according to requested visibility. - m_PanelManager.SetVisible(m_PanelsVisibilityRequested); - } - else - { - // Transition out. - m_PanelManager.SetVisible(false); - } - } - - void UpdateBaseInput() - { - UnityEngine.Profiling.Profiler.BeginSample("SketchControlScript.UpdateBaseInput"); - if (m_ControlsType == ControlsType.SixDofControllers) - { - m_PanelManager.UpdateWandOrientationControls(); - } - - //allow tool scaling if we're not drawing and our input device is active - bool bScaleInputActive = InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Scale); - bool bScaleCommandActive = - bScaleInputActive - && !InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Activate) - && m_GrabBrush.grabbingWorld == false - && m_CurrentGazeObject == -1 // free up swipe for use by gaze object - && (m_ControlsType != ControlsType.SixDofControllers || InputManager.Brush.IsTrackedObjectValid) // TODO:Mikesky - very hacky - && SketchSurfacePanel.m_Instance.ActiveTool.m_Type != BaseTool.ToolType.MultiCamTool; - - if (m_EatToolScaleInput) - { - m_EatToolScaleInput = bScaleInputActive; - } - - if (bScaleCommandActive && !m_EatToolScaleInput) - { - if (m_GrabWidgetState == GrabWidgetState.None) - { - //send scale command down to current tool - m_SketchSurfacePanel.UpdateToolSize( - m_AdjustToolSizeScalar * InputManager.m_Instance.GetAdjustedBrushScrollAmount()); - } - - //ugly, but brush size is becoming not an input state - m_MouseDeltaX = 0.0f; - m_MouseDeltaY = 0.0f; - } - - UpdateSwapControllers(); - UnityEngine.Profiling.Profiler.EndSample(); - } - - void UpdateSwapControllers() - { - // Don't allow controller swap in first run intro. - // Don't allow controller swap if we're grabbing a widget. - // Don't allow controller swap if a Logitech pen is present. - if (!TutorialManager.m_Instance.TutorialActive() && - m_GrabWidgetState == GrabWidgetState.None && - !App.VrSdk.VrControls.LogitechPenIsPresent()) - { - if (InputManager.m_Instance.GetCommandDown(InputManager.SketchCommands.SwapControls)) - { - DoSwapControls(); - } - } - } - - public static void DoSwapControls() - { - InputManager.m_Instance.WandOnRight = !InputManager.m_Instance.WandOnRight; - InputManager.m_Instance.GetControllerBehavior(InputManager.ControllerName.Brush) - .DisplayControllerSwapAnimation(); - InputManager.m_Instance.GetControllerBehavior(InputManager.ControllerName.Wand) - .DisplayControllerSwapAnimation(); - AudioManager.m_Instance.PlayControllerSwapSound( - InputManager.m_Instance.GetControllerPosition(InputManager.ControllerName.Brush)); - } - - void UpdateStandardInput() - { - UnityEngine.Profiling.Profiler.BeginSample("SketchControlScript.UpdateStandardInput"); - //debug keys - if (App.UserConfig.Flags.AdvancedKeyboardShortcuts) - { - var camTool = SketchSurfacePanel.m_Instance.ActiveTool as MultiCamTool; - - if (InputManager.m_Instance.GetKeyboardShortcutDown(InputManager.KeyboardShortcut.SaveNew)) - { - IssueGlobalCommand(GlobalCommands.SaveNew, 1); - } - else if (InputManager.m_Instance.GetKeyboardShortcutDown( - InputManager.KeyboardShortcut.SwitchCamera) && camTool != null) - { - camTool.ExternalObjectNextCameraStyle(); // For monoscopic mode - } - else if (InputManager.m_Instance.GetKeyboardShortcutDown( - InputManager.KeyboardShortcut.ViewOnly)) - { - IssueGlobalCommand(GlobalCommands.ViewOnly); - } - else if (InputManager.m_Instance.GetKeyboardShortcutDown( - InputManager.KeyboardShortcut.ToggleScreenMirroring)) - { - ViewpointScript.m_Instance.ToggleScreenMirroring(); - } - else if (InputManager.m_Instance.GetKeyboardShortcutDown( - InputManager.KeyboardShortcut.PreviousTool)) - { - m_SketchSurfacePanel.PreviousTool(); - } - else if (InputManager.m_Instance.GetKeyboardShortcutDown( - InputManager.KeyboardShortcut.NextTool)) - { - m_SketchSurfacePanel.NextTool(); - } - else if (InputManager.m_Instance.GetKeyboardShortcutDown( - InputManager.KeyboardShortcut.CycleSymmetryMode)) - { - var cur = PointerManager.m_Instance.CurrentSymmetryMode; - var next = (cur == SymmetryMode.None) ? SymmetryMode.SinglePlane - : (cur == SymmetryMode.SinglePlane) ? SymmetryMode.DebugMultiple - : (cur == SymmetryMode.DebugMultiple) ? SymmetryMode.MultiMirror - : (cur == SymmetryMode.MultiMirror) ? SymmetryMode.TwoHanded - : SymmetryMode.None; - PointerManager.m_Instance.CurrentSymmetryMode = next; - } - else if (InputManager.m_Instance.GetKeyboardShortcutDown( - InputManager.KeyboardShortcut.Export)) - { - StartCoroutine(ExportCoroutine()); - } - else if (InputManager.m_Instance.GetKeyboardShortcutDown( - InputManager.KeyboardShortcut.StoreHeadTransform) && - InputManager.m_Instance.GetAnyShift()) - { - Transform head = ViewpointScript.Head; - PlayerPrefs.SetFloat("HeadOffset_localPositionX", head.localPosition.x); - PlayerPrefs.SetFloat("HeadOffset_localPositionY", head.localPosition.y); - PlayerPrefs.SetFloat("HeadOffset_localPositionZ", head.localPosition.z); - PlayerPrefs.SetFloat("HeadOffset_localRotationX", head.localRotation.x); - PlayerPrefs.SetFloat("HeadOffset_localRotationY", head.localRotation.y); - PlayerPrefs.SetFloat("HeadOffset_localRotationZ", head.localRotation.z); - PlayerPrefs.SetFloat("HeadOffset_localRotationW", head.localRotation.w); - } - else if (InputManager.m_Instance.GetKeyboardShortcutDown( - InputManager.KeyboardShortcut.RecallHeadTransform)) - { - Transform head = ViewpointScript.Head; - // Toggle the head offset. - if (m_HeadOffset) - { - // Remove the offset. - Transform originalParent = head.parent; - head.SetParent(head.parent.parent); - GameObject.DestroyImmediate(originalParent.gameObject); - m_HeadOffset = false; - } - else - { - // Add the offset. - GameObject newParent = new GameObject(); - newParent.transform.SetParent(head.parent); - newParent.transform.localPosition = Vector3.zero; - newParent.transform.localRotation = Quaternion.identity; - newParent.transform.localScale = Vector3.one; - head.SetParent(newParent.transform); - TrTransform offsetTransform = TrTransform.TR( - new Vector3( - PlayerPrefs.GetFloat("HeadOffset_localPositionX", 0), - PlayerPrefs.GetFloat("HeadOffset_localPositionY", 1.5f), - PlayerPrefs.GetFloat("HeadOffset_localPositionZ", 0)), - new Quaternion( - PlayerPrefs.GetFloat("HeadOffset_localRotationX", 0), - PlayerPrefs.GetFloat("HeadOffset_localRotationY", 0), - PlayerPrefs.GetFloat("HeadOffset_localRotationZ", 0), - PlayerPrefs.GetFloat("HeadOffset_localRotationW", 1))); - TrTransform originalTransformInverse = TrTransform.FromLocalTransform(head).inverse; - TrTransform newParentTransform = offsetTransform * originalTransformInverse; - newParent.transform.localPosition = newParentTransform.translation; - newParent.transform.localRotation = newParentTransform.rotation; - m_HeadOffset = true; - } - } - else if (InputManager.m_Instance.GetKeyboardShortcutDown( - InputManager.KeyboardShortcut.ToggleLightType)) - { - // Toggle between per-pixel & SH lighting on the secondary directional light - Light secondaryLight = App.Scene.GetLight((1)); - if (LightRenderMode.ForceVertex == secondaryLight.renderMode) - { - secondaryLight.renderMode = LightRenderMode.ForcePixel; - } - else - { - secondaryLight.renderMode = LightRenderMode.ForceVertex; - } - } - else if (InputManager.m_Instance.GetKeyboardShortcutDown( - InputManager.KeyboardShortcut.TossWidget)) - { - m_WidgetManager.TossNearestWidget(); - } - else if (InputManager.m_Instance.GetKeyboardShortcutDown( - InputManager.KeyboardShortcut.Reset)) - { - App.Instance.SetDesiredState(App.AppState.LoadingBrushesAndLighting); - } - else if (InputManager.m_Instance.GetKeyboardShortcutDown( - InputManager.KeyboardShortcut.FlyMode)) - { - SketchSurfacePanel.m_Instance.EnableSpecificTool(BaseTool.ToolType.FlyTool); - } - else if (App.Config.m_ToggleProfileOnAppButton && - (InputManager.Wand.GetVrInputDown(VrInput.Button03) || - InputManager.m_Instance.GetKeyboardShortcutDown( - InputManager.KeyboardShortcut.ToggleProfile))) - { - IssueGlobalCommand(GlobalCommands.ToggleProfiling); - } - } - -#if DEBUG - if (InputManager.m_Instance.GetKeyboardShortcutDown( - InputManager.KeyboardShortcut.CheckStrokes)) - { - bool value = !SketchMemoryScript.m_Instance.m_SanityCheckStrokes; - string feature = "Stroke determinism checking"; - SketchMemoryScript.m_Instance.m_SanityCheckStrokes = value; - OutputWindowScript.m_Instance.CreateInfoCardAtController( - InputManager.ControllerName.Brush, - feature + (value ? ": On" : ": Off")); - } -#endif - - bool hasController = m_ControlsType == ControlsType.SixDofControllers; - var mouse = Mouse.current; - - // Toggle default tool. - if (!m_PanelManager.AdvancedModeActive() && - InputManager.m_Instance.GetCommandDown(InputManager.SketchCommands.ToggleDefaultTool) && - !m_SketchSurfacePanel.IsDefaultToolEnabled() && - m_SketchSurfacePanel.ActiveTool.AllowDefaultToolToggle() && m_CurrentGazeObject == -1)// don't allow tool to change while pointing at panel because there is no visual indication - { - m_SketchSurfacePanel.EnableDefaultTool(); - AudioManager.m_Instance.PlayPinCushionSound(true); - } - // Pan. - else if (!hasController && mouse.rightButton.isPressed) - { - SwitchState(InputState.Pan); - } - // Controller lock (this must be before rotate/head lock!). - else if (!hasController && - InputManager.m_Instance.GetCommand(InputManager.SketchCommands.LockToController)) - { - SwitchState(InputState.ControllerLock); - } - // Rotate. - else if (!hasController && - InputManager.m_Instance.GetCommand(InputManager.SketchCommands.PivotRotation)) - { - SwitchState(InputState.Rotation); - } - // Head lock. - else if (!hasController && - InputManager.m_Instance.GetCommand(InputManager.SketchCommands.LockToHead)) - { - SwitchState(InputState.HeadLock); - } - // Push pull. - else if (!hasController && - InputManager.m_Instance.GetCommand(InputManager.SketchCommands.AltActivate)) - { - SwitchState(InputState.PushPull); - } - else if (!PointerManager.m_Instance.IsMainPointerCreatingStroke()) - { - // Reset surface. - if (!hasController && - InputManager.m_Instance.GetCommandDown(InputManager.SketchCommands.Reset)) - { - ResetGrabbedPose(); - } - // Undo. - else if (InputManager.m_Instance.GetCommandDown(InputManager.SketchCommands.Undo) && - CanUndo()) - { - IssueGlobalCommand(GlobalCommands.Undo); - } - else if (InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Undo) && - CanUndo() && ShouldRepeatUndo()) - { - m_UndoHold_Timer = m_UndoRedoHold_RepeatInterval; - IssueGlobalCommand(GlobalCommands.Undo); - } - // Redo. - else if (InputManager.m_Instance.GetCommandDown(InputManager.SketchCommands.Redo) && - CanRedo()) - { - IssueGlobalCommand(GlobalCommands.Redo); - } - else if (InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Redo) && - CanRedo() && ShouldRepeatRedo()) - { - m_RedoHold_Timer = m_UndoRedoHold_RepeatInterval; - IssueGlobalCommand(GlobalCommands.Redo); - } - // Reset scene. - else if (!hasController && - InputManager.m_Instance.GetKeyboardShortcutDown( - InputManager.KeyboardShortcut.ResetScene)) - { - // TODO: Should thsi go away? Seems like the "sweetspot" may no longer be used. - if (App.VrSdk.GetControllerDof() == VrSdk.DoF.Two) - { - m_PanelManager.SetSweetSpotPosition(m_CurrentGazeRay.origin); - ResetGrabbedPose(); - } - } - // Straight edge. - else if (!hasController && - InputManager.m_Instance.GetKeyboardShortcutDown( - InputManager.KeyboardShortcut.StraightEdge)) - { - IssueGlobalCommand(GlobalCommands.StraightEdge); - } - // Always fall back on switching tools. - else - { - m_SketchSurfacePanel.CheckForToolSelection(); - } - } - - // Reset undo/redo hold timers. - if (!InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Undo)) - { - m_UndoHold_Timer = m_UndoRedoHold_DurationBeforeStart; - } - if (!InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Redo)) - { - m_RedoHold_Timer = m_UndoRedoHold_DurationBeforeStart; - } - UnityEngine.Profiling.Profiler.EndSample(); - } - - bool CanUndo() - { - return SketchMemoryScript.m_Instance.CanUndo() && - !IsUIBlockingUndoRedo() && - m_PanelManager.GazePanelsAreVisible() && - !m_GrabWand.grabbingWorld && - !InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Activate) && - !SelectionManager.m_Instance.IsAnimatingTossFromGrabbingGroup; - } - - bool CanRedo() - { - return SketchMemoryScript.m_Instance.CanRedo() && - !IsUIBlockingUndoRedo() && - m_PanelManager.GazePanelsAreVisible() && - !m_GrabBrush.grabbingWorld && - !InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Activate) && - !SelectionManager.m_Instance.IsAnimatingTossFromGrabbingGroup; - } - - bool ShouldRepeatUndo() - { - m_UndoHold_Timer -= Time.deltaTime; - return (m_UndoHold_Timer <= 0.0f); - } - - bool ShouldRepeatRedo() - { - m_RedoHold_Timer -= Time.deltaTime; - return (m_RedoHold_Timer <= 0.0f); - } - - // Updates the global state: - // m_CurrentGrabWidget - void UpdateGrab() - { - UnityEngine.Profiling.Profiler.BeginSample("SketchControlScript.UpdateGrab"); - if (m_ControlsType != ControlsType.SixDofControllers) - { - UnityEngine.Profiling.Profiler.EndSample(); - return; - } - - GrabWidget rPrevGrabWidget = m_CurrentGrabWidget; - GrabWidget rPrevPotentialBrush = m_PotentialGrabWidgetBrush; - GrabWidget rPrevPotentialWand = m_PotentialGrabWidgetWand; - if (m_CurrentGrabWidget) - { - m_CurrentGrabWidget.Activate(false); - } - if (m_PotentialGrabWidgetBrush) - { - m_PotentialGrabWidgetBrush.Activate(false); - } - if (m_PotentialGrabWidgetWand) - { - m_PotentialGrabWidgetWand.Activate(false); - } - m_CurrentGrabWidget = null; - m_PotentialGrabWidgetBrush = null; - m_PotentialGrabWidgetWand = null; - m_PotentialGrabWidgetBrushValid = false; - m_PotentialGrabWidgetWandValid = false; - - m_WidgetManager.RefreshNearestWidgetLists(m_CurrentGazeRay, m_CurrentGazeObject); - - if (m_GrabWidgetState == GrabWidgetState.None) - { - UpdateGrab_WasNone(rPrevPotentialBrush, rPrevPotentialWand); - } - else if (m_GrabWidgetState == GrabWidgetState.OneHand) - { - UpdateGrab_WasOneHand(rPrevGrabWidget); - } - else if (m_GrabWidgetState == GrabWidgetState.TwoHands) - { - UpdateGrab_WasTwoHands(rPrevGrabWidget); - } - - // Update grab intersection state. - switch (m_CurrentGrabIntersectionState) - { - case GrabIntersectionState.RequestIntersections: - m_CurrentGrabIntersectionState = GrabIntersectionState.ReadBrush; - break; - case GrabIntersectionState.ReadBrush: - m_CurrentGrabIntersectionState = GrabIntersectionState.ReadWand; - break; - case GrabIntersectionState.ReadWand: - m_CurrentGrabIntersectionState = GrabIntersectionState.RequestIntersections; - break; - } - - if (!TutorialManager.m_Instance.TutorialActive() && m_CurrentGrabWidget == null) - { - UpdateGrab_World(); - } - - App.Instance.SelectionEffect.HighlightForGrab( - m_GrabWidgetState != GrabWidgetState.None || - (m_PotentialGrabWidgetBrush != null && m_PotentialGrabWidgetBrushValid) || - (m_PotentialGrabWidgetWand != null && m_PotentialGrabWidgetWandValid)); - UnityEngine.Profiling.Profiler.EndSample(); - } - - void UpdateGrab_WasNone(GrabWidget rPrevPotentialBrush, GrabWidget rPrevPotentialWand) - { - // if a panel isn't in focus, allow for widget grab - // We can grab a widget as long as we aren't trying to draw with that hand. - bool bActiveInput = - (InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Activate) && - App.Instance.IsInStateThatAllowsPainting()); - - //certain tools don't allow us to mess with widgets - bool bWidgetManipOK = m_SketchSurfacePanel.DoesCurrentToolAllowWidgetManipulation() && - !m_GrabWand.grabbingWorld && !m_GrabBrush.grabbingWorld && IsGrabWorldStateStable() && - App.Instance.IsInStateThatAllowsAnyGrabbing(); - - // Update EatInput flags if they're valid. - if (m_GrabBrush.eatInput) - { - m_GrabBrush.eatInput = InputManager.Brush.GetControllerGrip(); - } - if (m_GrabWand.eatInput) - { - m_GrabWand.eatInput = InputManager.Wand.GetControllerGrip(); - } - - bool bShouldClearWandInside = false; - if (m_CurrentInputState == InputState.Standard && bWidgetManipOK) - { - // If we're in the intersection request state, fire off a new intersection request. If - // we're in the read brush state, update our brush grab data structure. - List brushBests = m_WidgetManager.WidgetsNearBrush; - if (m_CurrentGrabIntersectionState == GrabIntersectionState.RequestIntersections) - { - RequestWidgetIntersection(brushBests, InputManager.ControllerName.Brush); - } - else if (m_CurrentGrabIntersectionState == GrabIntersectionState.ReadBrush) - { - m_BackupBrushGrabData = GetBestWidget(brushBests, m_BrushResults); - } - - if (m_BackupBrushGrabData != null) - { - m_PotentialGrabWidgetBrush = m_BackupBrushGrabData.m_WidgetScript; - - // Allow widget grab if we're not painting. - if (!bActiveInput) - { - m_PotentialGrabWidgetBrush.Activate(true); - m_PotentialGrabWidgetBrushValid = true; - m_PotentialGrabWidgetBrush.VisualizePinState(); - - if (!m_GrabBrush.eatInput && InputManager.Brush.GetControllerGrip()) - { - m_CurrentGrabWidget = m_PotentialGrabWidgetBrush; - if (m_CurrentGrabWidget.Group != SketchGroupTag.None) - { - m_GrabBrush.grabbingGroup = true; - m_CurrentGrabWidget = - SelectionManager.m_Instance.StartGrabbingGroupWithWidget(m_CurrentGrabWidget); - } - UpdateGrab_NoneToOne(InputManager.ControllerName.Brush); - bShouldClearWandInside = true; - m_GrabBrush.startedGrabInsideWidget = true; - } - } - } - m_GrabBrush.SetHadBestGrabAndTriggerHaptics(m_BackupBrushGrabData); - m_ControllerGrabVisuals.BrushInWidgetRange = m_BackupBrushGrabData != null; - - // If we're in the intersection request state, fire off a new intersection request. If - // we're in the read wand state, update our wand grab data structure. - List wandBests = m_WidgetManager.WidgetsNearWand; - if (m_CurrentGrabIntersectionState == GrabIntersectionState.RequestIntersections) - { - RequestWidgetIntersection(wandBests, InputManager.ControllerName.Wand); - } - else if (m_CurrentGrabIntersectionState == GrabIntersectionState.ReadWand) - { - m_BackupWandGrabData = GetBestWidget(wandBests, m_WandResults); - } - - if (m_BackupWandGrabData != null) - { - m_PotentialGrabWidgetWand = m_BackupWandGrabData.m_WidgetScript; - // Allow wand widget grab if brush grab failed. - bool bGrabAllowed = (m_GrabWidgetState == GrabWidgetState.None) && !bActiveInput; - if (bGrabAllowed) - { - m_PotentialGrabWidgetWand.Activate(true); - m_PotentialGrabWidgetWandValid = true; - m_PotentialGrabWidgetWand.VisualizePinState(); - - if (!m_GrabWand.eatInput && InputManager.Wand.GetControllerGrip()) - { - m_CurrentGrabWidget = m_PotentialGrabWidgetWand; - if (m_CurrentGrabWidget.Group != SketchGroupTag.None) - { - m_GrabWand.grabbingGroup = true; - m_CurrentGrabWidget = - SelectionManager.m_Instance.StartGrabbingGroupWithWidget(m_CurrentGrabWidget); - } - UpdateGrab_NoneToOne(InputManager.ControllerName.Wand); - m_GrabBrush.ClearInsideWidget(); - m_GrabWand.startedGrabInsideWidget = true; - } - } - } - m_GrabWand.SetHadBestGrabAndTriggerHaptics(m_BackupWandGrabData); - m_ControllerGrabVisuals.WandInWidgetRange = m_BackupWandGrabData != null; - - // Account for asymmetry in controller processing by clearing after wand has updated - // GrabState.insideWidget according to bestWandGrab. - if (bShouldClearWandInside) - { - m_GrabWand.ClearInsideWidget(); - } - } - - // Update widget collisions if we've got a drifter. - if (m_GrabWidgetState == GrabWidgetState.None) - { - if (m_WidgetManager.ShouldUpdateCollisions()) - { - m_PanelManager.DoCollisionSimulationForWidgetPanels(); - } - } - } - - void UpdateGrab_WasOneHand(GrabWidget rPrevGrabWidget) - { - var controller = InputManager.Controllers[(int)m_GrabWidgetOneHandInfo.m_Name]; - bool shouldRelease = !App.Instance.IsInStateThatAllowsAnyGrabbing(); - if (!InputManager.Controllers[(int)m_GrabWidgetOneHandInfo.m_Name].GetControllerGrip() || - shouldRelease) - { - if (shouldRelease) - { - EatGrabInput(); - } - - Vector3 vLinearVelocity; - Vector3 vAngularVelocity; - if (GetGrabWidgetHoldHistory(out vLinearVelocity, out vAngularVelocity)) - { - rPrevGrabWidget.SetVelocities( - vLinearVelocity, vAngularVelocity, - controller.Transform.position); - } - // One -> None - UpdateGrab_ToNone(rPrevGrabWidget); - } - else - { - // Keep holding on to our widget. - m_CurrentGrabWidget = rPrevGrabWidget; - m_CurrentGrabWidget.Activate(true); - m_CurrentGrabWidget.UserInteracting(true, m_GrabWidgetOneHandInfo.m_Name); - - if (!m_CurrentGrabWidget.Pinned) - { - var info = InputManager.Controllers[(int)m_GrabWidgetOneHandInfo.m_Name]; - var controllerXf = Coords.AsGlobal[info.Transform]; - var newWidgetXf = controllerXf * m_GrabWidgetOneHandInfo.m_BaseWidgetXf_LS; - m_CurrentGrabWidget.RecordAndSetPosRot(newWidgetXf); - - UpdateGrabWidgetHoldHistory(m_GrabWidgetOneHandInfo.m_Name); - } - - m_PanelManager.DoCollisionSimulationForWidgetPanels(); - - // Check for widget pinning. - if (m_CurrentGrabWidget.AllowPinning) - { - if (InputManager.Controllers[(int)m_GrabWidgetOneHandInfo.m_Name].GetCommandDown( - InputManager.SketchCommands.PinWidget)) - { - // If the user initiates a pin action, buzz a bit. - if (!m_CurrentGrabWidget.Pinned) - { - InputManager.m_Instance.TriggerHapticsPulse( - m_GrabWidgetOneHandInfo.m_Name, 3, 0.10f, 0.07f); - } - m_CurrentGrabWidget.Pin(!m_CurrentGrabWidget.Pinned); - SketchSurfacePanel.m_Instance.EatToolsInput(); - m_WidgetManager.RefreshPinAndUnpinLists(); - } - } - - if (m_CurrentGrabWidget is SelectionWidget) - { - if (InputManager.m_Instance.GetCommandDown( - InputManager.SketchCommands.DuplicateSelection)) - { - controller.LastHeldInput = - controller.GetCommandHoldInput(InputManager.SketchCommands.DuplicateSelection); - } - - if (controller.LastHeldInput != null && - InputManager.m_Instance.GetCommandHeld(InputManager.SketchCommands.DuplicateSelection)) - { - SketchControlsScript.m_Instance.IssueGlobalCommand( - SketchControlsScript.GlobalCommands.Duplicate); - } - } - - InputManager.ControllerName otherName = - (m_GrabWidgetOneHandInfo.m_Name == InputManager.ControllerName.Brush) ? - InputManager.ControllerName.Wand : InputManager.ControllerName.Brush; - bool otherInputEaten = - (m_GrabWidgetOneHandInfo.m_Name == InputManager.ControllerName.Brush) ? - m_GrabWand.eatInput : m_GrabBrush.eatInput; - - // See if the other controller decides to grab the widget (unless we're pinned). - if (!m_CurrentGrabWidget.Pinned) - { - if (m_CurrentGrabWidget.AllowTwoHandGrab) - { - if (InputManager.Controllers[(int)otherName].GetControllerGrip()) - { - RequestPanelsVisibility(false); - m_GrabWidgetState = GrabWidgetState.TwoHands; - // Figure out if the new grab starts inside the widget. - Vector3 vOtherGrabPos = TrTransform.FromTransform( - InputManager.m_Instance.GetController(otherName)).translation; - bool bOtherGrabInBounds = m_CurrentGrabWidget.GetActivationScore( - vOtherGrabPos, otherName) >= 0; - m_CurrentGrabWidget.SetUserTwoHandGrabbing( - true, m_GrabWidgetOneHandInfo.m_Name, otherName, bOtherGrabInBounds); - - if (otherName == InputManager.ControllerName.Brush) - { - m_GrabBrush.startedGrabInsideWidget = bOtherGrabInBounds; - } - else - { - m_GrabWand.startedGrabInsideWidget = bOtherGrabInBounds; - } - - m_GrabWidgetTwoHandBrushPrev = TrTransform.FromTransform( - InputManager.m_Instance.GetController(InputManager.ControllerName.Brush)); - m_GrabWidgetTwoHandWandPrev = TrTransform.FromTransform( - InputManager.m_Instance.GetController(InputManager.ControllerName.Wand)); - } - } - } - else if (!otherInputEaten && InputManager.Controllers[(int)otherName].GetControllerGrip()) - { - // If it's a two hand grab but the current grab widget is pinned, grab the world. - UpdateGrab_ToNone(m_CurrentGrabWidget); - m_CurrentGrabWidget = null; - m_ControllerGrabVisuals.SetDesiredVisualState(ControllerGrabVisuals.VisualState.Off); - } - } - } - - // Previous frame was a two-handed grab. - // Handles all the cases where this frame's grab is zero, one, or two hands. - void UpdateGrab_WasTwoHands(GrabWidget rPrevGrabWidget) - { - //keep holding on to our widget - m_CurrentGrabWidget = rPrevGrabWidget; - m_CurrentGrabWidget.Activate(true); - m_CurrentGrabWidget.UserInteracting(true, m_GrabWidgetOneHandInfo.m_Name); - - if (!App.Instance.IsInStateThatAllowsAnyGrabbing()) - { - m_CurrentGrabWidget.SetUserTwoHandGrabbing(false); - UpdateGrab_ToNone(rPrevGrabWidget); - } - else if (!InputManager.Wand.GetControllerGrip()) - { // Look for button release. - m_CurrentGrabWidget.SetUserTwoHandGrabbing(false); - // See if our Brush hand is still within grab range of the widget. - if (m_GrabBrush.startedGrabInsideWidget || - IsControllerNearWidget(InputManager.ControllerName.Brush, m_CurrentGrabWidget)) - { - m_GrabWidgetOneHandInfo.m_Name = InputManager.ControllerName.Brush; - RequestPanelsVisibility(true); - InitializeGrabWidgetControllerInfo(m_GrabWidgetOneHandInfo); - m_GrabWidgetState = GrabWidgetState.OneHand; - } - else - { - // If the Brush hand is beyond the widget, we're not holding it anymore. - UpdateGrab_ToNone(rPrevGrabWidget); - - // Eat input on the brush grip until we release the button. - m_GrabBrush.eatInput = true; - } - } - else if (!InputManager.Brush.GetControllerGrip()) - { - m_CurrentGrabWidget.SetUserTwoHandGrabbing(false); - if (m_GrabWand.startedGrabInsideWidget || - IsControllerNearWidget(InputManager.ControllerName.Wand, m_CurrentGrabWidget)) - { - m_GrabWidgetOneHandInfo.m_Name = InputManager.ControllerName.Wand; - InitializeGrabWidgetControllerInfo(m_GrabWidgetOneHandInfo); - m_GrabWidgetState = GrabWidgetState.OneHand; - } - else - { - UpdateGrab_ToNone(rPrevGrabWidget); - m_GrabWand.eatInput = true; - } - } - else - { - // Both hands still grabbing. - // Check for pin, which forcibly releases one of the hands. - if (m_CurrentGrabWidget.AllowPinning && - InputManager.Controllers[(int)m_GrabWidgetOneHandInfo.m_Name].GetCommandDown( - InputManager.SketchCommands.PinWidget)) - { - // If the user initiates a pin action, buzz a bit. - if (!m_CurrentGrabWidget.Pinned) - { - InputManager.m_Instance.TriggerHapticsPulse( - m_GrabWidgetOneHandInfo.m_Name, 3, 0.10f, 0.07f); - } - - m_CurrentGrabWidget.Pin(!m_CurrentGrabWidget.Pinned); - SketchSurfacePanel.m_Instance.EatToolsInput(); - m_WidgetManager.RefreshPinAndUnpinLists(); - - InitializeGrabWidgetControllerInfo(m_GrabWidgetOneHandInfo); - m_GrabWidgetState = GrabWidgetState.OneHand; - m_CurrentGrabWidget.SetUserTwoHandGrabbing(false); - - // Eat input on the off hand so we don't immediately jump in to world transform. - if (m_GrabWidgetOneHandInfo.m_Name == InputManager.ControllerName.Brush) - { - RequestPanelsVisibility(true); - m_GrabWand.eatInput = true; - } - else - { - m_GrabBrush.eatInput = true; - } - } - - if (!m_CurrentGrabWidget.Pinned) - { - UpdateGrab_ContinuesTwoHands(); - } - } - ClearGrabWidgetHoldHistory(); - m_PanelManager.DoCollisionSimulationForWidgetPanels(); - } - - // Common case for two-handed grab: both the previous and current frames are two-handed. - private void UpdateGrab_ContinuesTwoHands() - { - //holding with two hands, transform accordingly - TrTransform xfBrush = TrTransform.FromTransform(InputManager.Brush.Transform); - TrTransform xfWand = TrTransform.FromTransform(InputManager.Wand.Transform); - Vector2 vSizeRange = m_CurrentGrabWidget.GetWidgetSizeRange(); - - GrabWidget.Axis axis = m_CurrentGrabWidget.GetScaleAxis( - xfWand.translation, xfBrush.translation, - out Vector3 axisDirection, out float axisExtent); - - TrTransform newWidgetXf; - if (axis != GrabWidget.Axis.Invalid) - { - // Scale along a single axis - float deltaScale; - if (App.Config.m_AxisManipulationIsResize) - { - newWidgetXf = MathUtils.TwoPointObjectTransformationAxisResize( - axisDirection, axisExtent, - m_GrabWidgetTwoHandWandPrev, m_GrabWidgetTwoHandBrushPrev, - xfWand, xfBrush, - GetWorkingTransform(m_CurrentGrabWidget), - out deltaScale, - deltaScaleMin: vSizeRange.x / axisExtent, - deltaScaleMax: vSizeRange.y / axisExtent); - } - else - { - newWidgetXf = MathUtils.TwoPointObjectTransformationNonUniformScale( - axisDirection, - m_GrabWidgetTwoHandWandPrev, m_GrabWidgetTwoHandBrushPrev, - xfWand, xfBrush, - GetWorkingTransform(m_CurrentGrabWidget), - out deltaScale, - finalScaleMin: vSizeRange.x, - deltaScaleMin: vSizeRange.x / axisExtent, - deltaScaleMax: vSizeRange.y / axisExtent); - } - - // The above functions return undefined values in newWidgetXf.scale; but that's - // okay because RecordAndSetPosRot ignores xf.scale. - // TODO: do this more cleanly - m_CurrentGrabWidget.RecordAndApplyScaleToAxis(deltaScale, axis); - } - else - { - // Uniform scaling - TrTransform xfObject = GetWorkingTransform(m_CurrentGrabWidget); - Vector3 extents = (m_CurrentGrabWidget is StencilWidget) - ? (m_CurrentGrabWidget as StencilWidget).Extents - : Vector3.one * Mathf.Abs(m_CurrentGrabWidget.GetSignedWidgetSize()); - - // Delta-scale bounds should be based on the smallest/largest extent. - // Irritatingly, the API wants absolute rather than relative scale bounds, - // so they need even more conversion. - float deltaScaleMin = vSizeRange.x / extents.Min(); - float deltaScaleMax = vSizeRange.y / extents.Max(); - if (m_GrabWand.startedGrabInsideWidget && m_GrabBrush.startedGrabInsideWidget) - { - newWidgetXf = MathUtils.TwoPointObjectTransformation( - m_GrabWidgetTwoHandWandPrev, m_GrabWidgetTwoHandBrushPrev, - xfWand, xfBrush, - xfObject, - deltaScaleMin: deltaScaleMin, deltaScaleMax: deltaScaleMax); - } - else if (m_GrabWand.startedGrabInsideWidget) - { - // keep the wand inside the object - newWidgetXf = MathUtils.TwoPointObjectTransformation( - m_GrabWidgetTwoHandWandPrev, m_GrabWidgetTwoHandBrushPrev, - xfWand, xfBrush, - xfObject, - deltaScaleMin: deltaScaleMin, deltaScaleMax: deltaScaleMax, - bUseLeftAsPivot: true); - } - else - { - // keep the brush inside the object (note the brush is the left hand) - newWidgetXf = MathUtils.TwoPointObjectTransformation( - m_GrabWidgetTwoHandBrushPrev, m_GrabWidgetTwoHandWandPrev, - xfBrush, xfWand, - xfObject, - deltaScaleMin: deltaScaleMin, deltaScaleMax: deltaScaleMax, - bUseLeftAsPivot: true); - } - - // Must do separately becvause RecordAndSetPosRot ignores newWidgetXf.scale - m_CurrentGrabWidget.RecordAndSetSize(newWidgetXf.scale); - - float currentSize = Mathf.Abs(m_CurrentGrabWidget.GetSignedWidgetSize()); - if (currentSize == vSizeRange.x || currentSize == vSizeRange.y) - { - InputManager.m_Instance.TriggerHaptics(InputManager.ControllerName.Brush, 0.05f); - InputManager.m_Instance.TriggerHaptics(InputManager.ControllerName.Wand, 0.05f); - } - } - - // Ignores TrTransform.scale - m_CurrentGrabWidget.RecordAndSetPosRot(newWidgetXf); - - m_GrabWidgetTwoHandBrushPrev = xfBrush; - m_GrabWidgetTwoHandWandPrev = xfWand; - } - - void UpdateGrab_NoneToOne(InputManager.ControllerName controllerName) - { - if (m_MaybeDriftingGrabWidget != null && - m_MaybeDriftingGrabWidget.IsMoving() && - !m_MaybeDriftingGrabWidget.IsSpinningFreely) - { - // If a new widget is grabbed but the previous one is still drifting, end the drift. - // TODO: Simplify in the widget animation cleanup. - if (m_MaybeDriftingGrabWidget == m_CurrentGrabWidget) - { - SketchMemoryScript.m_Instance.PerformAndRecordCommand( - new MoveWidgetCommand(m_MaybeDriftingGrabWidget, - m_MaybeDriftingGrabWidget.LocalTransform, m_MaybeDriftingGrabWidget.CustomDimension, - final: true), - discardIfNotMerged: true); - } - m_MaybeDriftingGrabWidget.ClearVelocities(); - } - - // UserInteracting should be the first thing that happens here so OnUserBeginInteracting can - // be called before everything else. - m_CurrentGrabWidget.UserInteracting(true, controllerName); - m_CurrentGrabWidget.ClearVelocities(); - ClearGrabWidgetHoldHistory(); - - //set our info names according to this controller's name - m_GrabWidgetOneHandInfo.m_Name = controllerName; - InitializeGrabWidgetControllerInfo(m_GrabWidgetOneHandInfo); - - PointerManager.m_Instance.AllowPointerPreviewLine(false); - PointerManager.m_Instance.RequestPointerRendering(false); - m_SketchSurfacePanel.RequestHideActiveTool(true); - if (m_GrabWidgetOneHandInfo.m_Name == InputManager.ControllerName.Wand) - { - RequestPanelsVisibility(false); - } - - // Notify visuals. - ControllerGrabVisuals.VisualState visualState = - m_GrabWidgetOneHandInfo.m_Name == InputManager.ControllerName.Brush ? - ControllerGrabVisuals.VisualState.WidgetBrushGrip : - ControllerGrabVisuals.VisualState.WidgetWandGrip; - m_ControllerGrabVisuals.SetDesiredVisualState(visualState); - m_ControllerGrabVisuals.SetHeldWidget(m_CurrentGrabWidget.transform); - - //if a gaze object had focus when we grabbed this widget, take focus off the object - ResetActivePanel(); - m_UIReticle.SetActive(false); - - // Prep all other grab widgets for collision. - m_PanelManager.PrimeCollisionSimForWidgets(m_CurrentGrabWidget); - - m_GrabWidgetState = GrabWidgetState.OneHand; - m_WidgetManager.WidgetsDormant = false; - PointerManager.m_Instance.EatLineEnabledInput(); - - m_BackupWandGrabData = null; - m_BackupBrushGrabData = null; - } - - void UpdateGrab_ToNone(GrabWidget rPrevGrabWidget) - { - m_MaybeDriftingGrabWidget = rPrevGrabWidget; - - m_GrabWidgetState = GrabWidgetState.None; - PointerManager.m_Instance.RequestPointerRendering(!App.Instance.IsLoading() && - m_SketchSurfacePanel.ShouldShowPointer()); - RequestPanelsVisibility(true); - m_SketchSurfacePanel.RequestHideActiveTool(false); - rPrevGrabWidget.UserInteracting(false); - - // Disable grab visuals. - m_ControllerGrabVisuals.SetDesiredVisualState(ControllerGrabVisuals.VisualState.Off); - m_ControllerGrabVisuals.SetHeldWidget(null); - - if (m_GrabBrush.grabbingGroup || m_GrabWand.grabbingGroup) - { - SelectionManager.m_Instance.EndGrabbingGroupWithWidget(); - m_GrabBrush.grabbingGroup = false; - m_GrabWand.grabbingGroup = false; - } - } - - void RequestWidgetIntersection(List candidates, - InputManager.ControllerName controllerName) - { - // Get locals based off what controller we're using. - Queue resultQueue = null; - Vector3 controllerPos = Vector3.zero; - if (controllerName == InputManager.ControllerName.Brush) - { - resultQueue = m_BrushResults; - controllerPos = InputManager.m_Instance.GetBrushControllerAttachPoint().position; - } - else - { - resultQueue = m_WandResults; - controllerPos = InputManager.m_Instance.GetWandControllerAttachPoint().position; - } - - // If we don't have a candidate that has a GPU object, don't bother firing off a GPU request. - bool requestGpuIntersection = false; - - // Fire off a new GPU intersection with all widgets that can use it. - for (int i = 0; i < candidates.Count; ++i) - { - if (candidates[i].m_WidgetScript.HasGPUIntersectionObject()) - { - candidates[i].m_WidgetScript.SetGPUIntersectionObjectLayer(m_WidgetGpuIntersectionLayer); - requestGpuIntersection = true; - } - } - - if (requestGpuIntersection) - { - GpuIntersectionResult newRequest = new GpuIntersectionResult(); - newRequest.resultList = new List(); - newRequest.result = App.Instance.GpuIntersector.RequestModelIntersections( - controllerPos, m_WidgetGpuIntersectionRadius, newRequest.resultList, 8, - (1 << m_WidgetGpuIntersectionLayer)); - - // The new result will only be null when the intersector is disabled. - if (newRequest.result != null) - { - resultQueue.Enqueue(newRequest); - } - - for (int i = 0; i < candidates.Count; ++i) - { - if (candidates[i].m_WidgetScript.HasGPUIntersectionObject()) - { - candidates[i].m_WidgetScript.RestoreGPUIntersectionObjectLayer(); - } - } - } - } - - GrabWidgetData GetBestWidget(List candidates, - Queue resultQueue) - { - // Discard futures that are too old. - while (resultQueue.Count > 0) - { - if (Time.frameCount - resultQueue.Peek().result.StartFrame < 5) - { - break; - } - resultQueue.Dequeue(); - } - - // If the oldest future is ready, use its intersection result to update the candidates. - GpuIntersectionResult finishedResult; - if (resultQueue.Count > 0 && resultQueue.Peek().result.IsReady) - { - finishedResult = resultQueue.Dequeue(); - } - else - { - finishedResult.resultList = new List(); - } - - // TODO: Speed this up. - for (int i = 0; i < candidates.Count; ++i) - { - if (candidates[i].m_WidgetScript.HasGPUIntersectionObject()) - { - // If a candidate can't find itself in the finished results list, it's not eligible. - bool candidateValid = false; - for (int j = 0; j < finishedResult.resultList.Count; ++j) - { - if (candidates[i].m_WidgetScript.Equals(finishedResult.resultList[j].widget)) - { - candidateValid = true; - break; - } - } - - if (candidateValid) - { - // If a candidate has a GPU intersection object and we found it in this list, - // not only is it valid, but it's as valid as it can be. - candidates[i].m_ControllerScore = 1.0f; - } - else - { - candidates[i].m_NearController = false; - } - } - } - - // Run through the candidates and pick - GrabWidgetData best = null; - for (int i = 0; i < candidates.Count; ++i) - { - var candidate = candidates[i]; - if (!candidate.m_NearController) continue; - - // For media widgets - only select from the active layer - if (candidate.m_WidgetScript is MediaWidget - && candidate.m_WidgetScript.Canvas != App.Scene.ActiveCanvas) continue; - - if (best == null || candidate.m_ControllerScore > best.m_ControllerScore) - { - best = candidate; - } - } - return best; - } - - void InitializeGrabWidgetControllerInfo(GrabWidgetControllerInfo info) - { - Transform controller = InputManager.Controllers[(int)info.m_Name].Transform; - Transform widget = m_CurrentGrabWidget.GrabTransform_GS; - TrTransform newWidgetXf = Coords.AsGlobal[widget]; - - info.m_BaseControllerXf = Coords.AsGlobal[controller]; - info.m_BaseWidgetXf_LS = info.m_BaseControllerXf.inverse * newWidgetXf; - } - - // returns the transform of the true widget (not the snapped one for those that can be) - private TrTransform GetWorkingTransform(GrabWidget w) - { - TrTransform ret = w.GetGrabbedTrTransform(); - ret.scale = w.GetSignedWidgetSize(); - return ret; - } - - // Initiate the world transform reset animation. - public void RequestWorldTransformReset(bool toSavedXf = false) - { - if (WorldIsReset(toSavedXf)) - { - return; - } - - m_WorldTransformResetXf = - toSavedXf ? SketchMemoryScript.m_Instance.InitialSketchTransform : TrTransform.identity; - m_WorldTransformResetState = WorldTransformResetState.Requested; - - App.Scene.disableTiltProtection = false; - } - - void UpdateWorldTransformReset() - { - switch (m_WorldTransformResetState) - { - case WorldTransformResetState.Requested: - ViewpointScript.m_Instance.FadeToColor(Color.black, m_GrabWorldFadeSpeed); - m_WorldTransformResetState = WorldTransformResetState.FadingToBlack; - m_xfDropCamReset_RS = Coords.AsRoom[m_DropCam.transform]; - PointerManager.m_Instance.EatLineEnabledInput(); - PointerManager.m_Instance.AllowPointerPreviewLine(false); - break; - case WorldTransformResetState.FadingToBlack: - m_WorldTransformFadeAmount += m_GrabWorldFadeSpeed * Time.deltaTime; - if (m_WorldTransformFadeAmount >= 1.0f) - { - App.Scene.Pose = m_WorldTransformResetXf; - m_WorldTransformFadeAmount = 1.0f; - m_WorldTransformResetState = WorldTransformResetState.FadingToScene; - ViewpointScript.m_Instance.FadeToScene(m_GrabWorldFadeSpeed); - m_DropCam.transform.position = m_xfDropCamReset_RS.translation; - m_DropCam.transform.rotation = m_xfDropCamReset_RS.rotation; - PointerManager.m_Instance.AllowPointerPreviewLine(true); - } - break; - case WorldTransformResetState.FadingToScene: - m_WorldTransformFadeAmount -= m_GrabWorldFadeSpeed * Time.deltaTime; - if (m_WorldTransformFadeAmount <= 0.0f) - { - m_WorldTransformFadeAmount = 0.0f; - m_WorldTransformResetState = WorldTransformResetState.Default; - } - break; - } - } - - bool CheckToggleTiltProtection() - { - if ( - InputManager.Wand.GetCommandDown(InputManager.SketchCommands.Redo) || - InputManager.Brush.GetCommandDown(InputManager.SketchCommands.Redo) - ) - { - App.Scene.disableTiltProtection = !App.Scene.disableTiltProtection; - - return !App.Scene.disableTiltProtection; - } - - return false; - - } - - void UpdateGrab_World() - { - bool bAllowWorldTransform = m_SketchSurfacePanel.ActiveTool.AllowWorldTransformation() && - (m_GrabWorldState != GrabWorldState.ResetDone) && - (!PointerManager.m_Instance.IsMainPointerCreatingStroke() || App.Instance.IsLoading()) && - App.Instance.IsInStateThatAllowsAnyGrabbing() && - !m_DisableWorldGrabbing; - - bool bWorldGrabWandPrev = m_GrabWand.grabbingWorld; - bool bWorldGrabBrushPrev = m_GrabBrush.grabbingWorld; - m_GrabWand.grabbingWorld = bAllowWorldTransform && !m_GrabWand.eatInput && - InputManager.Wand.GetControllerGrip(); - m_GrabBrush.grabbingWorld = bAllowWorldTransform && !m_GrabBrush.eatInput && - InputManager.Brush.GetControllerGrip() && - (m_CurrentGazeObject == -1); - - bool grabsChanged = (bWorldGrabWandPrev != m_GrabWand.grabbingWorld) || - (bWorldGrabBrushPrev != m_GrabBrush.grabbingWorld); - bool bAllowWorldTransformChanged = - bAllowWorldTransform != m_AllowWorldTransformLastFrame; - int nGrabs = m_GrabWand.grabbingWorld ? 1 : 0; - nGrabs += m_GrabBrush.grabbingWorld ? 1 : 0; - - // Allow grabbing again if grabs have changed and we're done resetting. - if (m_GrabWorldState == GrabWorldState.ResetDone && grabsChanged) - { - m_GrabWorldState = GrabWorldState.Normal; - } - - // Update panels visibility if brush grip has changed. - if (bWorldGrabWandPrev != m_GrabWand.grabbingWorld) - { - RequestPanelsVisibility(!m_GrabWand.grabbingWorld); - } - - // Update tool visibility if brush grip has changed. - if (bWorldGrabBrushPrev != m_GrabBrush.grabbingWorld) - { - m_SketchSurfacePanel.RequestHideActiveTool(m_GrabBrush.grabbingWorld); - PointerManager.m_Instance.AllowPointerPreviewLine(!m_GrabBrush.grabbingWorld); - PointerManager.m_Instance.RequestPointerRendering(!m_GrabBrush.grabbingWorld - && m_SketchSurfacePanel.ShouldShowPointer() && !App.Instance.IsLoading()); - } - - // Reset m_WorldBeingGrabbed and only set it when world is actually being grabbed. - bool bWorldBeingGrabbedPrev = m_WorldBeingGrabbed; - m_WorldBeingGrabbed = false; - - // Move the world if it has been grabbed. - if (m_GrabWorldState == GrabWorldState.Normal && bAllowWorldTransform) - { - if (nGrabs == 2) - { - // Two-handed world movement. - m_WorldBeingGrabbed = true; - TrTransform grabXfWand = TrTransform.FromTransform( - InputManager.m_Instance.GetController(InputManager.ControllerName.Wand)); - TrTransform grabXfBrush = TrTransform.FromTransform( - InputManager.m_Instance.GetController(InputManager.ControllerName.Brush)); - - // Offset the controller positions so that they're centered on the grips. - Vector3 gripPos = InputManager.Controllers[(int)InputManager.ControllerName.Brush].Geometry.GripAttachPoint.localPosition; - gripPos.x = 0.0f; - grabXfWand.translation += grabXfWand.MultiplyVector(gripPos); - grabXfBrush.translation += grabXfBrush.MultiplyVector(gripPos); - - // Are we initiating two hand transform this frame? - if (!bWorldGrabWandPrev || !bWorldGrabBrushPrev) - { - PointerManager.m_Instance.EnableLine(false); - PointerManager.m_Instance.AllowPointerPreviewLine(false); - PointerManager.m_Instance.RequestPointerRendering(false); - // Initiate audio loop - m_WorldTransformSpeedSmoothed = 0.0f; - AudioManager.m_Instance.WorldGrabLoop(true); - } - else - { - TrTransform xfOld = GrabbedPose; - TrTransform xfNew; - float deltaScaleMin = WorldTransformMinScale / xfOld.scale; - float deltaScaleMax = WorldTransformMaxScale / xfOld.scale; - bool fixOffset = false; - fixOffset = CheckToggleTiltProtection(); - xfNew = MathUtils.TwoPointObjectTransformation( - m_GrabBrush.grabTransform, m_GrabWand.grabTransform, - grabXfBrush, grabXfWand, - xfOld, - rotationAxisConstraint: (App.Scene.disableTiltProtection ? default(Vector3) : Vector3.up), - deltaScaleMin: deltaScaleMin, deltaScaleMax: deltaScaleMax); - float fCurrentWorldTransformSpeed = - Mathf.Abs((xfNew.scale - xfOld.scale) / Time.deltaTime); - m_WorldTransformSpeedSmoothed = - Mathf.Lerp(m_WorldTransformSpeedSmoothed, fCurrentWorldTransformSpeed, - AudioManager.m_Instance.m_WorldGrabLoopSmoothSpeed * Time.deltaTime); - AudioManager.m_Instance.ChangeLoopVolume("WorldGrab", - Mathf.Clamp(m_WorldTransformSpeedSmoothed / - AudioManager.m_Instance.m_WorldGrabLoopAttenuation, 0f, - AudioManager.m_Instance.m_WorldGrabLoopMaxVolume)); - - if (fixOffset) - { - Vector3 midPoint = Vector3.Lerp(grabXfBrush.translation, grabXfWand.translation, 0.5f); - - Vector3 localMidPointOldXF = xfOld.inverse * midPoint; - - // assign this to force the axial protection - GrabbedPose = xfNew; - xfNew = GrabbedPose; - - Vector3 midPointXFNew = xfNew * localMidPointOldXF; - - TrTransform xfDelta1 = TrTransform.T(midPoint - midPointXFNew); - xfNew = xfDelta1 * xfNew; - } - GrabbedPose = xfNew; - } - - // Update last states. - m_GrabBrush.grabTransform = grabXfBrush; - m_GrabWand.grabTransform = grabXfWand; - } - } - else if (m_GrabWorldState == GrabWorldState.ResettingTransform) - { - if (m_WorldTransformResetState == WorldTransformResetState.FadingToScene) - { - ResetGrabbedPose(); - PanelManager.m_Instance.ExecuteOnPanel(x => x.OnPanelMoved()); - - // World can't be transformed right after a reset until grab states have changed. - if (bAllowWorldTransform) - { - bAllowWorldTransform = false; - bAllowWorldTransformChanged = - bAllowWorldTransform != m_AllowWorldTransformLastFrame; - } - - // Set the grab world state on exit. - if (nGrabs == 0) - { - m_GrabWorldState = GrabWorldState.Normal; - } - else - { - m_GrabWorldState = GrabWorldState.ResetDone; - } - } - } - - if (grabsChanged || bAllowWorldTransformChanged) - { - // Fade in grid when doing two handed spin. - if (nGrabs == 2 && !bAllowWorldTransformChanged) - { - ViewpointScript.m_Instance.FadeGroundPlaneIn(m_GrabWorldGridColor, m_GrabWorldFadeSpeed); - } - else - { - ViewpointScript.m_Instance.FadeGroundPlaneOut(m_GrabWorldFadeSpeed); - } - } - - // Update visuals for world transform - if (grabsChanged) - { - bool bDoubleGrip = m_GrabBrush.grabbingWorld && m_GrabWand.grabbingWorld; - bool bSingleGrip = m_GrabBrush.grabbingWorld || m_GrabWand.grabbingWorld; - Vector3 vControllersMidpoint = - (InputManager.m_Instance.GetControllerPosition(InputManager.ControllerName.Brush) + - InputManager.m_Instance.GetControllerPosition(InputManager.ControllerName.Wand)) * 0.5f; - - // Update transform line visuals - if (bDoubleGrip) - { - m_ControllerGrabVisuals.SetDesiredVisualState(ControllerGrabVisuals.VisualState.WorldDoubleGrip); - AudioManager.m_Instance.WorldGrabbed(vControllersMidpoint); - } - else if (bSingleGrip) - { - if (m_GrabWand.grabbingWorld) - { - m_ControllerGrabVisuals.SetDesiredVisualState(ControllerGrabVisuals.VisualState.WorldWandGrip); - } - else - { - m_ControllerGrabVisuals.SetDesiredVisualState(ControllerGrabVisuals.VisualState.WorldBrushGrip); - } - - if (!bWorldGrabWandPrev && !bWorldGrabBrushPrev) - { - AudioManager.m_Instance.WorldGrabbed(vControllersMidpoint); - } - else - { - AudioManager.m_Instance.WorldGrabLoop(false); - } - } - else - { - m_ControllerGrabVisuals.SetDesiredVisualState(ControllerGrabVisuals.VisualState.Off); - AudioManager.m_Instance.WorldGrabLoop(false); - } - - if (m_GrabWand.grabbingWorld || m_GrabBrush.grabbingWorld) - { - m_WidgetManager.WidgetsDormant = false; - PointerManager.m_Instance.EatLineEnabledInput(); - } - } - - // Reset scene transform if we're gripping and press the track pad. - bool wandReset = m_GrabWand.grabbingWorld && - InputManager.Wand.GetCommandDown(InputManager.SketchCommands.WorldTransformReset); - bool brushReset = m_GrabBrush.grabbingWorld && - InputManager.Brush.GetCommandDown(InputManager.SketchCommands.WorldTransformReset); - if ((wandReset || brushReset) && !WorldIsReset(toSavedXf: false)) - { - m_GrabBrush.eatInput = true; - m_GrabWand.eatInput = true; - m_EatToolScaleInput = true; - m_GrabWorldState = GrabWorldState.ResettingTransform; - RequestWorldTransformReset(); - AudioManager.m_Instance.PlayTransformResetSound(); - } - - // Update the skybox rotation with the new scene rotation. - if (RenderSettings.skybox) - { - Quaternion sceneQuaternion = App.Instance.m_SceneTransform.rotation; - RenderSettings.skybox.SetVector( - "_SkyboxRotation", - new Vector4(sceneQuaternion.x, sceneQuaternion.y, sceneQuaternion.z, sceneQuaternion.w)); - } - - // Update last frame members. - m_AllowWorldTransformLastFrame = bAllowWorldTransform; - } - - /// If lhs and rhs are overlapping, return the smallest vector that would - /// cause rhs to stop overlapping; otherwise, return 0. - /// lhs: an antisphere (solid outside, empty inside) - /// rhs: a sphere (empty outside, solid inside) - private static Vector3 GetOverlap_Antisphere_Sphere( - Vector3 lhsCenter, float lhsRadius, - Vector3 rhsCenter, float rhsRadius) - { - // If anyone passes negative values, they are a bad person - lhsRadius = Mathf.Abs(lhsRadius); - rhsRadius = Mathf.Abs(rhsRadius); - // Without loss of generality, can recenter on lhs - rhsCenter -= lhsCenter; - lhsCenter -= lhsCenter; - - float maxDistance = lhsRadius - rhsRadius; - - // Edge case: sphere does not fit in antisphere - if (maxDistance <= 0) - { - return -rhsCenter; - } - - float penetrationDistance = Mathf.Max(0, rhsCenter.magnitude - maxDistance); - return -penetrationDistance * rhsCenter.normalized; - } - - public static bool IsValidScenePose(TrTransform xf, float radialBounds) - { - // Simple and dumb implementation for now. - return xf == MakeValidScenePose(xf, radialBounds); - } - - /// This is like MakeValidScenePose, but it guarantees that: - /// - The return value is a valid result of Lerp(scene0, scene1, t), - /// for some handwavy definition of "lerp" - /// - The lerp "t" is in [0, 1] - /// - IsValidScenePose(return value) is true, subject to the previous constraints. - /// - /// Think of it as doing a cast from scene0 to scene1. - public static TrTransform MakeValidSceneMove( - TrTransform scene0, TrTransform scene1, float radialBounds) - { - if (IsValidScenePose(scene1, radialBounds)) - { - return scene1; - } - if (!IsValidScenePose(scene0, radialBounds)) - { - Debug.LogError("Invalid scene cast start"); - return scene0; - } - - // We don't support lerping either of these - Debug.Assert(scene0.rotation == scene1.rotation); - Debug.Assert(scene0.scale == scene1.scale); - - Vector3 vRoom0 = -scene0.translation; - Vector3 vRoom1 = -scene1.translation; - float radius = (scene0.scale - * radialBounds - * App.METERS_TO_UNITS) - App.Instance.RoomRadius; - - float t0, t1; - bool success = MathUtils.RaySphereIntersection( - vRoom0, vRoom1 - vRoom0, - Vector3.zero, radius, out t0, out t1); - if (!success) - { - // If this were more important, we could solve for the t of the closest approach - return scene0; - } - - // t0 is expected to be < 0 (room starts inside the fence) - // t1 is expected to be in [0, 1] (room ends outside the fence) - - // Constraints: - // - Lerp t must be in [0, 1]. (Do not move past the requested endpoint) - // - Lerp t should be as high as possible but < t1. (Do not exit the sphere) - float t = Mathf.Clamp(t1, 0, 1); - - TrTransform sceneT = TrTransform.TRS( - Vector3.Lerp(scene0.translation, scene1.translation, t), - scene0.rotation, - scene0.scale); - return MakeValidScenePose(sceneT, radialBounds); - } - - /// Returns a new ScenePose TrTransform that does not cause the room - /// to violate the hard scene bounds. - /// - /// scenePose - The current, possibly invalid scene pose - public static TrTransform MakeValidScenePose(TrTransform scenePose, float radialBounds) - { - scenePose.scale = Mathf.Clamp( - scenePose.scale, - SketchControlsScript.m_Instance.WorldTransformMinScale, - SketchControlsScript.m_Instance.WorldTransformMaxScale); - - // Anything not explicitly qualified is in room space. - - float roomRadius = App.Instance.RoomRadius; - Vector3 roomCenter = Vector3.zero; - - float fenceRadius = scenePose.scale * radialBounds - * App.METERS_TO_UNITS; - Vector3 fenceCenter = scenePose.translation; - - Vector3 moveRoom = GetOverlap_Antisphere_Sphere( - fenceCenter, fenceRadius, roomCenter, roomRadius); - Vector3 moveFence = -moveRoom; - - scenePose.translation += moveFence; - return scenePose; - } - - /// Clears data used by GetGrabWidgetHoldHistory() - /// Should be called any time m_GrabWidgetOneHandInfo changes - void ClearGrabWidgetHoldHistory() - { - m_GrabWidgetHoldHistory.Clear(); - } - - /// Collects data for use with GetGrabWidgetHoldHistory() - void UpdateGrabWidgetHoldHistory(InputManager.ControllerName name) - { - float t = Time.realtimeSinceStartup; - var info = InputManager.Controllers[(int)name]; - m_GrabWidgetHoldHistory.Enqueue(new GrabWidgetHoldPoint - { - m_Name = name, - m_BirthTime = t, - m_Pos = info.Transform.position, - m_Rot = info.Transform.rotation - }); - - // Trim the fat off our widget history - while (m_GrabWidgetHoldHistory.Count > 0 && - t - m_GrabWidgetHoldHistory.Peek().m_BirthTime >= kControlPointHistoryMaxTime) - { - m_GrabWidgetHoldHistory.Dequeue(); - } - } - - /// Returns possibly-smoothed linear and angular velocities. May fail. - /// Angular velocity is returned as an axial vector whose length() is degrees/second - bool GetGrabWidgetHoldHistory(out Vector3 vLinearVelocity, out Vector3 vAngularVelocity) - { - vLinearVelocity = vAngularVelocity = Vector3.zero; - if (m_GrabWidgetHoldHistory.Count < 2) - { - return false; - } - - // We need pairs of elements, so a simple foreach() won't quite work. - // Maybe using linq .First() and .Skip() would be okay. - using (IEnumerator enumerator = m_GrabWidgetHoldHistory.GetEnumerator()) - { - if (!enumerator.MoveNext()) - { - return false; - } - - // Infinitesimal rotations commute, and scaled-axis-angle rotations commute - // "better" than other rotation formats. - Vector3 totalDeltaTheta = Vector3.zero; - - GrabWidgetHoldPoint first = enumerator.Current; - GrabWidgetHoldPoint prev = first; - GrabWidgetHoldPoint current = first; - while (enumerator.MoveNext()) - { - current = enumerator.Current; - - // For our quaternion, find the difference, convert it to angle/axis, and sum it - // Find delta such that delta * prev = cur - // left-multiply because we want it in world-space. - // multiply vs prev since we want the delta that takes us forward in time - // rather than backward in time. - Quaternion dtheta = current.m_Rot * Quaternion.Inverse(prev.m_Rot); - // Assume the rotation took the shorter path - if (dtheta.w < 0) - { - dtheta.Set(-dtheta.x, -dtheta.y, -dtheta.z, -dtheta.w); - } - - float degrees; - Vector3 axis; - dtheta.ToAngleAxis(out degrees, out axis); - totalDeltaTheta += (axis * degrees); - prev = current; - } - - // Linear velocity calculation doesn't need to look at intermediate points - Vector3 totalDeltaPosition = current.m_Pos - first.m_Pos; - float totalDeltaTime = current.m_BirthTime - first.m_BirthTime; - if (totalDeltaTime == 0) - { - return false; - } - - vLinearVelocity = totalDeltaPosition / totalDeltaTime; - vAngularVelocity = totalDeltaTheta / totalDeltaTime; - return true; - } - } - - bool IsControllerNearWidget(InputManager.ControllerName name, GrabWidget widget) - { - Vector3 vControllerPos = InputManager.m_Instance.GetControllerAttachPointPosition(name); - return widget.GetActivationScore(vControllerPos, name) >= 0.0f; - } - - void RefreshCurrentGazeObject() - { - UnityEngine.Profiling.Profiler.BeginSample("SketchControlScript.RefreshCurrentGazeObject"); - int iPrevGazeObject = m_CurrentGazeObject; - m_CurrentGazeObject = -1; - bool bGazeAllowed = (m_CurrentInputState == InputState.Standard) - && !InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Activate) - && !m_SketchSurfacePanel.ActiveTool.InputBlocked() - && (m_GrabWidgetState == GrabWidgetState.None) - && !m_GrabBrush.grabbingWorld - && !m_PinCushion.IsShowing() - && !PointerManager.MainPointerIsPainting() - ; - - bool bGazeDeactivationOverrideWithInput = false; - List aAllPanels = m_PanelManager.GetAllPanels(); - - bool hasController = m_ControlsType == ControlsType.SixDofControllers; - - //if we're re-positioning a panel, keep it active - if (m_PositioningPanelWithHead) - { - m_CurrentGazeObject = iPrevGazeObject; - } - // Only activate gaze objects if we're in standard input mode, and if we don't have the 'draw' - // button held. - else if ((bGazeAllowed || (iPrevGazeObject != -1))) - { - //reset hit flags - for (int i = 0; i < m_GazeResults.Length; ++i) - { - m_GazeResults[i].m_HitWithGaze = false; - m_GazeResults[i].m_HitWithController = false; - m_GazeResults[i].m_WithinView = false; - } - - // If we're in controller mode, find the nearest colliding widget that might get in our way. - float fNearestWidget = 99999.0f; - if (hasController) - { - fNearestWidget = m_WidgetManager.DistanceToNearestWidget(m_GazeControllerRay); - } - - //check all panels for gaze hit - bool bRequireVisibilityCheck = !hasController || (iPrevGazeObject == -1); - if (m_PanelManager.PanelsAreStable()) - { - RaycastHit rHitInfo; - bool bRayHit = false; - int panelsHit = 0; - for (int i = 0; i < aAllPanels.Count; ++i) - { - // Ignore fixed panels when they are not visible. - if (!m_PanelManager.GazePanelsAreVisible() && aAllPanels[i].m_Panel.m_Fixed) - { - continue; - } - - if (aAllPanels[i].m_Panel.gameObject.activeSelf && aAllPanels[i].m_Panel.IsAvailable()) - { - //make sure this b-snap is in view - Vector3 vToPanel = aAllPanels[i].m_Panel.transform.position - m_CurrentGazeRay.origin; - vToPanel.Normalize(); - if (!bRequireVisibilityCheck || Vector3.Angle(vToPanel, m_CurrentGazeRay.direction) < m_GazeMaxAngleFromFacing) - { - if (hasController) - { - if (aAllPanels[i].m_Panel.HasMeshCollider()) - { - //make sure the angle between the pointer and the panel forward is below our max angle - if (Vector3.Angle(aAllPanels[i].m_Panel.transform.forward, m_GazeControllerRay.direction) < m_GazeMaxAngleFromPointing) - { - //make sure the angle between the user-to-panel and the panel forward is reasonable - if (Vector3.Angle(aAllPanels[i].m_Panel.transform.forward, vToPanel) < m_GazeMaxAngleFacingToForward) - { - m_GazeResults[i].m_WithinView = true; - - bRayHit = false; - bRayHit = aAllPanels[i].m_Panel.RaycastAgainstMeshCollider( - m_GazeControllerRay, out rHitInfo, m_GazeControllerPointingDistance); - - if (bRayHit) - { - //if the ray starts inside the panel, we won't get a good hit point, it'll just be zero - if (rHitInfo.point.sqrMagnitude > 0.1f) - { - if (rHitInfo.distance < fNearestWidget) - { - m_GazeResults[i].m_ControllerDistance = rHitInfo.distance; - m_GazeResults[i].m_ControllerPosition = rHitInfo.point; - m_GazeResults[i].m_HitWithController = true; - panelsHit++; - } - } - } - } - } - } - } - else - { - m_GazeResults[i].m_WithinView = true; - if (aAllPanels[i].m_Panel.GetCollider().Raycast(m_CurrentGazeRay, out rHitInfo, m_GazeMaxDistance)) - { - m_GazeResults[i].m_GazePosition = rHitInfo.point; - m_GazeResults[i].m_HitWithGaze = true; - } - } - } - } - } - - // No panels hit within normal ray distance. - // Check if previous panel still pointed to. - if (panelsHit == 0) - { - if (iPrevGazeObject != -1) - { - // Don't allow any panel to hold focus if it's facing away from the user. - Vector3 vToPanel = aAllPanels[iPrevGazeObject].m_Panel.transform.position - - m_CurrentGazeRay.origin; - vToPanel.Normalize(); - if (Vector3.Angle(aAllPanels[iPrevGazeObject].m_Panel.transform.forward, vToPanel) < - m_GazeMaxAngleFacingToForward) - { - float fDist = m_GazeControllerPointingDistance * 1.5f; - bRayHit = aAllPanels[iPrevGazeObject].m_Panel.RaycastAgainstMeshCollider( - m_GazeControllerRayActivePanel, out rHitInfo, fDist); - if (bRayHit) - { - if (rHitInfo.point.sqrMagnitude > 0.1f) - { - if (rHitInfo.distance < fNearestWidget) - { - m_GazeResults[iPrevGazeObject].m_ControllerDistance = rHitInfo.distance; - m_GazeResults[iPrevGazeObject].m_ControllerPosition = rHitInfo.point; - m_GazeResults[iPrevGazeObject].m_HitWithController = true; - } - } - } - } - } - } - } - - //determine what panel we hit, take the one with the lowest controller distance - float fControllerDist = 999.0f; - int iControllerIndex = -1; - if (hasController) - { - for (int i = 0; i < m_GazeResults.Length; ++i) - { - if (m_GazeResults[i].m_HitWithController) - { - if (m_GazeResults[i].m_ControllerDistance < fControllerDist) - { - iControllerIndex = i; - fControllerDist = m_GazeResults[i].m_ControllerDistance; - } - } - } - } - - //if we found something near our controller, take it - if (iControllerIndex != -1) - { - m_CurrentGazeObject = iControllerIndex; - m_CurrentGazeHitPoint = m_GazeResults[iControllerIndex].m_ControllerPosition; - - // TODO: This should not be hardcoded once multiple pointers are allowed. - m_GazeResults[m_CurrentGazeObject].m_ControllerName = InputManager.ControllerName.Brush; - if (m_GazeResults[m_CurrentGazeObject].m_HitWithGaze) - { - //average with the gaze position if we hit that too - m_CurrentGazeHitPoint += m_GazeResults[m_CurrentGazeObject].m_GazePosition; - m_CurrentGazeHitPoint *= 0.5f; - } - } - else - { - //nothing near the controller, see if we're looking at the previous - if (iPrevGazeObject != -1 && m_GazeResults[iPrevGazeObject].m_HitWithGaze) - { - m_CurrentGazeObject = iPrevGazeObject; - m_CurrentGazeHitPoint = m_GazeResults[m_CurrentGazeObject].m_GazePosition; - } - else - { - //controller and gaze not near panel, pick the first panel we're looking at - for (int i = 0; i < m_GazeResults.Length; ++i) - { - if (m_GazeResults[i].m_HitWithGaze) - { - m_CurrentGazeObject = i; - m_CurrentGazeHitPoint = m_GazeResults[i].m_GazePosition; - break; - } - } - } - } - - //forcing users to look away from gaze panel - if (m_EatInputGazeObject && m_CurrentGazeObject != -1) - { - m_CurrentGazeObject = -1; - } - else if (m_CurrentGazeObject == -1) - { - m_EatInputGazeObject = false; - } - } - - //if we're staring at a panel, keep our countdown fresh - if (m_CurrentGazeObject != -1 || m_ForcePanelActivation) - { - m_GazePanelDectivationCountdown = m_GazePanelDectivationDelay; - } - else - { - if (InputManager.m_Instance.GetCommandDown(InputManager.SketchCommands.Activate)) - { - bGazeDeactivationOverrideWithInput = true; - m_GazePanelDectivationCountdown = 0.0f; - } - else - { - m_GazePanelDectivationCountdown -= Time.deltaTime; - } - if (m_GazePanelDectivationCountdown > 0.0f) - { - m_CurrentGazeObject = iPrevGazeObject; - } - } - - //update our positioning timer - if (m_PositioningPanelWithHead) - { - m_PositioningTimer += m_PositioningSpeed * Time.deltaTime; - m_PositioningTimer = Mathf.Min(m_PositioningTimer, 1.0f); - } - else - { - m_PositioningTimer -= m_PositioningSpeed * Time.deltaTime; - m_PositioningTimer = Mathf.Max(m_PositioningTimer, 0.0f); - } - - //prime objects if we change targets - if (iPrevGazeObject != m_CurrentGazeObject) - { - //if we're switching panels, make sure the pointer doesn't streak - PointerManager.m_Instance.DisablePointerPreviewLine(); - - if (iPrevGazeObject != -1) - { - aAllPanels[iPrevGazeObject].m_Panel.PanelGazeActive(false); - aAllPanels[iPrevGazeObject].m_Panel.SetPositioningPercent(0.0f); - } - if (m_CurrentGazeObject != -1) - { - //make sure our line is disabled - if (m_GazeResults[m_CurrentGazeObject].m_ControllerName == InputManager.ControllerName.Brush) - { - PointerManager.m_Instance.EnableLine(false); - PointerManager.m_Instance.AllowPointerPreviewLine(false); - } - - aAllPanels[m_CurrentGazeObject].m_Panel.PanelGazeActive(true); - aAllPanels[m_CurrentGazeObject].m_Panel.SetPositioningPercent(0.0f); - - if (m_GazeResults[m_CurrentGazeObject].m_ControllerName == InputManager.ControllerName.Brush) - { - m_SketchSurfacePanel.RequestHideActiveTool(true); - } - } - else - { - //if we don't have a panel, we need to enable the pointer according to the current tool - PointerManager.m_Instance.RefreshFreePaintPointerAngle(); - PointerManager.m_Instance.RequestPointerRendering(m_SketchSurfacePanel.ShouldShowPointer()); - m_UIReticle.SetActive(false); - m_SketchSurfacePanel.RequestHideActiveTool(false); - if (!bGazeDeactivationOverrideWithInput) - { - m_SketchSurfacePanel.EatToolsInput(); - } - } - - m_PositioningPanelWithHead = false; - } - UnityEngine.Profiling.Profiler.EndSample(); - } - - void UpdateActiveGazeObject() - { - BasePanel currentPanel = m_PanelManager.GetPanel(m_CurrentGazeObject); - currentPanel.SetPositioningPercent(m_PositioningTimer); - bool hasController = m_ControlsType == ControlsType.SixDofControllers; - // Update positioning behavior. - if (m_PositioningPanelWithHead) - { - if (!InputManager.m_Instance.GetCommand(InputManager.SketchCommands.LockToHead) && - !InputManager.m_Instance.GetCommand(InputManager.SketchCommands.LockToController)) - { - // No more positioning. - m_PositioningPanelWithHead = false; - m_PanelManager.m_SweetSpot.EnableBorderSphere(false, Vector3.zero, 0.0f); - currentPanel.PanelHasStoppedMoving(); - } - else - { - //lock the panel to the sweet spot bounds in the direction the user is looking - Quaternion qDiff = m_CurrentHeadOrientation * Quaternion.Inverse(m_PositioningPanelBaseHeadRotation); - Vector3 vAdjustedOffset = qDiff * m_PositioningPanelOffset; - - Vector3 vNewPos = m_PanelManager.m_SweetSpot.transform.position + vAdjustedOffset; - currentPanel.transform.position = vNewPos; - - vAdjustedOffset.Normalize(); - currentPanel.transform.forward = vAdjustedOffset; - - float fHighlightRadius = currentPanel.m_BorderSphereHighlightRadius; - m_PanelManager.m_SweetSpot.EnableBorderSphere(true, vNewPos, fHighlightRadius * m_PositioningTimer); - - //once we've moved this panel, run the simulation on the other panels to resolve collisions - m_PanelManager.DoCollisionSimulationForKeyboardMouse(currentPanel); - } - } - else - { - // It's possible that, on this frame, before this function was called, active gaze was pulled - // from this panel. In this case, we want to skip updating this frame. - // This happens when a panel has gaze and world grab dismisses all panels, for example. - if (currentPanel.IsActive()) - { - //orient to gaze - if (hasController) - { - currentPanel.UpdatePanel(m_GazeControllerRay.direction, m_CurrentGazeHitPoint); - } - else - { - currentPanel.UpdatePanel(m_CurrentGazeRay.direction, m_CurrentGazeHitPoint); - } - } - - if (!hasController) - { - //lock to head if we're holding a lock button.. - bool bLockToHead = InputManager.m_Instance.GetCommand(InputManager.SketchCommands.LockToHead) || - InputManager.m_Instance.GetCommand(InputManager.SketchCommands.LockToController); - - if (bLockToHead) - { - m_PositioningPanelWithHead = true; - m_PositioningPanelBaseHeadRotation = m_CurrentHeadOrientation; - m_PositioningPanelOffset = currentPanel.transform.position - - m_PanelManager.m_SweetSpot.transform.position; - - currentPanel.ResetPanelFlair(); - - //prime all other panels for movement - m_PanelManager.PrimeCollisionSimForKeyboardMouse(); - } - } - - PointerManager.m_Instance.RequestPointerRendering(false); - currentPanel.UpdateReticleOffset(m_MouseDeltaX, m_MouseDeltaY); - } - - // Keep reticle locked in the right spot. - Vector3 reticlePos = Vector3.zero; - Vector3 reticleForward = Vector3.zero; - if (hasController) - { - currentPanel.GetReticleTransformFromPosDir(m_CurrentGazeHitPoint, - m_GazeControllerRay.direction, out reticlePos, out reticleForward); - } - else - { - currentPanel.GetReticleTransform(out reticlePos, out reticleForward, - (m_ControlsType == ControlsType.ViewingOnly)); - } - - SetUIReticleTransform(reticlePos, -reticleForward); - m_UIReticle.SetActive(GetGazePanelActivationRatio() >= 1.0f); - } - - public void ResetActivePanel() - { - m_PanelManager.ResetPanel(m_CurrentGazeObject); - PointerManager.m_Instance.DisablePointerPreviewLine(); - m_PositioningPanelWithHead = false; - m_CurrentGazeObject = -1; - } - - void UpdatePanInput() - { - if (Mouse.current.rightButton.isPressed) - { - Vector3 vPanDiff = Vector3.zero; - vPanDiff += (Vector3.right * m_MouseDeltaXScaled); - vPanDiff += (Vector3.up * m_MouseDeltaYScaled); - Vector3 vSurfacePos = m_SketchSurface.transform.position; - m_SketchSurface.transform.position = vSurfacePos + vPanDiff; - } - else - { - float fCurrentTime = Time.realtimeSinceStartup; - if (fCurrentTime - m_PositionOffsetResetTapTime < m_DoubleTapWindow) - { - if (m_CurrentGazeObject == -1) - { - ResetGrabbedPose(); - } - } - m_PositionOffsetResetTapTime = fCurrentTime; - - SwitchState(InputState.Standard); - } - } - - void UpdateRotationInput() - { - if (InputManager.m_Instance.GetCommand(InputManager.SketchCommands.PivotRotation)) - { - bool bAltInputActive = InputManager.m_Instance.GetCommand(InputManager.SketchCommands.AltActivate); - bool bRollRotation = m_RotationRollActive || bAltInputActive || m_CurrentRotationType == RotationType.RollOnly; - m_RotationIcon.SetActive(bRollRotation); - if (bRollRotation) - { - m_RotationCursorOffset.x += m_MouseDeltaXScaled; - float fRotationAmount = m_RotationCursorOffset.x * -m_RotationRollScalar; - - Quaternion qOffsetRotation = Quaternion.AngleAxis(fRotationAmount, m_SurfaceForward); - Quaternion qNewRotation = qOffsetRotation * m_RotationOrigin; - m_SketchSurface.transform.rotation = qNewRotation; - - m_RotationRollActive = true; - m_RotationCursor.gameObject.SetActive(false); - } - else - { - //update offset with mouse movement - m_RotationCursorOffset.x += m_MouseDeltaXScaled; - m_RotationCursorOffset.y += m_MouseDeltaYScaled; - - //get offset in model space - Vector3 vSurfaceBounds = m_SketchSurface.transform.localScale * 0.5f; - m_RotationCursorOffset.x = Mathf.Clamp(m_RotationCursorOffset.x, -vSurfaceBounds.x, vSurfaceBounds.x); - m_RotationCursorOffset.y = Mathf.Clamp(m_RotationCursorOffset.y, -vSurfaceBounds.y, vSurfaceBounds.y); - float fCursorOffsetDist = m_RotationCursorOffset.magnitude; - float fMaxCursorOffsetDist = vSurfaceBounds.x; - - //transform offset in to world space - Vector3 vTransformedOffset = m_RotationOrigin * m_RotationCursorOffset; - vTransformedOffset.Normalize(); - - //get world space rotation axis - Vector3 vSketchSurfaceRotationAxis = Vector3.Cross(vTransformedOffset, m_SurfaceForward); - vSketchSurfaceRotationAxis.Normalize(); - - //amount to rotate is determined by offset distance from origin - float fSketchSurfaceRotationAngle = Mathf.Min(fCursorOffsetDist / fMaxCursorOffsetDist, 1.0f); - fSketchSurfaceRotationAngle *= m_RotationMaxAngle; - - //set new surface rotation by combining base rotation with angle/axis rotation - Quaternion qOffsetRotation = Quaternion.AngleAxis(fSketchSurfaceRotationAngle, vSketchSurfaceRotationAxis); - Quaternion qNewRotation = qOffsetRotation * m_RotationOrigin; - m_SketchSurface.transform.rotation = qNewRotation; - - //set position of rotation cursor - Vector3 vNewTransformedOffset = qNewRotation * m_RotationCursorOffset; - m_RotationCursor.transform.position = m_SketchSurface.transform.position + vNewTransformedOffset; - m_RotationCursor.transform.rotation = qNewRotation; - - //set position of guide lines - Vector2 vToCenter = m_RotationCursorOffset; - vToCenter.Normalize(); - float fOffsetAngle = Vector2.Angle(vToCenter, Vector2.up); - m_RotationCursor.PositionCursorLines(m_SketchSurface.transform.position, m_SketchSurface.transform.forward, fOffsetAngle, vSurfaceBounds.x * 2.0f); - } - } - else - { - float fCurrentTime = Time.realtimeSinceStartup; - if (fCurrentTime - m_RotationResetTapTime < m_DoubleTapWindow) - { - //reset drawing surface rotation - m_SketchSurface.transform.rotation = Quaternion.identity; - } - m_RotationResetTapTime = fCurrentTime; - - m_SurfaceForward = m_SketchSurface.transform.forward; - m_SurfaceRight = m_SketchSurface.transform.right; - m_SurfaceUp = m_SketchSurface.transform.up; - - if (!m_RotationRollActive && m_AutoOrientAfterRotation && m_SketchSurfacePanel.IsSketchSurfaceToolActive()) - { - //get possible auto rotations - Quaternion qQuatUp = OrientSketchSurfaceToUp(); - Quaternion qQuatForward = OrientSketchSurfaceToForward(); - - //get the angle between our current and desired auto-rotation - float toUpAngle = Quaternion.Angle(qQuatUp, m_SketchSurface.transform.rotation); - float toForwardAngle = Quaternion.Angle(qQuatForward, m_SketchSurface.transform.rotation); - - //set our new rotation to be whichever autorotation is closeset - Quaternion qNewRotation; - if (Mathf.Abs(toUpAngle) < Mathf.Abs(toForwardAngle)) - { - qNewRotation = qQuatUp; - } - else - { - qNewRotation = qQuatForward; - } - - //update the sketch surface - m_SketchSurface.transform.rotation = qNewRotation; - - m_SurfaceForward = m_SketchSurface.transform.forward; - m_SurfaceRight = m_SketchSurface.transform.right; - m_SurfaceUp = m_SketchSurface.transform.up; - } - - SwitchState(InputState.Standard); - } - } - - void UpdateHeadLockInput() - { - if (InputManager.m_Instance.GetCommand(InputManager.SketchCommands.LockToHead)) - { - //compute new position/orientation of sketch surface - Vector3 vTransformedOffset = m_CurrentHeadOrientation * m_SurfaceLockOffset; - Vector3 vSurfacePos = m_CurrentGazeRay.origin + vTransformedOffset; - - Quaternion qDiff = m_CurrentHeadOrientation * Quaternion.Inverse(m_SurfaceLockBaseHeadRotation); - Quaternion qNewSurfaceRot = qDiff * m_SurfaceLockBaseSurfaceRotation; - - m_SketchSurface.transform.position = vSurfacePos; - m_SketchSurface.transform.rotation = qNewSurfaceRot; - } - else - { - m_SurfaceForward = m_SketchSurface.transform.forward; - m_SurfaceRight = m_SketchSurface.transform.right; - m_SurfaceUp = m_SketchSurface.transform.up; - - SwitchState(InputState.Standard); - } - } - - void UpdateControllerLock() - { - if (InputManager.m_Instance.GetCommand(InputManager.SketchCommands.LockToController)) - { - //compute new position/orientation of sketch surface - Vector3 vControllerDiff = InputManager.m_Instance.GetControllerPosition(m_SurfaceLockActingController) - m_SurfaceLockBaseControllerPosition; - m_SketchSurface.transform.position = m_SurfaceLockBaseSurfacePosition + (vControllerDiff * m_SurfaceLockControllerScalar); - - Quaternion qDiff = InputManager.m_Instance.GetControllerRotation(m_SurfaceLockActingController) * Quaternion.Inverse(m_SurfaceLockBaseControllerRotation); - m_SketchSurface.transform.rotation = qDiff * m_SurfaceLockBaseSurfaceRotation; - } - else - { - m_SurfaceForward = m_SketchSurface.transform.forward; - m_SurfaceRight = m_SketchSurface.transform.right; - m_SurfaceUp = m_SketchSurface.transform.up; - - SwitchState(InputState.Standard); - } - } - - void UpdatePushPullInput() - { - bool bRotationActive = InputManager.m_Instance.GetCommand(InputManager.SketchCommands.PivotRotation); - bool bInputActive = InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Activate); - bool bAltInputActive = InputManager.m_Instance.GetCommand(InputManager.SketchCommands.AltActivate); - - if (bRotationActive && bInputActive) - { - SwitchState(InputState.Rotation); - } - else if (bAltInputActive) - { - Vector3 vPos = m_SketchSurface.transform.position; - float fBigDiff = Mathf.Abs(m_MouseDeltaXScaled) > Mathf.Abs(m_MouseDeltaYScaled) ? -m_MouseDeltaXScaled : m_MouseDeltaYScaled; - vPos += Vector3.forward * fBigDiff; - - m_SketchSurface.transform.position = vPos; - } - else - { - SwitchState(InputState.Standard); - } - } - - void UpdateSaveInput() - { - if (!InputManager.m_Instance.GetKeyboardShortcut(InputManager.KeyboardShortcut.Save)) - { - SwitchState(InputState.Standard); - } - } - - void UpdateLoadInput() - { - if (!InputManager.m_Instance.GetKeyboardShortcut(InputManager.KeyboardShortcut.Load)) - { - SwitchState(InputState.Standard); - } - } - - void OnBrushSetToDefault() - { - BrushDescriptor rDefaultBrush = BrushCatalog.m_Instance.DefaultBrush; - PointerManager.m_Instance.SetBrushForAllPointers(rDefaultBrush); - PointerManager.m_Instance.SetAllPointersBrushSize01(0.5f); - PointerManager.m_Instance.MarkAllBrushSizeUsed(); - } - - public void AssignControllerMaterials(InputManager.ControllerName controller) - { - ControllerGeometry geometry = InputManager.GetControllerGeometry(controller); - - // Start from a clean state - geometry.ResetAll(); - - // If the tutorial is enabled, override all materials. - if (TutorialManager.m_Instance.TutorialActive()) - { - InputManager.m_Instance - .GetControllerTutorial(controller) - ?.AssignControllerMaterials(controller); - return; - } - - // If we're grabbing the world, get the materials from the world transform panel. - if (m_GrabBrush.grabbingWorld && controller == InputManager.ControllerName.Brush) - { - TrTransform scenePose = App.Scene.Pose; - if (scenePose.scale != 1 || scenePose.translation != Vector3.zero - || scenePose.rotation != Quaternion.identity) - { - geometry.ShowWorldTransformReset(); - } - return; - } - else if (m_GrabWand.grabbingWorld && controller == InputManager.ControllerName.Wand) - { - TrTransform scenePose = App.Scene.Pose; - if (scenePose.scale != 1 || scenePose.translation != Vector3.zero - || scenePose.rotation != Quaternion.identity) - { - geometry.ShowWorldTransformReset(); - } - return; - } - - // Not grabbing the world, so see if we're grabbing a widget. - if (m_GrabWidgetState != GrabWidgetState.None) - { - m_CurrentGrabWidget.AssignControllerMaterials(controller); - return; - } - - // See if we're highlighting a widget and if that matters. - if (m_CurrentGrabWidget != null && m_CurrentGrabWidget.HasHoverInteractions()) - { - m_CurrentGrabWidget.AssignHoverControllerMaterials(controller); - return; - } - - // Not grabbing the world or a widget, see if we're interacting with a panel. - if (controller == InputManager.ControllerName.Brush && m_CurrentGazeObject != -1) - { - BasePanel panel = m_PanelManager.GetPanel(m_CurrentGazeObject); - panel.AssignControllerMaterials(controller); - return; - } - - // Defaults. - if (controller == InputManager.ControllerName.Wand) - { - if (App.CurrentState != App.AppState.Standard || m_PanelManager.IntroSketchbookMode) - { - // If app is not in standard mode, the actions represented by subsequent material - // assigments cannot be taken. - return; - } - bool creatingStroke = PointerManager.m_Instance.IsMainPointerCreatingStroke(); - bool allowPainting = App.Instance.IsInStateThatAllowsPainting(); - - InputManager.Wand.Geometry.ShowRotatePanels(); - InputManager.Wand.Geometry.ShowUndoRedo(CanUndo() && !creatingStroke && allowPainting, - CanRedo() && !creatingStroke && allowPainting); - } - - // Show the pin cushion icon on the button if it's available. - if (controller == InputManager.ControllerName.Brush && CanUsePinCushion()) - { - InputManager.Brush.Geometry.ShowPinCushion(); - } - - // Finally, override with tools. - m_SketchSurfacePanel.AssignControllerMaterials(controller); - } - - public float GetControllerPadShaderRatio( - InputManager.ControllerName controller, VrInput input) - { - // If we're interacting with a panel, get touch ratio from the panel. - if (controller == InputManager.ControllerName.Brush && m_CurrentGazeObject != -1) - { - BasePanel panel = m_PanelManager.GetPanel(m_CurrentGazeObject); - return panel.GetControllerPadShaderRatio(controller); - } - return SketchSurfacePanel.m_Instance.GetCurrentToolSizeRatio(controller, input); - } - - void SwitchState(InputState rDesiredState) - { - //exit current state - switch (m_CurrentInputState) - { - case InputState.Pan: - m_TransformGizmoScript.ResetTransform(); - break; - case InputState.PushPull: - m_TransformGizmoScript.ResetTransform(); - break; - case InputState.Rotation: - m_RotationRollActive = false; - m_RotationIcon.SetActive(false); - m_RotationCursor.gameObject.SetActive(false); - break; - } - - bool bSketchSurfaceToolActive = m_SketchSurfacePanel.IsSketchSurfaceToolActive(); - - //enter new state - switch (rDesiredState) - { - case InputState.Pan: - m_TransformGizmoScript.SetTransformForPan(); - break; - case InputState.PushPull: - m_TransformGizmoScript.SetTransformForPushPull(); - break; - case InputState.Rotation: - if (bSketchSurfaceToolActive) - { - m_SketchSurface.transform.position = PointerManager.m_Instance.MainPointer.transform.position; - m_SketchSurfacePanel.ResetReticleOffset(); - } - m_RotationOrigin = m_SketchSurface.transform.rotation; - m_RotationCursorOffset = Vector2.zero; - m_RotationCursor.transform.position = m_SketchSurface.transform.position; - m_RotationCursor.transform.rotation = m_SketchSurface.transform.rotation; - m_RotationCursor.ClearCursorLines(m_SketchSurface.transform.position); - m_RotationCursor.gameObject.SetActive(bSketchSurfaceToolActive); - break; - case InputState.HeadLock: - m_SurfaceLockBaseHeadRotation = m_CurrentHeadOrientation; - m_SurfaceLockBaseSurfaceRotation = m_SketchSurface.transform.rotation; - m_SurfaceLockOffset = m_SketchSurface.transform.position - m_CurrentGazeRay.origin; - m_SurfaceLockOffset = Quaternion.Inverse(m_SurfaceLockBaseHeadRotation) * m_SurfaceLockOffset; - break; - case InputState.ControllerLock: - if (bSketchSurfaceToolActive) - { - m_SketchSurface.transform.position = PointerManager.m_Instance.MainPointer.transform.position; - m_SketchSurfacePanel.ResetReticleOffset(); - } - m_SurfaceLockActingController = InputManager.m_Instance.GetDominantController(InputManager.SketchCommands.LockToController); - m_SurfaceLockBaseSurfaceRotation = m_SketchSurface.transform.rotation; - m_SurfaceLockBaseControllerRotation = InputManager.m_Instance.GetControllerRotation(m_SurfaceLockActingController); - m_SurfaceLockBaseSurfacePosition = m_SketchSurface.transform.position; - m_SurfaceLockBaseControllerPosition = InputManager.m_Instance.GetControllerPosition(m_SurfaceLockActingController); - m_SurfaceLockControllerScalar = m_SketchSurfacePanel.m_PanelSensitivity / m_SurfaceLockControllerBaseScalar; - break; - case InputState.Save: - IssueGlobalCommand(GlobalCommands.Save); - break; - case InputState.Load: - IssueGlobalCommand(GlobalCommands.Load); - break; - } - - m_CurrentInputState = rDesiredState; - } - - public void RequestPanelsVisibility(bool bVisible) - { - // Always false in viewonly mode - bVisible = m_ViewOnly ? false : bVisible; - m_PanelsVisibilityRequested = bVisible; - } - - Quaternion OrientSketchSurfaceToUp() - { - //project the world up vector on to the surface plane - Vector3 vUpOnSurfacePlane = Vector3.up - (Vector3.Dot(Vector3.up, m_SurfaceForward) * m_SurfaceForward); - vUpOnSurfacePlane.Normalize(); - - //get the angle between the surface up and the projected world up - float fUpOnSurfacePlaneAngle = Vector3.Angle(vUpOnSurfacePlane, m_SurfaceUp); - Vector3 vUpCross = Vector3.Cross(vUpOnSurfacePlane, m_SurfaceUp); - vUpCross.Normalize(); - if (Vector3.Dot(vUpCross, m_SurfaceForward) > 0.0f) - { - fUpOnSurfacePlaneAngle *= -1.0f; - } - - //rotate around the surface foward by the angle diff - Quaternion qOrientToUp = Quaternion.AngleAxis(fUpOnSurfacePlaneAngle, m_SurfaceForward); - Quaternion qNewRotation = qOrientToUp * m_SketchSurface.transform.rotation; - return qNewRotation; - } - - Quaternion OrientSketchSurfaceToForward() - { - //project the world forward vector on to the surface plane - Vector3 vForwardOnSurfacePlane = Vector3.forward - (Vector3.Dot(Vector3.forward, m_SurfaceForward) * m_SurfaceForward); - vForwardOnSurfacePlane.Normalize(); - - //get the angle between the surface up and the projected world forward - float fForwardOnSurfacePlaneAngle = Vector3.Angle(vForwardOnSurfacePlane, m_SurfaceUp); - Vector3 vUpCross = Vector3.Cross(vForwardOnSurfacePlane, m_SurfaceUp); - vUpCross.Normalize(); - if (Vector3.Dot(vUpCross, m_SurfaceForward) > 0.0f) - { - fForwardOnSurfacePlaneAngle *= -1.0f; - } - - //rotate around the surface foward by the angle diff - Quaternion qOrientToForward = Quaternion.AngleAxis(fForwardOnSurfacePlaneAngle, m_SurfaceForward); - Quaternion qNewRotation = qOrientToForward * m_SketchSurface.transform.rotation; - return qNewRotation; - } - - /// Reset the scene or the canvas, depending on the current mode - public void ResetGrabbedPose(bool everything = false) - { - //update sketch surface position with offset to sweet spot - m_SketchSurface.transform.position = m_PanelManager.GetSketchSurfaceResetPos(); - if (everything) - { - App.Scene.Pose = TrTransform.identity; - Coords.CanvasLocalPose = TrTransform.identity; - } - App.Scene.Pose = TrTransform.identity; - - //reset orientation and pointer - ResetSketchSurfaceOrientation(); - m_SketchSurfacePanel.ResetReticleOffset(); - PointerManager.m_Instance.DisablePointerPreviewLine(); - PointerManager.m_Instance.SetPointerPreviewLineDelayTimer(); - } - - public void ResetSketchSurfaceOrientation() - { - m_SketchSurface.transform.rotation = Quaternion.identity; - m_SurfaceForward = m_SketchSurface.transform.forward; - m_SurfaceRight = m_SketchSurface.transform.right; - m_SurfaceUp = m_SketchSurface.transform.up; - } - - float GetAppropriateMovementScalar() - { - switch (m_CurrentInputState) - { - case InputState.Pan: return m_PanScalar; - case InputState.Rotation: return m_RotationScalar; - case InputState.PushPull: return m_PushPullScale; - } - - return 1.0f; - } - - // TODO - it'd be great if we could disentangle this from the multicam. - IEnumerator RenderPathAndQuit() - { -#if USD_SUPPORTED - App.Instance.SetDesiredState(App.AppState.OfflineRendering); - SketchSurfacePanel.m_Instance.EnableSpecificTool(BaseTool.ToolType.MultiCamTool); - MultiCamTool multiCam = SketchSurfacePanel.m_Instance.ActiveTool as MultiCamTool; - Debug.Assert(multiCam != null); // Something's gone wrong if we've been unable to find multicam! - if (multiCam == null) - { - yield break; - } - multiCam.ExternalObjectForceCameraStyle(MultiCamStyle.Video); - MultiCamCaptureRig.ForceClippingPlanes(MultiCamStyle.Video); - // Give the video tool time to switch - TODO - be a little more graceful here - yield return new WaitForSeconds(2); - // Make sure the videos have had time to load, and set playing ones to start - while (VideoCatalog.Instance.IsScanning) - { - yield return null; - } - foreach (var widget in WidgetManager.m_Instance.VideoWidgets) - { - if (widget.VideoController.Playing) - { - widget.VideoController.Position = 0; - } - } - yield return null; - var ssMgr = MultiCamCaptureRig.ManagerFromStyle(MultiCamStyle.Video); - ssMgr.SetScreenshotResolution(App.UserConfig.Video.OfflineResolution); - multiCam.StartVideoCapture(MultiCamTool.GetSaveName(MultiCamStyle.Video), offlineRender: true); - App.Instance.FrameCountDisplay.gameObject.SetActive(true); - App.Instance.FrameCountDisplay.SetFramesTotal(VideoRecorderUtils.NumFramesInUsdSerializer); - while (VideoRecorderUtils.ActiveVideoRecording != null) - { - App.Instance.FrameCountDisplay.SetCurrentFrame( - VideoRecorderUtils.ActiveVideoRecording.FrameCount); - yield return null; - } - ssMgr.SetScreenshotResolution(App.UserConfig.Video.Resolution); -#else - Debug.LogError("Render path requires USD support"); - yield return null; -#endif - QuitApp(); - } - - IEnumerator ExportListAndQuit() - { - App.Config.m_ForceDeterministicBirthTimeForExport = true; - List filesToExport = new List(); - foreach (string filePattern in App.Config.m_FilePatternsToExport) - { - bool absolute = Path.IsPathRooted(filePattern); - string directory = absolute ? Path.GetDirectoryName(filePattern) : App.UserSketchPath(); - string filename = Path.GetFileName(filePattern); - var tiltFiles = Directory.GetFiles(directory, filename); - filesToExport.AddRange(tiltFiles); - // Also look at .tilt files which have been unzipped into directory format - var tiltDirs = Directory.GetDirectories(directory, filename) - .Where(n => n.EndsWith(".tilt")); - filesToExport.AddRange(tiltDirs); - } - - using (var coroutine = LoadAndExportList(filesToExport)) - { - while (coroutine.MoveNext()) - { - yield return coroutine.Current; - } - } - QuitApp(); - } - - void QuitApp() - { - // We're done! Quit! -#if UNITY_EDITOR - UnityEditor.EditorApplication.isPlaying = false; -#else - Application.Quit(); -#endif - } - - // This coroutine must be run to completion or disposed. - IEnumerator LoadAndExportList(List filenames) - { - foreach (var filename in filenames) - { - using (var coroutine = LoadAndExport(filename)) - { - while (coroutine.MoveNext()) - { - yield return coroutine.Current; - } - } - } - } - - // This coroutine must be run to completion or disposed. - IEnumerator LoadAndExportAll() - { - SketchSet sketchSet = SketchCatalog.m_Instance.GetSet(SketchSetType.User); - for (int i = 0; i < SketchCatalog.m_Instance.GetSet(SketchSetType.User).NumSketches; ++i) - { - SceneFileInfo rInfo = sketchSet.GetSketchSceneFileInfo(i); - using (var coroutine = LoadAndExport(rInfo.FullPath)) - { - while (coroutine.MoveNext()) - { - yield return coroutine.Current; - } - } - } - } - - /// Loads a .tilt file completely. - /// This may be slightly buggy; it's not currently used for production. - /// This coroutine must be run to completion or disposed. - public IEnumerable LoadTiltFile(string filename) - { - using (var unused = new SceneSettings.RequestInstantSceneSwitch()) - { - IssueGlobalCommand( - GlobalCommands.LoadNamedFile, - iParam1: (int)LoadSpeed.Quick, sParam: filename); - yield return null; - while (App.Instance.IsLoading()) - { - yield return null; - } - - // I don't know why App.Instance.IsLoading() doesn't cover this, but it doesn't. - while (m_WidgetManager.CreatingMediaWidgets) - { - yield return null; - } - while (WidgetManager.m_Instance.AreMediaWidgetsStillLoading()) - { - yield return null; - } - - // This is kind of a hack. - // Despite the RequestInstantSceneSwitch above, I think scene colors still require - // a few frames to settle; also, GrabWidgets need to register themselves on the - // first frame, etc. - for (int i = 0; i < 10; ++i) - { - yield return null; - } - } - } - - // This coroutine must be run to completion or disposed. - IEnumerator LoadAndExport(string filename) - { - foreach (var val in LoadTiltFile(filename)) - { - yield return val; - } - using (var coroutine = ExportCoroutine()) - { - while (coroutine.MoveNext()) - { - yield return coroutine.Current; - } - } - } - - IEnumerator ExportCoroutine() - { - return OverlayManager.m_Instance.RunInCompositor( - OverlayType.Export, () => - { - // Sort of a kludge: put stuff back into the main canvas - SelectionManager.m_Instance.ClearActiveSelection(); - Export.ExportScene(); - }, 0.25f, false, true); - } - - private void SaveModel() - { -#if USD_SUPPORTED - var current = SaveLoadScript.m_Instance.SceneFile; - string basename = (current.Valid) - ? Path.GetFileNameWithoutExtension(current.FullPath) - : "Untitled"; - string directoryName = FileUtils.GenerateNonexistentFilename( - App.ModelLibraryPath(), basename, ""); - - string usdname = Path.Combine(directoryName, basename + ".usd"); - // TODO: export selection only, though this is still only experimental. The blocking - // issue to implement this is that the export collector needs to expose this as an option. - // - // SelectionManager.m_Instance.HasSelection - // ? SelectionManager.m_Instance.SelectedStrokes - // : null - ExportUsd.ExportPayload(usdname); - OutputWindowScript.m_Instance.CreateInfoCardAtController( - InputManager.ControllerName.Brush, "Model created!"); -#endif - } - - /// Generates a view from the previous thumbnail viewpoint. - public void GenerateReplacementSaveIcon() - { - if (SaveLoadScript.m_Instance.LastThumbnail_SS.HasValue) - { - TrTransform thumbnailInGlobalSpace = App.Scene.Pose * - SaveLoadScript.m_Instance.LastThumbnail_SS.Value; - - m_SaveIconTool.ProgrammaticCaptureSaveIcon(thumbnailInGlobalSpace.translation, - thumbnailInGlobalSpace.rotation); - } - else - { - GenerateBestGuessSaveIcon(); - } - } - - public void GenerateBestGuessSaveIcon() - { - TrTransform camXform = GenerateBestGuessSaveIconTransform(); - m_SaveIconTool.ProgrammaticCaptureSaveIcon(camXform.translation, camXform.rotation); - } - - /// This positions the save icon camera at the user's head position, and faces it towards - /// the most recent strokes the user has created. - /// If there are no strokes, it faces towards the 'most recent' models. - /// Sadly we cannot really mix the two as we don't know when the models were instantiated. - public TrTransform GenerateBestGuessSaveIconTransform(int itemsToEnumerate = 0) - { - if (itemsToEnumerate == 0) - { - itemsToEnumerate = m_NumStrokesForSaveIcon; - } - int startIndex = Mathf.Max(0, SketchMemoryScript.AllStrokesCount() - itemsToEnumerate); - var lastFewStrokes = SketchMemoryScript.AllStrokes().Skip(startIndex).ToArray(); - - Bounds bounds; - if (lastFewStrokes.Length > 0) - { - bounds = new Bounds(lastFewStrokes.First().m_ControlPoints.First().m_Pos, Vector3.zero); - foreach (var stroke in lastFewStrokes.Skip(1)) - { - bounds.Encapsulate(stroke.m_ControlPoints.First().m_Pos); - bounds.Encapsulate(stroke.m_ControlPoints.Last().m_Pos); - } - } - else - { - // If we have no strokes, just use the aggregates bounding boxes of the blocks models. - var models = m_WidgetManager.ModelWidgets.ToArray(); - // we should always have models to get here, but just in case... - if (models.Length > 0) - { - startIndex = Mathf.Max(0, models.Length - itemsToEnumerate); - bounds = models[startIndex].WorldSpaceBounds; - for (int i = startIndex + 1; i < models.Length; ++i) - { - bounds.Encapsulate(models[i].WorldSpaceBounds); - } - } - else - { - bounds = new Bounds(new Vector3(0, 1, -100000), Vector3.one); // some point in the distance - } - } - - Vector3 camPos = ViewpointScript.Head.position; - Vector3 worldPos = App.Scene.Pose.MultiplyPoint(bounds.center); - Quaternion direction = Quaternion.LookRotation(worldPos - camPos); - return TrTransform.TR(camPos, direction); - } - - - public void GenerateBoundingBoxSaveIcon() - { - Vector3 vNewCamPos; - { - Bounds rCanvasBounds = App.Scene.AllCanvases - .Select(canvas => canvas.GetCanvasBoundingBox()) - .Aggregate((b1, b2) => - { - b1.Encapsulate(b2); - return b1; - }); - - //position the camera at the center of the canvas bounds - vNewCamPos = rCanvasBounds.center; - - //back the camera up, along -z until we can see the extent of the bounds - float fCanvasWidth = rCanvasBounds.max.x - rCanvasBounds.min.x; - float fCanvasHeight = rCanvasBounds.max.y - rCanvasBounds.min.y; - float fLargerExtent = Mathf.Max(fCanvasHeight, fCanvasWidth); - - //half fov for camera - float fHalfFOV = m_SaveIconTool.ScreenshotManager.LeftEye.fieldOfView * 0.5f; - - //TODO: find the real reason this isn't working as it should - float fMagicNumber = 1.375f; - - //set new cam position and zero out orientation - float fBackupDistance = (fLargerExtent * 0.5f) - * Mathf.Tan(Mathf.Deg2Rad * fHalfFOV) * fMagicNumber; - vNewCamPos.z = rCanvasBounds.min.z - fBackupDistance; - } - - m_SaveIconTool.ProgrammaticCaptureSaveIcon(vNewCamPos, Quaternion.identity); - } - - private void MergeBrushStrokes(SceneFileInfo fileInfo) - { - m_PanelManager.ToggleSketchbookPanels(isLoadingSketch: true); - PointerManager.m_Instance.EnablePointerStrokeGeneration(true); - if (SaveLoadScript.m_Instance.Load(fileInfo, true)) - { - SketchMemoryScript.m_Instance.SetPlaybackMode(m_SketchPlaybackMode, m_DefaultSketchLoadSpeed); - SketchMemoryScript.m_Instance.BeginDrawingFromMemory(bDrawFromStart: true, false, false); - // the order of these two lines are important as ExitIntroSketch is setting the - // color of the pointer and we need the color to be set before we go to the Loading - // state. App script's ShouldTintControllers allow the controller to be tinted only - // when the app is in the standard mode. That was there to prevent the controller color - // from flickering while in the intro mode. - App.Instance.ExitIntroSketch(); - App.Instance.SetDesiredState(App.AppState.QuickLoad); - } - } - - public void LoadSketch(SceneFileInfo fileInfo, bool quickload = false, bool additive = false) - { - LightsControlScript.m_Instance.DiscoMode = false; - m_WidgetManager.FollowingPath = false; - m_WidgetManager.CameraPathsVisible = false; - m_WidgetManager.DestroyAllWidgets(); - m_PanelManager.ToggleSketchbookPanels(isLoadingSketch: true); - ResetGrabbedPose(everything: true); - PointerManager.m_Instance.EnablePointerStrokeGeneration(true); - if (SaveLoadScript.m_Instance.Load(fileInfo, additive)) - { - SketchMemoryScript.m_Instance.SetPlaybackMode(m_SketchPlaybackMode, m_DefaultSketchLoadSpeed); - SketchMemoryScript.m_Instance.BeginDrawingFromMemory(bDrawFromStart: true); - // the order of these two lines are important as ExitIntroSketch is setting the - // color of the pointer and we need the color to be set before we go to the Loading - // state. App script's ShouldTintControllers allow the controller to be tinted only - // when the app is in the standard mode. That was there to prevent the controller color - // from flickering while in the intro mode. - App.Instance.ExitIntroSketch(); - App.Instance.SetDesiredState(quickload ? App.AppState.QuickLoad : App.AppState.Loading); - } - QualityControls.m_Instance.ResetAutoQuality(); - m_WidgetManager.ValidateCurrentCameraPath(); - } - - public void IssueGlobalCommand(GlobalCommands rEnum, int iParam1 = -1, - int iParam2 = -1, string sParam = null) - { - switch (rEnum) - { - - // Keyboard command, for debugging and emergency use. - case GlobalCommands.Save: - { - if (!FileUtils.CheckDiskSpaceWithError(App.UserSketchPath())) - { - return; - } - // Disable active selection before saving. - // This looks fishy, here's what's going on: When an object is selected and it has moved, the - // the user is observing the selection canvas in the HMD, but we will be saving the main canvas. - // Because they haven't deselected yet, the selection canvas and the main canvas are out of sync - // so the strokes that will be saved will not match what the user sees. - // - // Here we deselect to force the main canvas to sync with the selection canvas, which is more - // correct from the user's perspective. Push the deselect operation onto the stack so the user - // can undo it after save, if desired. - SelectionManager.m_Instance.ClearActiveSelection(); - GenerateReplacementSaveIcon(); - if (iParam1 == -1) - { - if (iParam2 == 1) - { - // Do a save in Tiltasaurus mode, which creates a new filename prefixed with - // "Tiltasaurus_" and the current prompt. Also, don't eat gaze input so that the - // Tiltasaurus prompt stays open. - StartCoroutine(SaveLoadScript.m_Instance.SaveOverwrite(tiltasaurusMode: true)); - } - else - { - StartCoroutine(SaveLoadScript.m_Instance.SaveOverwrite()); - EatGazeObjectInput(); - } - } - else - { - StartCoroutine(SaveLoadScript.m_Instance.SaveMonoscopic(iParam1)); - } - break; - } - case GlobalCommands.SaveNew: - { - if (!FileUtils.CheckDiskSpaceWithError(App.UserSketchPath())) - { - return; - } - if (iParam1 == 1) - { - GenerateBoundingBoxSaveIcon(); - } - StartCoroutine(SaveLoadScript.m_Instance.SaveNewName()); - EatGazeObjectInput(); - break; - } - case GlobalCommands.SaveAndUpload: - { - if (!FileUtils.CheckDiskSpaceWithError(App.UserSketchPath())) - { - Debug.LogError("SaveAndUpload: Disk space error"); - return; - } - SelectionManager.m_Instance.ClearActiveSelection(); - m_PanelManager.GetPanel(m_CurrentGazeObject).CreatePopUp( - GlobalCommands.UploadToGenericCloud, (int)Cloud.None, -1); - EatGazeObjectInput(); - break; - } - case GlobalCommands.ExportAll: - { - StartCoroutine(LoadAndExportAll()); - break; - } - // Glen Keane request: a way to draw guidelines that can be toggled on and off - // at runtime. - case GlobalCommands.DraftingVisibility: - { - if (!Enum.IsDefined(typeof(DraftingVisibilityOption), iParam1)) - { - Debug.LogError("Unknown draft visibility value: " + iParam1); - return; - } - DraftingVisibilityOption option = (DraftingVisibilityOption)iParam1; - if (option != m_DraftingVisibility) - { - m_DraftingVisibility = option; - UpdateDraftingVisibility(); - } - break; - } - case GlobalCommands.MergeBrushStrokes: - { - // TODO Refactor with Load below - var index = iParam1; - var sketchSetType = (SketchSetType)iParam2; - SketchSet sketchSet = SketchCatalog.m_Instance.GetSet(sketchSetType); - SceneFileInfo rInfo = sketchSet.GetSketchSceneFileInfo(index); - if (rInfo != null) - { - MergeBrushStrokes(rInfo); - if (m_ControlsType != ControlsType.ViewingOnly) - { - EatGazeObjectInput(); - } - } - break; - } - case GlobalCommands.Load: - { - var index = iParam1; - var sketchSetType = (SketchSetType)iParam2; - SketchSet sketchSet = SketchCatalog.m_Instance.GetSet(sketchSetType); - SceneFileInfo rInfo = sketchSet.GetSketchSceneFileInfo(index); - if (rInfo != null) - { - LoadSketch(rInfo); - if (m_ControlsType != ControlsType.ViewingOnly) - { - EatGazeObjectInput(); - } - } - break; - } - case GlobalCommands.LoadNamedFile: - LoadNamed(sParam, iParam1 == (int)LoadSpeed.Quick, iParam2 != -1); - break; - case GlobalCommands.NewSketch: - NewSketch(fade: true); - Vector3 vTrashSoundPos = m_CurrentGazeRay.origin; - if (App.VrSdk.GetControllerDof() == VrSdk.DoF.Six) - { - vTrashSoundPos = InputManager.m_Instance.GetControllerPosition( - InputManager.ControllerName.Wand); - } - AudioManager.m_Instance.PlayTrashSound(vTrashSoundPos); - PromoManager.m_Instance.RequestAdvancedPanelsPromo(); - break; - case GlobalCommands.SymmetryPlane: - if (PointerManager.m_Instance.CurrentSymmetryMode != SymmetryMode.SinglePlane) - { - PointerManager.m_Instance.SetSymmetryMode(SymmetryMode.SinglePlane); - ControllerConsoleScript.m_Instance.AddNewLine("Mirror Enabled"); - } - else - { - PointerManager.m_Instance.SetSymmetryMode(SymmetryMode.None); - ControllerConsoleScript.m_Instance.AddNewLine("Mirror Off"); - } - break; - case GlobalCommands.MultiMirror: - if (PointerManager.m_Instance.CurrentSymmetryMode != SymmetryMode.MultiMirror) - { - PointerManager.m_Instance.SetSymmetryMode(SymmetryMode.MultiMirror); - ControllerConsoleScript.m_Instance.AddNewLine("Symmetry Enabled"); - } - else - { - PointerManager.m_Instance.SetSymmetryMode(SymmetryMode.None); - ControllerConsoleScript.m_Instance.AddNewLine("Symmetry Off"); - } - InputManager.m_Instance.TriggerHaptics(InputManager.ControllerName.Brush, 0.1f); - break; - case GlobalCommands.SymmetryTwoHanded: - if (PointerManager.m_Instance.CurrentSymmetryMode != SymmetryMode.TwoHanded) - { - PointerManager.m_Instance.SetSymmetryMode(SymmetryMode.TwoHanded); - ControllerConsoleScript.m_Instance.AddNewLine("Symmetry Enabled"); - } - else - { - PointerManager.m_Instance.SetSymmetryMode(SymmetryMode.None); - ControllerConsoleScript.m_Instance.AddNewLine("Symmetry Off"); - } - InputManager.m_Instance.TriggerHaptics(InputManager.ControllerName.Brush, 0.1f); - break; - case GlobalCommands.StraightEdge: - PointerManager.m_Instance.StraightEdgeModeEnabled = !PointerManager.m_Instance.StraightEdgeModeEnabled; - if (PointerManager.m_Instance.StraightEdgeModeEnabled) - { - ControllerConsoleScript.m_Instance.AddNewLine("Straight Edge On"); - } - else - { - ControllerConsoleScript.m_Instance.AddNewLine("Straight Edge Off"); - } - break; - case GlobalCommands.AutoOrient: - m_AutoOrientAfterRotation = !m_AutoOrientAfterRotation; - if (m_AutoOrientAfterRotation) - { - ControllerConsoleScript.m_Instance.AddNewLine("Auto-Orient On"); - } - else - { - ControllerConsoleScript.m_Instance.AddNewLine("Auto-Orient Off"); - } - break; - case GlobalCommands.Undo: - SketchMemoryScript.m_Instance.StepBack(); - break; - case GlobalCommands.Redo: - SketchMemoryScript.m_Instance.StepForward(); - break; - case GlobalCommands.AudioVisualization: // Intentionally blank. - break; - case GlobalCommands.ResetAllPanels: - m_PanelManager.ResetWandPanelsConfiguration(); - EatGazeObjectInput(); - break; - case GlobalCommands.SketchOrigin: - m_SketchSurfacePanel.EnableSpecificTool(BaseTool.ToolType.SketchOrigin); - EatGazeObjectInput(); - break; - case GlobalCommands.ViewOnly: - m_ViewOnly = !m_ViewOnly; - RequestPanelsVisibility(!m_ViewOnly); - PointerManager.m_Instance.RequestPointerRendering(!m_ViewOnly); - // TODO - decide if this is a permanent change - // With this line, you can't set a tool such as fly or teleport - // and switch to View Only mode as the mode change disables all tools - //m_SketchSurface.SetActive(!m_ViewOnly); - m_Decor.SetActive(!m_ViewOnly); - break; - case GlobalCommands.SaveGallery: - m_SketchSurfacePanel.EnableSpecificTool(BaseTool.ToolType.SaveIconTool); - break; - case GlobalCommands.DropCam: - // Want to enable this if in monoscopic or VR modes. - // TODO: seems odd to tie this switch to the controller type, should be based on some - // other build-time configuration setting. - if (App.VrSdk.GetControllerDof() != VrSdk.DoF.None) - { - m_DropCam.Show(!m_DropCam.gameObject.activeSelf); - } - break; - case GlobalCommands.AnalyticsEnabled_Deprecated: - break; - case GlobalCommands.ToggleAutosimplification: - QualityControls.AutosimplifyEnabled = !QualityControls.AutosimplifyEnabled; - break; - case GlobalCommands.Credits: - LoadSketch(new DiskSceneFileInfo(m_CreditsSketchFilename, embedded: true, readOnly: true)); - EatGazeObjectInput(); - break; - case GlobalCommands.AshleysSketch: - LoadSketch(new DiskSceneFileInfo(m_AshleysSketchFilename, embedded: true, readOnly: true)); - EatGazeObjectInput(); - break; - case GlobalCommands.FAQ: - OpenURLAndInformUser(m_HelpCenterURL); - break; - case GlobalCommands.ReleaseNotes: - OpenURLAndInformUser(m_ReleaseNotesURL); - break; - case GlobalCommands.ExportRaw: - if (!FileUtils.CheckDiskSpaceWithError(App.UserExportPath())) - { - return; - } - EatGazeObjectInput(); - StartCoroutine(ExportCoroutine()); - break; - case GlobalCommands.IRC: - if (m_IRCChatWidget == null) - { - GameObject widgetobject = (GameObject)Instantiate(m_IRCChatPrefab); - widgetobject.transform.parent = App.Instance.m_RoomTransform; - m_IRCChatWidget = widgetobject.GetComponent(); - m_IRCChatWidget.Show(true); - } - else - { - m_IRCChatWidget.Show(false); - m_IRCChatWidget = null; - } - break; - case GlobalCommands.YouTubeChat: - if (m_YouTubeChatWidget == null) - { - GameObject widgetobject = (GameObject)Instantiate(m_YouTubeChatPrefab); - widgetobject.transform.parent = App.Instance.m_RoomTransform; - m_YouTubeChatWidget = widgetobject.GetComponent(); - m_YouTubeChatWidget.Show(true); - } - else - { - m_YouTubeChatWidget.Show(false); - m_YouTubeChatWidget = null; - } - break; - case GlobalCommands.CameraOptions: - // If we're switching in to Camera mode, make sure Multicam is selected. - if (!m_PanelManager.CameraActive()) - { - SketchSurfacePanel.m_Instance.EnableSpecificTool(BaseTool.ToolType.MultiCamTool); - } - m_PanelManager.ToggleCameraPanels(); - PointerManager.m_Instance.EatLineEnabledInput(); - SketchSurfacePanel.m_Instance.EatToolsInput(); - break; - case GlobalCommands.ShowSketchFolder: - { - var index = iParam1; - var sketchSetType = (SketchSetType)iParam2; - SketchSet sketchSet = SketchCatalog.m_Instance.GetSet(sketchSetType); - SceneFileInfo rInfo = sketchSet.GetSketchSceneFileInfo(index); - EatGazeObjectInput(); - //launch external window and tell the user we did so - //this call is windows only - if ((Application.platform == RuntimePlatform.WindowsPlayer) || - (Application.platform == RuntimePlatform.WindowsEditor)) - { - OutputWindowScript.m_Instance.CreateInfoCardAtController( - InputManager.ControllerName.Brush, - kRemoveHeadsetFyi, fPopScalar: 0.5f); - System.Diagnostics.Process.Start("explorer.exe", - "/select," + rInfo.FullPath); - } - break; - } - case GlobalCommands.About: - OpenURLAndInformUser(m_ThirdPartyNoticesURL); - break; - case GlobalCommands.StencilsDisabled: - SketchMemoryScript.m_Instance.PerformAndRecordCommand(new StencilsVisibleCommand()); - break; - case GlobalCommands.StraightEdgeMeterDisplay: - PointerManager.m_Instance.StraightEdgeGuide.FlipMeter(); - break; - case GlobalCommands.Sketchbook: - m_PanelManager.ToggleSketchbookPanels(); - PointerManager.m_Instance.EatLineEnabledInput(); - SketchSurfacePanel.m_Instance.EatToolsInput(); - break; - case GlobalCommands.StraightEdgeShape: - // Previously experimental mode only. - // Untested and currently untriggerable. - PointerManager.m_Instance.StraightEdgeGuide.SetTempShape( - (StraightEdgeGuideScript.Shape)iParam1); - break; - case GlobalCommands.DeleteSketch: - { - var sketchSetType = (SketchSetType)iParam2; - SketchSet sketchSet = SketchCatalog.m_Instance.GetSet(sketchSetType); - sketchSet.DeleteSketch(iParam1); - DismissPopupOnCurrentGazeObject(false); - break; - } - case GlobalCommands.RenameSketch: - { - var sketchSetType = (SketchSetType)iParam2; - SketchSet sketchSet = SketchCatalog.m_Instance.GetSet(sketchSetType); - if (sketchSetType == SketchSetType.User) - { - sketchSet.RenameSketch(iParam1, KeyboardPopUpWindow.m_LastInput); - } - DismissPopupOnCurrentGazeObject(false); - break; - } - case GlobalCommands.RenameLayer: - { - var layer = App.Scene.GetCanvasByLayerIndex(iParam1); - App.Scene.RenameLayer(layer, KeyboardPopUpWindow.m_LastInput); - DismissPopupOnCurrentGazeObject(false); - break; - } - case GlobalCommands.EditMultiplayerRoomName: - { - var panel = (MultiplayerPanel)m_PanelManager.GetActivePanelByType(BasePanel.PanelType.Multiplayer); - panel.RoomName = KeyboardPopUpWindow.m_LastInput; - DismissPopupOnCurrentGazeObject(false); - break; - } - case GlobalCommands.EditMultiplayerNickName: - { - var panel = (MultiplayerPanel)m_PanelManager.GetActivePanelByType(BasePanel.PanelType.Multiplayer); - panel.NickName = KeyboardPopUpWindow.m_LastInput; - DismissPopupOnCurrentGazeObject(false); - break; - } - case GlobalCommands.ShowWindowGUI: - break; - case GlobalCommands.Disco: - LightsControlScript.m_Instance.DiscoMode = !LightsControlScript.m_Instance.DiscoMode; - break; - case GlobalCommands.AccountInfo: break; // Intentionally blank. - case GlobalCommands.LoginToGenericCloud: - { - var ident = App.GetIdentity((Cloud)iParam1); - if (!ident.LoggedIn) { ident.LoginAsync(); } - // iParam2 is being used as a UX flag. If not set to the default, it will cause the UI - // to lose focus. - if (iParam2 != -1) { EatGazeObjectInput(); } - break; - } - case GlobalCommands.LogOutOfGenericCloud: - { - var ident = App.GetIdentity((Cloud)iParam1); - if (ident.LoggedIn) { ident.Logout(); } - break; - } - case GlobalCommands.UploadToGenericCloud: - { - Cloud cloud = (Cloud)iParam1; - var ident = App.GetIdentity(cloud); - if (!ident.LoggedIn) - { - ident.LoginAsync(); - break; - } - SelectionManager.m_Instance.ClearActiveSelection(); - VrAssetService.m_Instance.UploadCurrentSketchAsync(cloud, isDemoUpload: false).AsAsyncVoid(); - EatGazeObjectInput(); - break; - } - case GlobalCommands.ViewOnlineGallery: - OpenURLAndInformUser(kTiltBrushGalleryUrl); - break; - case GlobalCommands.CancelUpload: - VrAssetService.m_Instance.CancelUpload(); - break; - case GlobalCommands.ViewLastUpload: - if (VrAssetService.m_Instance.LastUploadCompleteUrl != null) - { - var url = VrAssetService.m_Instance.LastUploadCompleteUrl; - App.OpenURL(url); - - // The upload flow is different on mobile and requires the user to manually accept - // that they'll go to the browser for publishing. In that case, we want to reset - // state when the leave to publish. This is automatically part of the - // UploadPopUpWindow state flow on PC. - if (App.Config.IsMobileHardware) - { - DismissPopupOnCurrentGazeObject(true); - } - } - break; - case GlobalCommands.ShowGoogleDrive: - string baseDriveUrl = "https://drive.google.com"; - string driveURL = !App.GoogleIdentity.LoggedIn ? baseDriveUrl : - string.Format( - "http://accounts.google.com/AccountChooser?Email={0}&continue={1}", - App.GoogleIdentity.Profile.email, baseDriveUrl); - OpenURLAndInformUser(driveURL); - break; - case GlobalCommands.GoogleDriveSync: - App.DriveSync.SyncEnabled = !App.DriveSync.SyncEnabled; - break; - case GlobalCommands.GoogleDriveSync_Folder: - App.DriveSync.ToggleSyncOnFolderOfType((DriveSync.SyncedFolderType)iParam1); - break; - case GlobalCommands.Duplicate: - { - int selectedVerts = SelectionManager.m_Instance.NumVertsInSelection; - - // TODO - this code has never taken imported models etc into account - if (PointerManager.m_Instance.CurrentSymmetryMode == SymmetryMode.MultiMirror) - { - selectedVerts *= PointerManager.m_Instance.CustomMirrorMatrices.Count; - } - - if (!SketchMemoryScript.m_Instance.MemoryWarningAccepted && - SketchMemoryScript.m_Instance.WillVertCountPutUsOverTheMemoryLimit(selectedVerts)) - { - AudioManager.m_Instance.PlayUploadCanceledSound(InputManager.Wand.Transform.position); - if (!m_PanelManager.MemoryWarningActive()) - { - m_PanelManager.ToggleMemoryWarningMode(); - } - } - else - { - ClipboardManager.Instance.DuplicateSelection( - stampMode: IsUserInteractingWithSelectionWidget()); - } - EatToolScaleInput(); - break; - } - case GlobalCommands.AdvancedPanelsToggle: - m_PanelManager.ToggleAdvancedPanels(); - // If we're now in basic mode, ensure we don't have advanced abilities. - if (!m_PanelManager.AdvancedModeActive()) - { - m_WidgetManager.StencilsDisabled = true; - m_WidgetManager.CameraPathsVisible = false; - App.Switchboard.TriggerStencilModeChanged(); - m_SketchSurfacePanel.EnsureUserHasBasicToolEnabled(); - if (PointerManager.m_Instance.CurrentSymmetryMode != SymmetryMode.None) - { - PointerManager.m_Instance.SetSymmetryMode(SymmetryMode.None, false); - } - } - PromoManager.m_Instance.RecordCompletion(PromoType.AdvancedPanels); - EatGazeObjectInput(); - break; - case GlobalCommands.Music: break; // Intentionally blank. - case GlobalCommands.ToggleGroupStrokesAndWidgets: - SelectionManager.m_Instance.ToggleGroupSelectedStrokesAndWidgets(); - EatToolScaleInput(); - break; - case GlobalCommands.SaveModel: - SaveModel(); - break; - case GlobalCommands.ViewPolyPage: - OpenURLAndInformUser(kPolyMainPageUri); - break; - case GlobalCommands.ViewPolyGallery: - OpenURLAndInformUser(kBlocksGalleryUrl); - break; - case GlobalCommands.ExportListed: - StartCoroutine(ExportListAndQuit()); - break; - case GlobalCommands.RenderCameraPath: - StartCoroutine(RenderPathAndQuit()); - break; - case GlobalCommands.ToggleProfiling: - ToggleProfiling(); - break; - case GlobalCommands.DoAutoProfile: - DoAutoProfile(); - break; - case GlobalCommands.DoAutoProfileAndQuit: - DoAutoProfileAndQuit(); - break; - case GlobalCommands.ToggleSettings: - m_PanelManager.ToggleSettingsPanels(); - PointerManager.m_Instance.EatLineEnabledInput(); - SketchSurfacePanel.m_Instance.EatToolsInput(); - break; - case GlobalCommands.SummonMirror: - PointerManager.m_Instance.BringSymmetryToUser(); - break; - case GlobalCommands.InvertSelection: - SelectionManager.m_Instance.InvertSelection(); - break; - case GlobalCommands.SelectAll: - SketchSurfacePanel.m_Instance.EnableSpecificTool(BaseTool.ToolType.SelectionTool); - SelectionManager.m_Instance.SelectAll(); - EatGazeObjectInput(); - break; - case GlobalCommands.FlipSelection: - SelectionManager.m_Instance.FlipSelection(); - break; - case GlobalCommands.ToggleBrushLab: - m_PanelManager.ToggleBrushLabPanels(); - PointerManager.m_Instance.EatLineEnabledInput(); - SketchSurfacePanel.m_Instance.EatToolsInput(); - break; - case GlobalCommands.ToggleCameraPostEffects: - CameraConfig.PostEffects = !CameraConfig.PostEffects; - break; - case GlobalCommands.ToggleWatermark: - if (PlayerPrefs.GetInt("Promo_Contribution", 0) == 0) - { - OutputWindowScript.m_Instance.CreateInfoCardAtController( - InputManager.ControllerName.Wand, - m_ContributionPromoText, fPopScalar: 1.0f); - PlayerPrefs.SetInt("Promo_Contribution", 1); - } - CameraConfig.Watermark = !CameraConfig.Watermark; - break; - case GlobalCommands.LoadConfirmComplexHigh: - IssueGlobalCommand(GlobalCommands.Load, iParam1, iParam2, null); - break; - case GlobalCommands.LoadConfirmComplex: - { - var index = iParam1; - var sketchSetType = (SketchSetType)iParam2; - bool loadSketch = true; - - // If the sketchbook is active, we may want to show a popup instead of load. - if (m_PanelManager.SketchbookActive()) - { - BasePanel sketchBook = m_PanelManager.GetSketchBookPanel(); - if (sketchBook != null) - { - // Get triangle count from cloud scene file info. - SketchSet sketchSet = SketchCatalog.m_Instance.GetSet(sketchSetType); - SceneFileInfo sfi = sketchSet.GetSketchSceneFileInfo(index); - int tris = sfi.TriangleCount ?? -1; - - // Show "this is bad" popup if we're over the triangle limit. - if (tris > QualityControls.m_Instance.AppQualityLevels.MaxPolySketchTriangles) - { - loadSketch = false; - sketchBook.CreatePopUp(GlobalCommands.LoadConfirmComplexHigh, iParam1, iParam2); - } - else if (tris > - QualityControls.m_Instance.AppQualityLevels.WarningPolySketchTriangles) - { - // Show, "this could be bad" popup if we're over the warning limit. - loadSketch = false; - sketchBook.CreatePopUp(GlobalCommands.Load, iParam1, iParam2); - } - } - } - - if (loadSketch) - { - IssueGlobalCommand(GlobalCommands.Load, iParam1, iParam2, null); - } - } - break; - case GlobalCommands.LoadConfirmUnsaved: - { - BasePanel sketchBook = m_PanelManager.GetSketchBookPanel(); - if ((sketchBook != null) && SketchMemoryScript.m_Instance.IsMemoryDirty()) - { - sketchBook.CreatePopUp(GlobalCommands.LoadWaitOnDownload, iParam1, iParam2, null); - } - else - { - IssueGlobalCommand(GlobalCommands.LoadWaitOnDownload, iParam1, iParam2, null); - } - } - break; - case GlobalCommands.LoadWaitOnDownload: - { - bool download = false; - if (iParam2 == (int)SketchSetType.Drive) - { - BasePanel sketchBook = m_PanelManager.GetSketchBookPanel(); - var googleSketchSet = SketchCatalog.m_Instance.GetSet(SketchSetType.Drive); - if (sketchBook != null - && googleSketchSet != null - && googleSketchSet.IsSketchIndexValid(iParam1) - && !googleSketchSet.GetSketchSceneFileInfo(iParam1).Available) - { - sketchBook.CreatePopUp(GlobalCommands.LoadConfirmComplex, iParam1, iParam2, null); - download = true; - } - } - if (!download) - { - IssueGlobalCommand(GlobalCommands.LoadConfirmComplex, iParam1, iParam2, null); - } - } - break; - case GlobalCommands.MemoryWarning: - if (iParam1 > 0) - { - SketchMemoryScript.m_Instance.MemoryWarningAccepted = true; - } - m_PanelManager.ToggleMemoryWarningMode(); - break; - case GlobalCommands.MemoryExceeded: - // If we're in the memory exceeded app state, exit. - if (App.CurrentState == App.AppState.MemoryExceeded) - { - App.Instance.SetDesiredState(App.AppState.Standard); - } - else - { - // If we're not in the full app state, just switch our panel mode. - m_PanelManager.ToggleMemoryWarningMode(); - } - break; - case GlobalCommands.ShowTos: - OpenURLAndInformUser(m_TosURL); - break; - case GlobalCommands.ShowPrivacy: - OpenURLAndInformUser(m_PrivacyURL); - break; - case GlobalCommands.ShowQuestSideLoading: - OpenURLAndInformUser(m_QuestSideLoadingHowToURL); - break; - case GlobalCommands.ShowContribution: - OpenURLAndInformUser(m_ContributionURL); - break; - case GlobalCommands.UnloadReferenceImageCatalog: - ReferenceImageCatalog.m_Instance.UnloadAllImages(); - break; - case GlobalCommands.ToggleCameraPathVisuals: - m_WidgetManager.CameraPathsVisible = !m_WidgetManager.CameraPathsVisible; - break; - case GlobalCommands.ToggleCameraPathPreview: - m_WidgetManager.FollowingPath = !m_WidgetManager.FollowingPath; - break; - case GlobalCommands.DeleteCameraPath: - { - var cameraPath = m_WidgetManager.GetCurrentCameraPath(); - GrabWidget cameraPathWidget = cameraPath == null ? null : cameraPath.m_WidgetScript; - m_WidgetManager.DeleteCameraPath(cameraPathWidget); - } - break; - case GlobalCommands.RecordCameraPath: - // Turn off MultiCam if we're going to record the camera path. - if (m_SketchSurfacePanel.GetCurrentToolType() == BaseTool.ToolType.MultiCamTool) - { - m_SketchSurfacePanel.EnableDefaultTool(); - } - CameraPathCaptureRig.RecordPath(); - EatGazeObjectInput(); - break; - case GlobalCommands.OpenScriptsCommandsList: - OpenURLAndInformUser($"http://localhost:{App.HttpServer.HttpPort}/help/commands"); - break; - case GlobalCommands.OpenScriptsList: - OpenURLAndInformUser($"http://localhost:{App.HttpServer.HttpPort}/scripts"); - break; - case GlobalCommands.OpenExampleScriptsList: - OpenURLAndInformUser($"http://localhost:{App.HttpServer.HttpPort}/examplescripts"); - break; - case GlobalCommands.MultiplayerTogglePanel: - m_PanelManager.ToggleMultiplayerPanels(); - PointerManager.m_Instance.EatLineEnabledInput(); - SketchSurfacePanel.m_Instance.EatToolsInput(); - break; - case GlobalCommands.RepaintOptions: break; // Intentionally blank. - case GlobalCommands.Null: break; // Intentionally blank. - case GlobalCommands.MultiplayerPanelOptions: break; // Intentionally blank. - case GlobalCommands.MultiplayerJoinRoom: break; // Intentionally blank. - case GlobalCommands.MultiplayerLeaveRoom: break; // Intentionally blank. - case GlobalCommands.MultiplayerConnect: break; // Intentionally blank. - case GlobalCommands.MultiplayerDisconnect: break; // Intentionally blank. - default: - Debug.LogError($"Unrecognized command {rEnum}"); - break; - } - } - - private void LoadNamed(string path, bool quickload, bool additive) - { - var fileInfo = new DiskSceneFileInfo(path); - fileInfo.ReadMetadata(); - if (SaveLoadScript.m_Instance.LastMetadataError != null) - { - ControllerConsoleScript.m_Instance.AddNewLine( - string.Format("Error detected in sketch '{0}'.\nTry re-saving.", - fileInfo.HumanName)); - Debug.LogWarning(string.Format("Error reading metadata for {0}.\n{1}", - fileInfo.FullPath, SaveLoadScript.m_Instance.LastMetadataError)); - } - LoadSketch(fileInfo, quickload, additive); - if (m_ControlsType != ControlsType.ViewingOnly) - { - EatGazeObjectInput(); - } - } - - public void OpenURLAndInformUser(string url) - { - // On desktop - launch external browser and inform the user - // On mobile - the browser appears over the app - if (!App.Config.IsMobileHardware) - { - OutputWindowScript.m_Instance.CreateInfoCardAtController( - InputManager.ControllerName.Brush, - kRemoveHeadsetFyi, fPopScalar: 0.5f); - } - App.OpenURL(url); - EatGazeObjectInput(); - } - - public bool IsCommandActive(GlobalCommands rEnum, int iParam = -1) - { - switch (rEnum) - { - case GlobalCommands.StraightEdge: return PointerManager.m_Instance.StraightEdgeModeEnabled; - case GlobalCommands.StraightEdgeMeterDisplay: return PointerManager.m_Instance.StraightEdgeGuide.IsShowingMeter(); - case GlobalCommands.SymmetryPlane: return PointerManager.m_Instance.CurrentSymmetryMode == SymmetryMode.SinglePlane; - case GlobalCommands.MultiMirror: return PointerManager.m_Instance.CurrentSymmetryMode == SymmetryMode.MultiMirror; - case GlobalCommands.SymmetryTwoHanded: return PointerManager.m_Instance.CurrentSymmetryMode == SymmetryMode.TwoHanded; - case GlobalCommands.AutoOrient: return m_AutoOrientAfterRotation; - case GlobalCommands.AudioVisualization: return VisualizerManager.m_Instance.VisualsRequested; - case GlobalCommands.AdvancedPanelsToggle: return m_PanelManager.AdvancedModeActive(); - case GlobalCommands.Music: return VisualizerManager.m_Instance.VisualsRequested; - case GlobalCommands.DropCam: return m_DropCam.gameObject.activeSelf; - case GlobalCommands.ToggleAutosimplification: return QualityControls.AutosimplifyEnabled; - case GlobalCommands.DraftingVisibility: return m_DraftingVisibility == (DraftingVisibilityOption)iParam; - case GlobalCommands.Cameras: - return SketchSurfacePanel.m_Instance.GetCurrentToolType() == BaseTool.ToolType.AutoGif || - SketchSurfacePanel.m_Instance.GetCurrentToolType() == BaseTool.ToolType.ScreenshotTool; - case GlobalCommands.IRC: return m_IRCChatWidget != null; - case GlobalCommands.YouTubeChat: return m_YouTubeChatWidget != null; - case GlobalCommands.StencilsDisabled: return m_WidgetManager.StencilsDisabled; - case GlobalCommands.StraightEdgeShape: - // Previously experimental mode only. - // Untested and currently untriggerable. - return PointerManager.m_Instance.StraightEdgeGuide.TempShape == (StraightEdgeGuideScript.Shape)iParam || - (PointerManager.m_Instance.StraightEdgeGuide.TempShape == StraightEdgeGuideScript.Shape.None - && PointerManager.m_Instance.StraightEdgeGuide.CurrentShape == (StraightEdgeGuideScript.Shape)iParam); - case GlobalCommands.Disco: return LightsControlScript.m_Instance.DiscoMode; - case GlobalCommands.ToggleGroupStrokesAndWidgets: return SelectionManager.m_Instance.UngroupingAllowed; - case GlobalCommands.ToggleProfiling: return UnityEngine.Profiling.Profiler.enabled; - case GlobalCommands.ToggleCameraPostEffects: return CameraConfig.PostEffects; - case GlobalCommands.ToggleWatermark: return CameraConfig.Watermark; - case GlobalCommands.ToggleCameraPathVisuals: return m_WidgetManager.CameraPathsVisible; - case GlobalCommands.ToggleCameraPathPreview: return m_WidgetManager.FollowingPath; - case GlobalCommands.SelectCameraPath: - return m_WidgetManager.IsCameraPathAtIndexCurrent(iParam) && - m_WidgetManager.CameraPathsVisible; - case GlobalCommands.GoogleDriveSync_Folder: - return App.DriveSync.IsFolderOfTypeSynced((DriveSync.SyncedFolderType)iParam); - case GlobalCommands.GoogleDriveSync: return App.DriveSync.SyncEnabled; - case GlobalCommands.RecordCameraPath: return VideoRecorderUtils.ActiveVideoRecording != null; - } - return false; - } - - public void NewSketch(bool fade) - { - LightsControlScript.m_Instance.DiscoMode = false; - m_WidgetManager.FollowingPath = false; - SketchMemoryScript.m_Instance.ClearMemory(); - ControllerConsoleScript.m_Instance.AddNewLine("Sketch Cleared"); - ResetGrabbedPose(everything: true); - QualityControls.m_Instance.ResetAutoQuality(); - InputManager.m_Instance.TriggerHaptics(InputManager.ControllerName.Brush, 0.1f); - SaveLoadScript.m_Instance.ResetLastFilename(); - SelectionManager.m_Instance.RemoveFromSelection(false); - PointerManager.m_Instance.ResetSymmetryToHome(); - App.Scene.ResetLayers(notify: true); - ApiManager.Instance.ResetBrushTransform(); - - // If we've got the camera path tool active, switch back to the default tool. - // I'm doing this because if we leave the camera path tool active, the camera path - // panel shows the button highlighted, which affects the user's flow for being - // invited to start a path. It looks weird. - if (m_SketchSurfacePanel.ActiveToolType == BaseTool.ToolType.CameraPathTool) - { - m_SketchSurfacePanel.EnableDefaultTool(); - } - - m_WidgetManager.DestroyAllWidgets(); - if (LightsControlScript.m_Instance.LightsChanged || - SceneSettings.m_Instance.EnvironmentChanged) - { - SceneSettings.m_Instance.RecordSkyColorsForFading(); - SceneSettings.m_Instance.SetDesiredPreset( - SceneSettings.m_Instance.GetDesiredPreset(), skipFade: !fade); - } - // Blank the thumbnail position so that autosave won't save the thumbnail position to be - // the one from the old sketch. - SaveLoadScript.m_Instance.LastThumbnail_SS = null; - - // Re-set the quality level to reset simplification level - QualityControls.m_Instance.QualityLevel = QualityControls.m_Instance.QualityLevel; - - App.PolyAssetCatalog.ClearLoadingQueue(); - App.PolyAssetCatalog.UnloadUnusedModels(); - } - - private bool WorldIsReset(bool toSavedXf) - { - return App.Scene.Pose == - (toSavedXf ? SketchMemoryScript.m_Instance.InitialSketchTransform : TrTransform.identity); - } - - public bool IsCommandAvailable(GlobalCommands rEnum, int iParam = -1) - { - // TODO: hide gallery view / publish if there are no saved sketches - switch (rEnum) - { - case GlobalCommands.Undo: return SketchMemoryScript.m_Instance.CanUndo(); - case GlobalCommands.Redo: return SketchMemoryScript.m_Instance.CanRedo(); - case GlobalCommands.Save: - bool canSave = - SaveLoadScript.m_Instance.SceneFile.Valid && - SaveLoadScript.m_Instance.IsSavingAllowed(); - return canSave && (!WorldIsReset(toSavedXf: true) || - (SketchHasChanges() && SketchMemoryScript.m_Instance.IsMemoryDirty())); - case GlobalCommands.SaveOptions: - case GlobalCommands.SaveNew: - case GlobalCommands.SaveGallery: - return SketchHasChanges(); - case GlobalCommands.SaveOnLocalChanges: - if (!SaveLoadScript.m_Instance.SceneFile.Valid) - { - // No save file, but something has changed. - return SketchHasChanges(); - } - else - { - if (SaveLoadScript.m_Instance.CanOverwriteSource) - { - // Save file, and it's our file. Whether we have changes is irrelevant. - return true; - } - // Save file, but it's not our file. Only make a copy if there are local changes. - return SketchMemoryScript.m_Instance.IsMemoryDirty(); - } - case GlobalCommands.UploadToGenericCloud: - return SketchMemoryScript.m_Instance.HasVisibleObjects() || - m_WidgetManager.ExportableModelWidgets.Any(w => w.gameObject.activeSelf) || - m_WidgetManager.ImageWidgets.Any(w => w.gameObject.activeSelf) || - VrAssetService.m_Instance.UploadProgress >= 1.0f || - VrAssetService.m_Instance.LastUploadFailed; - case GlobalCommands.SaveAndUpload: - return App.GoogleIdentity.LoggedIn && - (VrAssetService.m_Instance.UploadProgress <= 0.0f) && - IsCommandAvailable(GlobalCommands.UploadToGenericCloud); - case GlobalCommands.NewSketch: - return SketchHasChanges(); - case GlobalCommands.Credits: - case GlobalCommands.AshleysSketch: - return !SketchHasChanges() && !SketchMemoryScript.m_Instance.IsMemoryDirty(); - case GlobalCommands.Tiltasaurus: return TiltBrush.Tiltasaurus.m_Instance.TiltasaurusAvailable(); - case GlobalCommands.ExportRaw: - return SketchMemoryScript.m_Instance.HasVisibleObjects() || - m_WidgetManager.ModelWidgets.Any(w => w.gameObject.activeSelf) || - m_WidgetManager.ImageWidgets.Any(w => w.gameObject.activeSelf); - case GlobalCommands.ResetAllPanels: return m_PanelManager.PanelsHaveBeenCustomized(); - case GlobalCommands.Duplicate: return ClipboardManager.Instance.CanCopy; - case GlobalCommands.ToggleGroupStrokesAndWidgets: return SelectionManager.m_Instance.SelectionCanBeGrouped; - case GlobalCommands.SaveModel: return SelectionManager.m_Instance.HasSelection; - case GlobalCommands.SummonMirror: - return PointerManager.m_Instance.CurrentSymmetryMode != - SymmetryMode.None; - case GlobalCommands.InvertSelection: - case GlobalCommands.FlipSelection: - return SelectionManager.m_Instance.HasSelection; - case GlobalCommands.SelectAll: - return SketchMemoryScript.m_Instance.HasVisibleObjects() || - m_WidgetManager.HasSelectableWidgets(); - case GlobalCommands.UnloadReferenceImageCatalog: - return ReferenceImageCatalog.m_Instance.AnyImageValid(); - case GlobalCommands.ToggleCameraPathPreview: - return m_WidgetManager.CanRecordCurrentCameraPath(); - case GlobalCommands.DeleteCameraPath: - return CameraPathCaptureRig.Enabled && m_WidgetManager.AnyActivePathHasAKnot(); - case GlobalCommands.ToggleCameraPathVisuals: - return m_WidgetManager.AnyActivePathHasAKnot(); - case GlobalCommands.GoogleDriveSync: - return App.GoogleIdentity.LoggedIn; - case GlobalCommands.RecordCameraPath: - return m_WidgetManager.CameraPathsVisible; - case GlobalCommands.AdvancedPanelsToggle: - return !(MultiplayerManager.m_Instance.State == ConnectionState.IN_ROOM); - case GlobalCommands.MultiplayerConnect: - return MultiplayerManager.m_Instance.IsConnectable(); - case GlobalCommands.MultiplayerDisconnect: - return MultiplayerManager.m_Instance.IsDisconnectable(); - case GlobalCommands.MultiplayerJoinRoom: - return !PanelManager.m_Instance.AdvancedModeActive() && MultiplayerManager.m_Instance.CanJoinRoom(); - case GlobalCommands.MultiplayerLeaveRoom: - return MultiplayerManager.m_Instance.CanLeaveRoom(); - } - return true; - } - - public bool SketchHasChanges() - { - if (SceneSettings.m_Instance.IsTransitioning) { return false; } - return SketchMemoryScript.m_Instance.HasVisibleObjects() || - SceneSettings.m_Instance.EnvironmentChanged || - LightsControlScript.m_Instance.LightsChanged || - m_WidgetManager.ModelWidgets.Any(w => w.gameObject.activeSelf) || - m_WidgetManager.LightWidgets.Any(w => w.gameObject.activeSelf) || - m_WidgetManager.StencilWidgets.Any(w => w.gameObject.activeSelf) || - m_WidgetManager.ImageWidgets.Any(w => w.gameObject.activeSelf) || - m_WidgetManager.VideoWidgets.Any(w => w.gameObject.activeSelf) || - m_WidgetManager.AnyCameraPathWidgetsActive; - } - - public void OpenPanelOfType(BasePanel.PanelType type, TrTransform trSpawnXf) - { - m_PanelManager.OpenPanel(type, trSpawnXf); - EatGazeObjectInput(); - } - - public void RestoreFloatingPanels() - { - if (!m_SketchSurfacePanel.ActiveTool.HidePanels()) - { - m_PanelManager.RestoreHiddenPanels(); - } - } - - public void UpdateDraftingVisibility() - { - float value = 0; - switch (m_DraftingVisibility) - { - case DraftingVisibilityOption.Visible: - value = 1; - break; - case DraftingVisibilityOption.Transparent: - value = .5f; - break; - case DraftingVisibilityOption.Hidden: - value = 0; - break; - } - Shader.SetGlobalFloat("_DraftingVisibility01", value); - } - - private void ToggleProfiling() - { - if (Debug.isDebugBuild && ProfileDisplay.Instance != null) - { - ProfileDisplay.Instance.gameObject.SetActive(UnityEngine.Profiling.Profiler.enabled); - } - if (UnityEngine.Profiling.Profiler.enabled) - { - ProfilingManager.Instance.StopProfiling(); - } - else - { - ProfilingManager.Instance.StartProfiling(App.UserConfig.Profiling.ProfilingMode); - } - } - - private void DoAutoProfile() - { - StartCoroutine(DoProfiling()); - } - - private void DoAutoProfileAndQuit() - { - StartCoroutine(DoProfiling(andQuit: true)); - } - - private IEnumerator DoProfiling(bool andQuit = false) - { - TrTransform oldWandPose = TrTransform.FromTransform(InputManager.Wand.Geometry.transform); - TrTransform oldBrushPose = TrTransform.FromTransform(InputManager.Brush.Geometry.transform); - - App.AppState oldState = App.CurrentState; - App.Instance.SetDesiredState(App.AppState.AutoProfiling); - while (App.CurrentState != App.AppState.AutoProfiling) - { - yield return null; - } - - TrTransform camPose = App.Scene.Pose * SaveLoadScript.m_Instance.ReasonableThumbnail_SS; - camPose.ToTransform(App.VrSdk.GetVrCamera().transform); - float controllerDirection = App.UserConfig.Profiling.ShowControllers ? 1f : -1f; - Vector3 roffset = Camera.main.transform.right * 2f; - Vector3 fOffset = Camera.main.transform.forward * 4f * controllerDirection; - InputManager.Brush.Geometry.transform.position = Camera.main.transform.position + roffset + fOffset; - InputManager.Brush.Geometry.transform.rotation = Camera.main.transform.rotation; - InputManager.Wand.Geometry.transform.position = Camera.main.transform.position - roffset + fOffset; - InputManager.Wand.Geometry.transform.rotation = Camera.main.transform.rotation; - m_PanelManager.LockPanelsToController(); - - ProfilingManager.Instance.StartProfiling(App.UserConfig.Profiling.ProfilingMode); - yield return new WaitForSeconds(App.UserConfig.Profiling.Duration); - ProfilingManager.Instance.StopProfiling(); - - if (App.UserConfig.Profiling.TakeScreenshot) - { - GameObject camObj = new GameObject("ScreenShotter"); - Camera cam = camObj.AddComponent(); - cam.CopyFrom(App.VrSdk.GetVrCamera()); - cam.stereoTargetEye = StereoTargetEyeMask.None; - cam.clearFlags = CameraClearFlags.SolidColor; - camPose.ToTransform(camObj.transform); - int res = App.UserConfig.Profiling.ScreenshotResolution; - RenderTexture renderTexture = RenderTexture.GetTemporary(res, res, 24); - try - { - cam.targetTexture = renderTexture; - cam.Render(); - RenderTexture prev = RenderTexture.active; - RenderTexture.active = renderTexture; - var texture = new Texture2D(res, res, TextureFormat.RGB24, false); - texture.ReadPixels(new Rect(0, 0, texture.width, texture.height), 0, 0); - RenderTexture.active = prev; - byte[] jpegBytes = texture.EncodeToJPG(); - string filename = - Path.GetFileNameWithoutExtension(SaveLoadScript.m_Instance.SceneFile.FullPath); - File.WriteAllBytes(Path.Combine(App.UserPath(), filename + ".jpg"), jpegBytes); - } - finally - { - Destroy(camObj); - RenderTexture.ReleaseTemporary(renderTexture); - } - } - - oldWandPose.ToTransform(InputManager.Wand.Geometry.transform); - oldBrushPose.ToTransform(InputManager.Brush.Geometry.transform); - App.Instance.SetDesiredState(oldState); - - if (andQuit) - { - QuitApp(); - } - } - } - -} // namespace TiltBrush +// Copyright 2020 The Tilt 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 OpenBrush.Multiplayer; +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using TiltBrush.Layers; +using UnityEngine; +using UnityEngine.InputSystem; +using SymmetryMode = TiltBrush.PointerManager.SymmetryMode; + +namespace TiltBrush +{ + + public class SketchControlsScript : MonoBehaviour + { + public const string kRemoveHeadsetFyi = "Remove headset to view."; + const string kTiltBrushGalleryUrl = "https://poly.google.com/tiltbrush"; + const string kBlocksGalleryUrl = "https://poly.google.com/blocks"; + const string kPolyMainPageUri = "https://poly.google.com"; + + static public SketchControlsScript m_Instance; + static bool sm_enableGrabHaptics = true; + + // ------------------------------------------------------------ + // Constants and types + // ------------------------------------------------------------ + + public enum GlobalCommands + { + Null, + Save, + SaveNew, + Load, + NewSketch, + StraightEdge, + AutoOrient, + Undo, + Redo, + Tiltasaurus, + LightingHdr, + AudioVisualization, + ResetAllPanels, + SketchOrigin, + SymmetryPlane, + MultiMirror, + ViewOnly, + SaveGallery, + LightingLdr, + ShowSketchFolder, + About, + LoadNamedFile, // iParam1 : (optional) - send through a LoadSpeed as int + DropCam, + CuratedGallery, + Unused_UploadToCloud, + AnalyticsEnabled_Deprecated, + Credits, + LogOutOfGenericCloud, + DraftingVisibility, + DeleteSketch, + ShowWindowGUI, + MorePanels, + Cameras, + FAQ, + ExportRaw, + IRC, + YouTubeChat, + CameraOptions, + StencilsDisabled, + AdvancedTools, + FloatingPanelsMode, + StraightEdgeMeterDisplay, + Sketchbook, + ExportAll, + Lights, + SaveAndUpload, + StraightEdgeShape, + SaveOptions, + SketchbookMenu, + Disco, + ViewOnlineGallery, + CancelUpload, + AdvancedPanelsToggle, + Music, + Duplicate, + ToggleGroupStrokesAndWidgets, + SaveModel, + ViewPolyPage, + ViewPolyGallery, + ExportListed, + RenderCameraPath, + ToggleProfiling, + DoAutoProfile, + DoAutoProfileAndQuit, + ToggleSettings, + SummonMirror, + InvertSelection, + SelectAll, + FlipSelection, + ToggleBrushLab, + ReleaseNotes, + ToggleCameraPostEffects, + ToggleWatermark, + AccountInfo, + // LoadConfirmUnsaved -> LoadWaitOnDownload -> LoadConfirmComplex -> LoadComplexHigh -> Load + LoadConfirmUnsaved, + LoadConfirmComplex, + MemoryWarning, + MemoryExceeded, + ViewLastUpload, + LoadConfirmComplexHigh, + ShowTos, + ShowPrivacy, + ShowQuestSideLoading, + AshleysSketch, + UnloadReferenceImageCatalog, + SaveOnLocalChanges, + ToggleCameraPathVisuals, + ToggleCameraPathPreview, + DeleteCameraPath, + RecordCameraPath, + SelectCameraPath, + ToggleAutosimplification, + ShowGoogleDrive, + GoogleDriveSync_Folder, // iParam1: folder id as DriveSync.SyncedFolderType + GoogleDriveSync, + LoginToGenericCloud, // iParam1: Cloud enum + UploadToGenericCloud, // iParam1: Cloud enum + LoadWaitOnDownload, + SignOutConfirm, + ReadOnlyNotice, + ShowContribution, + + // Open Brush Reserved Enums 1000-1999 + LanguagePopup = 1000, + MultiplayerTogglePanel = 1001, + MultiplayerPanelOptions = 1002, // iParam1: Popup options + MultiplayerJoinRoom = 1004, + EditMultiplayerRoomName = 1005, + MultiplayerLeaveRoom = 1006, + MultiplayerConnect = 1007, + MultiplayerDisconnect = 1008, + EditMultiplayerNickName = 1009, + + RenameSketch = 5200, + OpenLayerOptionsPopup = 5201, + RenameLayer = 5202, + OpenDirectorChooserPopup = 5800, + OpenScriptsCommandsList = 6000, + OpenScriptsList = 6001, + OpenExampleScriptsList = 6002, + SymmetryTwoHanded = 6003, + OpenColorOptionsPopup = 7000, + ChangeSnapAngle = 8000, + MergeBrushStrokes = 10000, + RepaintOptions = 11500, + OpenNumericInputPopup = 12000 + } + + public enum ControlsType + { + KeyboardMouse, + SixDofControllers, + ViewingOnly + } + + public enum DraftingVisibilityOption + { + Visible, + Transparent, + Hidden + } + + public enum InputState + { + Standard, + Pan, + Rotation, + HeadLock, + ControllerLock, + PushPull, + BrushSize, + Save, + Load, + Num + } + + public enum LoadSpeed + { + Normal = -1, + Quick = 1, + } + + const float kControlPointHistoryMaxTime = 0.1f; + + class GazeResult + { + public bool m_HitWithGaze; + public bool m_HitWithController; + // ReSharper disable once NotAccessedField.Local + public bool m_WithinView; + public float m_ControllerDistance; + public Vector3 m_GazePosition; + public Vector3 m_ControllerPosition; + public InputManager.ControllerName m_ControllerName; + } + + class GrabWidgetControllerInfo + { + public InputManager.ControllerName m_Name; + /// Transform of controller at the time the grab started + public TrTransform m_BaseControllerXf; + /// "local" transform of widget (relative to controller), at the time the grab started. + /// The widget isn't parented to the controller, but if it were, this would be its transform. + public TrTransform m_BaseWidgetXf_LS; + } + + struct GrabWidgetHoldPoint + { + // ReSharper disable once NotAccessedField.Local + public InputManager.ControllerName m_Name; + public float m_BirthTime; + public Vector3 m_Pos; // where controller is holding the widget + public Quaternion m_Rot; + } + + class InputStateConfig + { + public bool m_AllowDrawing; + public bool m_AllowMovement; + public bool m_ShowGizmo; + } + + enum FadeState + { + None, + FadeOn, + FadeOff + } + + enum GrabWidgetState + { + None, + OneHand, + TwoHands + } + + enum GrabWorldState + { + Normal, + ResettingTransform, + ResetDone + } + + private enum WorldTransformResetState + { + Default, + Requested, + FadingToBlack, + FadingToScene, + } + + enum RotationType + { + All, + RollOnly + } + + enum GrabIntersectionState + { + RequestIntersections, + ReadBrush, + ReadWand + } + + // ------------------------------------------------------------ + // Inspector data (read-only even if public) + // ------------------------------------------------------------ + + public GameObject m_SketchSurface; + public SketchMemoryScript.PlaybackMode m_DefaultSketchPlaybackMode; + public float m_GazeMaxAngleFromPointing = 85.0f; + public float m_GazeMaxAngleFacingToForward = 80.0f; + + [SerializeField] bool m_AtlasIconTextures; + + [SerializeField] SaveIconTool m_SaveIconTool; + [SerializeField] DropCamWidget m_DropCam; + [SerializeField] string m_CreditsSketchFilename; + [SerializeField] string m_AshleysSketchFilename; + [SerializeField] float m_DefaultSketchLoadSpeed; + [SerializeField] GameObject m_TransformGizmoPrefab; + + [SerializeField] GameObject m_RotationIconPrefab; + [SerializeField] float m_GazeMaxAngleFromFacing = 70.0f; + [SerializeField] float m_GazeMaxDistance = 10.0f; + [SerializeField] float m_GazeControllerPointingDistance; + [SerializeField] float m_GazePanelDectivationDelay = 0.25f; + + [SerializeField] GameObject m_UIReticle; + [SerializeField] GameObject m_UIReticleMobile; + [SerializeField] GameObject m_UIReticleSixDofController; + + [SerializeField] float m_DoubleTapWindow; + [SerializeField] float m_PushPullScale; + [SerializeField] RotationCursorScript m_RotationCursor; + [SerializeField] float m_RotationMaxAngle; + + [SerializeField] float m_RotationScalar; + [SerializeField] float m_RotationRollScalar; + [SerializeField] float m_PanScalar; + + [SerializeField] float m_AdjustToolSizeScalar; + + [SerializeField] GameObject m_IRCChatPrefab; + [SerializeField] GameObject m_YouTubeChatPrefab; + [SerializeField] GameObject m_Decor; + [SerializeField] BaseTool.ToolType m_InitialTool = BaseTool.ToolType.SketchSurface; + [SerializeField] string m_ReleaseNotesURL; + [SerializeField] string m_HelpCenterURL; + [SerializeField] string m_ThirdPartyNoticesURL; + [SerializeField] string m_TosURL; + [SerializeField] string m_PrivacyURL; + [SerializeField] string m_QuestSideLoadingHowToURL; + + [Multiline] + [SerializeField] string m_ContributionPromoText; + [SerializeField] string m_ContributionURL; + + [SerializeField] float m_WorldTransformMinScale = .1f; + [SerializeField] float m_WorldTransformMaxScale = 10.0f; + + [Header("Undo/Redo Hold")] + [SerializeField] float m_UndoRedoHold_DurationBeforeStart; + [SerializeField] float m_UndoRedoHold_RepeatInterval; + + [Header("Pin Cushion")] + [SerializeField] GameObject m_PinCushionPrefab; + + [Header("Grabbing and tossing")] + [SerializeField] float m_GrabWorldFadeSpeed = 8.0f; + [SerializeField] Color m_GrabWorldGridColor = new Color(0.0f, 1.0f, 1.0f, 0.2f); + [SerializeField] ControllerGrabVisuals m_ControllerGrabVisuals; + [SerializeField] float m_WidgetGpuIntersectionRadius; + + [Header("Saving")] + [SerializeField] int m_NumStrokesForSaveIcon = 50; + + [NonSerialized] public Color m_GrabHighlightActiveColor; + [NonSerialized] public bool m_DisableWorldGrabbing = false; + + /// Throwing an object faster than this means it's a "toss". Units are m/s. + public float m_TossThresholdMeters = 3f; + /// Angular motion contributes more towards the toss velocity the larger the object is; + /// or rather, the larger the distance between the grab point and the object's center. + /// To prevent large objects from being too-easily-tossed, bound that distance. + public float m_TossMaxPivotDistMeters = 0.33f; + + // ------------------------------------------------------------ + // Internal data + // ------------------------------------------------------------ + + private SketchSurfacePanel m_SketchSurfacePanel; + private SketchMemoryScript.PlaybackMode m_SketchPlaybackMode; + private GameObject m_TransformGizmo; + private TransformGizmoScript m_TransformGizmoScript; + private GameObject m_RotationIcon; + private float m_MouseDeltaX; + private float m_MouseDeltaY; + private float m_MouseDeltaXScaled; + private float m_MouseDeltaYScaled; + private float m_PositionOffsetResetTapTime; + private bool m_EatToolScaleInput; + + private PanelManager m_PanelManager; + private WidgetManager m_WidgetManager; + private PinCushion m_PinCushion; + private bool m_EatPinCushionInput; + + // This is the gaze that was used to compute m_CurrentGazeHitPoint. + // It is not a general substitute for ViewpointScript.Gaze. + private Ray m_CurrentGazeRay; + private Quaternion m_CurrentHeadOrientation; + private GazeResult[] m_GazeResults; + private int m_CurrentGazeObject; + private bool m_EatInputGazeObject; + private Vector3 m_CurrentGazeHitPoint; + private Ray m_GazeControllerRay; + private Ray m_GazeControllerRayActivePanel; + private bool m_ForcePanelActivation = false; + private float m_GazePanelDectivationCountdown; + private bool m_PanelsVisibilityRequested; + + // Previously Experimental-Model only + private bool m_HeadOffset; + + float m_UndoHold_Timer; + float m_RedoHold_Timer; + + // Grab world member variables. + struct GrabState + { + public InputManager.ControllerName name; + public TrTransform grabTransform; + public bool grabbingWorld; + public bool grabbingGroup; + public bool startedGrabInsideWidget; + public bool eatInput; + private GrabWidget lastWidgetIntersect; + + public void SetHadBestGrabAndTriggerHaptics(GrabWidgetData data) + { + bool dormant = WidgetManager.m_Instance.WidgetsDormant; + if (data != null && !data.m_WidgetScript.AllowDormancy) + { + dormant = false; + } + GrabWidget newInsideWidget = (data != null && !dormant) ? data.m_WidgetScript : null; + if (sm_enableGrabHaptics && newInsideWidget != lastWidgetIntersect) + { + // state changed + if (newInsideWidget != null) + { + // transitioning in + InputManager.m_Instance.TriggerHaptics(name, data.m_WidgetScript.HapticDuration); + } + else + { + // transitioning out + InputManager.m_Instance.TriggerHaptics(name, 0.03f); + } + } + lastWidgetIntersect = newInsideWidget; + } + + public void ClearInsideWidget() + { + lastWidgetIntersect = null; + } + } + private GrabState m_GrabBrush = new GrabState { name = InputManager.ControllerName.Brush }; + private GrabState m_GrabWand = new GrabState { name = InputManager.ControllerName.Wand }; + + private WorldTransformResetState m_WorldTransformResetState = WorldTransformResetState.Default; + private TrTransform m_WorldTransformResetXf = TrTransform.identity; // set when reset requested + private GrabWorldState m_GrabWorldState = GrabWorldState.Normal; + private float m_WorldTransformFadeAmount; + private bool m_AllowWorldTransformLastFrame = false; + private bool m_WorldBeingGrabbed; + private TrTransform m_xfDropCamReset_RS; + + struct GpuIntersectionResult + { + public GpuIntersector.FutureModelResult result; + public List resultList; + } + private Queue m_BrushResults; + private Queue m_WandResults; + private int m_WidgetGpuIntersectionLayer; + + private GrabWidget m_CurrentGrabWidget; + private GrabWidget m_MaybeDriftingGrabWidget; // use only to clear drift + + // References to widgets, cached in the UpdateGrab_None, to be used by helper functions + // for the remainder of the frame. + private GrabWidget m_PotentialGrabWidgetBrush; + private GrabWidget m_PotentialGrabWidgetWand; + + // Flags for the explaining if the m_PotentialGrabWidget_x widgets are able to be interacted with. + // Cached in the UpdateGrab_None, used for the remainder of the frame. + private bool m_PotentialGrabWidgetBrushValid; + private bool m_PotentialGrabWidgetWandValid; + + // References to widget metadata, cached in UpdateGrab_None, to be re-used on "off frames" + // when the GPU intersector is not refreshing the nearest widget to the respective controller. + private GrabWidgetData m_BackupBrushGrabData; + private GrabWidgetData m_BackupWandGrabData; + + private GrabWidgetState m_GrabWidgetState; + private GrabWidgetControllerInfo m_GrabWidgetOneHandInfo; + private TrTransform m_GrabWidgetTwoHandBrushPrev; + private TrTransform m_GrabWidgetTwoHandWandPrev; + private Queue m_GrabWidgetHoldHistory; + + private Quaternion m_RotationOrigin; + private Vector2 m_RotationCursorOffset; + + private bool m_RotationRollActive; + private float m_RotationResetTapTime; + + private RotationType m_CurrentRotationType; + private bool m_AutoOrientAfterRotation; + + private Vector3 m_SurfaceForward; + private Vector3 m_SurfaceRight; + private Vector3 m_SurfaceUp; + + private Vector3 m_SurfaceLockOffset; + private Vector3 m_SurfaceLockBaseSurfacePosition; + private Vector3 m_SurfaceLockBaseControllerPosition; + private Quaternion m_SurfaceLockBaseHeadRotation; + private Quaternion m_SurfaceLockBaseControllerRotation; + private Quaternion m_SurfaceLockBaseSurfaceRotation; + private InputManager.ControllerName m_SurfaceLockActingController; + private float m_SurfaceLockControllerBaseScalar; + private float m_SurfaceLockControllerScalar; + + private bool m_PositioningPanelWithHead; + private Quaternion m_PositioningPanelBaseHeadRotation; + private Vector3 m_PositioningPanelOffset; + private float m_PositioningTimer; + private float m_PositioningSpeed; + + private DraftingVisibilityOption m_DraftingVisibility = DraftingVisibilityOption.Visible; + + private Vector3 m_SketchOrigin; + + private ControlsType m_ControlsType; + private GrabWidget m_IRCChatWidget; + private GrabWidget m_YouTubeChatWidget; + private MultiCamCaptureRig m_MultiCamCaptureRig; + private CameraPathCaptureRig m_CameraPathCaptureRig; + + private bool m_ViewOnly = false; + + private InputState m_CurrentInputState; + private InputStateConfig[] m_InputStateConfigs; + + private GrabIntersectionState m_CurrentGrabIntersectionState; + + private float m_WorldTransformSpeedSmoothed; + + // ------------------------------------------------------------ + // Properties and events + // ------------------------------------------------------------ + + public MultiCamCaptureRig MultiCamCaptureRig + { + get { return m_MultiCamCaptureRig; } + } + + public CameraPathCaptureRig CameraPathCaptureRig + { + get { return m_CameraPathCaptureRig; } + } + + public ControllerGrabVisuals ControllerGrabVisuals + { + get { return m_ControllerGrabVisuals; } + } + + public SketchMemoryScript.PlaybackMode SketchPlaybackMode + { + get { return m_SketchPlaybackMode; } + set { m_SketchPlaybackMode = value; } + } + + public Transform m_Canvas + { + get { return App.Instance.m_CanvasTransform; } + } + + public ControlsType ActiveControlsType + { + get { return m_ControlsType; } + set { m_ControlsType = value; } + } + + public float WorldTransformMinScale + { + get + { + return App.UserConfig.Flags.UnlockScale ? m_WorldTransformMinScale * 0.01f : + m_WorldTransformMinScale; + } + } + + public float WorldTransformMaxScale + { + get + { + return App.UserConfig.Flags.UnlockScale ? m_WorldTransformMaxScale * 10.0f : + m_WorldTransformMaxScale; + } + } + + public void SetInitialTool(BaseTool.ToolType rType) + { + m_InitialTool = rType; + } + + public void SetInFreePaintMode(bool bFreePaint) + { + m_SketchSurfacePanel.SetInFreePaintMode(bFreePaint); + } + + public float GazeMaxDistance + { + get { return m_GazeMaxDistance; } + } + + public InputManager.ControllerName OneHandGrabController + { + get + { + return m_CurrentGrabWidget != null ? + m_GrabWidgetOneHandInfo.m_Name : + InputManager.ControllerName.None; + } + } + + public InputManager.ControllerName PotentialOneHandGrabController(GrabWidget widget) + { + if (m_PotentialGrabWidgetBrush == widget) + { + return InputManager.ControllerName.Brush; + } + else if (m_PotentialGrabWidgetWand == widget) + { + return InputManager.ControllerName.Wand; + } + return OneHandGrabController; + } + + public Vector3 GetSurfaceForward() { return m_SurfaceForward; } + public Vector3 GetSurfaceUp() { return m_SurfaceUp; } + public Vector3 GetSurfaceRight() { return m_SurfaceRight; } + public Vector3 GetSketchOrigin() { return m_SketchOrigin; } + public float GetDefaultSketchLoadSpeed() { return m_DefaultSketchLoadSpeed; } + public Quaternion GetCurrentHeadOrientation() { return m_CurrentHeadOrientation; } + public Vector3 GetUIReticlePos() { return m_UIReticle.transform.position; } + public Vector3 GetSweetSpotPos() { return m_PanelManager.m_SweetSpot.transform.position; } + public void SetSketchOrigin(Vector3 vOrigin) { m_SketchOrigin = vOrigin; } + + public void EatGazeObjectInput() + { + m_EatInputGazeObject = true; + m_GazePanelDectivationCountdown = 0.0f; + PointerManager.m_Instance.EatLineEnabledInput(); + SketchSurfacePanel.m_Instance.EatToolsInput(); + } + public void EatToolScaleInput() { m_EatToolScaleInput = true; } + public void EatGrabInput() + { + m_GrabWand.eatInput = true; + m_GrabBrush.eatInput = true; + } + + public bool ShouldRespondToPadInput(InputManager.ControllerName name) + { + if (name == InputManager.ControllerName.Brush && m_CurrentGazeObject != -1) + { + return m_PanelManager.GetPanel(m_CurrentGazeObject).BrushPadAnimatesOnHover(); + } + return !m_EatToolScaleInput && SketchSurfacePanel.m_Instance.CanAdjustToolSize(); + } + public void ForcePanelActivation(bool bForce) + { + m_ForcePanelActivation = bForce; + if (m_ForcePanelActivation) + { + m_GazePanelDectivationCountdown = m_GazePanelDectivationDelay; + } + } + public bool IsUserInteractingWithUI() + { + return (m_CurrentGazeObject != -1) || (m_GazePanelDectivationCountdown > 0.0f); + } + public bool IsUIBlockingUndoRedo() + { + if (m_CurrentGazeObject != -1) + { + return m_PanelManager.GetPanel(m_CurrentGazeObject).UndoRedoBlocked(); + } + return false; + } + public bool IsUserAbleToInteractWithAnyWidget() + { + return IsUserInteractingWithAnyWidget() || + (m_PotentialGrabWidgetBrush != null && m_PotentialGrabWidgetBrushValid) || + (m_PotentialGrabWidgetWand != null && m_PotentialGrabWidgetWandValid); + } + public bool IsUserInteractingWithAnyWidget() { return m_CurrentGrabWidget != null; } + public bool IsUserGrabbingAnyPanel() + { + return (m_CurrentGrabWidget != null && m_CurrentGrabWidget is PanelWidget); + } + public bool IsUsersBrushIntersectingWithSelectionWidget() + { + return (m_PotentialGrabWidgetBrush != null && + m_PotentialGrabWidgetBrushValid && + m_PotentialGrabWidgetBrush is SelectionWidget); + } + public bool IsUserIntersectingWithSelectionWidget() + { + return IsUsersBrushIntersectingWithSelectionWidget() || + (m_PotentialGrabWidgetWand != null && + m_PotentialGrabWidgetWandValid && + m_PotentialGrabWidgetWand is SelectionWidget); + } + public bool IsUserInteractingWithSelectionWidget() + { + return (m_CurrentGrabWidget != null && m_CurrentGrabWidget is SelectionWidget); + } + + public bool IsUserGrabbingWorld() { return m_GrabWand.grabbingWorld || m_GrabBrush.grabbingWorld; } + public bool IsUserGrabbingWorldWithBrushHand() { return m_GrabBrush.grabbingWorld; } + public bool IsUserTransformingWorld() { return m_GrabWand.grabbingWorld && m_GrabBrush.grabbingWorld; } + public float GetGazePanelActivationRatio() { return m_GazePanelDectivationCountdown / m_GazePanelDectivationDelay; } + public bool IsCurrentGrabWidgetPinned() { return IsUserInteractingWithAnyWidget() && m_CurrentGrabWidget.Pinned; } + public bool CanCurrentGrabWidgetBePinned() { return IsUserInteractingWithAnyWidget() && m_CurrentGrabWidget.AllowPinning; } + public bool DidUserGrabWithBothInside() { return m_GrabBrush.startedGrabInsideWidget && m_GrabWand.startedGrabInsideWidget; } + public bool IsUserGrabbingWidget(GrabWidget widget) { return widget == m_CurrentGrabWidget; } + public bool IsUserTwoHandGrabbingWidget() { return m_GrabWidgetState == GrabWidgetState.TwoHands; } + public bool IsPinCushionShowing() { return m_PinCushion.IsShowing(); } + public bool IsUserLookingAtPanel(BasePanel panel) + { + return m_CurrentGazeObject > -1 && + m_PanelManager.GetAllPanels()[m_CurrentGazeObject].m_Panel == panel; + } + + public SaveIconTool GetSaveIconTool() + { + return m_SaveIconTool; + } + + public DropCamWidget GetDropCampWidget() + { + return m_DropCam; + } + + public bool IsGrabWorldStateStable() + { + return m_GrabWorldState == GrabWorldState.Normal; + } + + // Internal: modify Coords.ScenePose or Coords.CanvasPose depending on the + // state of m_InTransformCanvasMode + TrTransform GrabbedPose + { + get + { + return App.Scene.Pose; + } + set + { + App.Scene.Pose = value; + } + } + + public Transform GazeObjectTransform() + { + if (m_CurrentGazeObject != -1) + { + return m_PanelManager.GetPanel(m_CurrentGazeObject).transform; + } + return null; + } + + public void ForceShowUIReticle(bool bVisible) + { + m_UIReticle.SetActive(bVisible); + } + + public void SetUIReticleTransform(Vector3 vPos, Vector3 vForward) + { + m_UIReticle.transform.position = vPos; + m_UIReticle.transform.forward = vForward; + } + + public bool AtlasIconTextures + { + get { return m_AtlasIconTextures; } + } + + public IconTextureAtlas IconTextureAtlas + { + get { return GetComponent(); } + } + public GrabWidget CurrentGrabWidget => m_CurrentGrabWidget; + + void DismissPopupOnCurrentGazeObject(bool force) + { + if (m_CurrentGazeObject != -1) + { + m_PanelManager.GetPanel(m_CurrentGazeObject).CloseActivePopUp(force); + } + } + + void Awake() + { + m_Instance = this; + + BrushController.m_Instance.BrushSetToDefault += OnBrushSetToDefault; + + IconTextureAtlas.Init(); + + m_MultiCamCaptureRig = GetComponentInChildren(true); + m_MultiCamCaptureRig.Init(); + + m_CameraPathCaptureRig = GetComponentInChildren(true); + m_CameraPathCaptureRig.Init(); + + m_SketchSurfacePanel = m_SketchSurface.GetComponent(); + m_PanelManager = GetComponent(); + m_PanelManager.Init(); + InitGazePanels(); + + m_WidgetManager = GetComponent(); + m_WidgetManager.Init(); + + m_InputStateConfigs = new InputStateConfig[(int)InputState.Num]; + for (int i = 0; i < (int)InputState.Num; ++i) + { + m_InputStateConfigs[i] = new InputStateConfig(); + m_InputStateConfigs[i].m_AllowDrawing = false; + m_InputStateConfigs[i].m_AllowMovement = true; + m_InputStateConfigs[i].m_ShowGizmo = false; + } + + m_InputStateConfigs[(int)InputState.Standard].m_AllowDrawing = true; + m_InputStateConfigs[(int)InputState.Pan].m_AllowDrawing = true; + m_InputStateConfigs[(int)InputState.HeadLock].m_AllowDrawing = true; + m_InputStateConfigs[(int)InputState.ControllerLock].m_AllowDrawing = true; + m_InputStateConfigs[(int)InputState.PushPull].m_AllowDrawing = true; + + m_InputStateConfigs[(int)InputState.Pan].m_AllowMovement = false; + m_InputStateConfigs[(int)InputState.Rotation].m_AllowMovement = false; + m_InputStateConfigs[(int)InputState.ControllerLock].m_AllowMovement = false; + m_InputStateConfigs[(int)InputState.PushPull].m_AllowMovement = false; + m_InputStateConfigs[(int)InputState.BrushSize].m_AllowMovement = false; + + m_InputStateConfigs[(int)InputState.Pan].m_ShowGizmo = true; + m_InputStateConfigs[(int)InputState.Rotation].m_ShowGizmo = true; + m_InputStateConfigs[(int)InputState.HeadLock].m_ShowGizmo = true; + m_InputStateConfigs[(int)InputState.PushPull].m_ShowGizmo = true; + + m_CurrentGazeRay = new Ray(Vector3.zero, Vector3.forward); + m_GazeControllerRay = new Ray(Vector3.zero, Vector3.forward); + m_GazeControllerRayActivePanel = new Ray(Vector3.zero, Vector3.forward); + + m_GrabWidgetHoldHistory = new Queue(); + m_GrabWidgetOneHandInfo = new GrabWidgetControllerInfo(); + + // Initialize world grip members. + m_GrabBrush.grabTransform = TrTransform.identity; + m_GrabWand.grabTransform = TrTransform.identity; + + m_BrushResults = new Queue(); + m_WandResults = new Queue(); + m_WidgetGpuIntersectionLayer = LayerMask.NameToLayer("GpuIntersection"); + m_CurrentGrabIntersectionState = GrabIntersectionState.RequestIntersections; + } + + public void InitGazePanels() + { + // Find all gaze panels. + int iNumGazePanels = m_PanelManager.GetAllPanels().Count; + m_GazeResults = new GazeResult[iNumGazePanels]; + for (int i = 0; i < iNumGazePanels; ++i) + { + m_GazeResults[i] = new GazeResult(); + m_GazeResults[i].m_HitWithGaze = false; + m_GazeResults[i].m_HitWithController = false; + m_GazeResults[i].m_WithinView = false; + m_GazeResults[i].m_GazePosition = new Vector3(); + } + } + + public void OnEnable() + { + // This needs to run before other tools initialize, which is why it's running in OnEnable. + // The sequence is Awake(), OnEnable(), Start(). + if (App.VrSdk.GetControllerDof() == VrSdk.DoF.Six) + { + SetInFreePaintMode(true); + SetInitialTool(BaseTool.ToolType.FreePaintTool); + } + } + + void Start() + { + m_TransformGizmo = (GameObject)Instantiate(m_TransformGizmoPrefab); + m_TransformGizmo.transform.parent = transform; + m_TransformGizmoScript = m_TransformGizmo.GetComponent(); + m_TransformGizmo.SetActive(false); + + m_RotationIcon = (GameObject)Instantiate(m_RotationIconPrefab); + m_RotationIcon.transform.position = m_SketchSurface.transform.position; + m_RotationIcon.transform.parent = m_SketchSurface.transform; + m_RotationIcon.SetActive(false); + + GameObject pinCushionObj = (GameObject)Instantiate(m_PinCushionPrefab); + m_PinCushion = pinCushionObj.GetComponent(); + + m_PositionOffsetResetTapTime = 0.0f; + + m_UndoHold_Timer = m_UndoRedoHold_DurationBeforeStart; + m_RedoHold_Timer = m_UndoRedoHold_DurationBeforeStart; + + m_AutoOrientAfterRotation = true; + m_RotationCursor.gameObject.SetActive(false); + + ResetGrabbedPose(); + m_SketchOrigin = m_SketchSurface.transform.position; + + m_PanelManager.InitPanels(m_ControlsType == ControlsType.SixDofControllers); + + m_UIReticleMobile.SetActive(m_ControlsType == ControlsType.ViewingOnly); + m_UIReticleSixDofController.SetActive(m_ControlsType != ControlsType.ViewingOnly); + + m_PositioningPanelWithHead = false; + m_PositioningSpeed = 16.0f; + + m_CurrentRotationType = RotationType.All; + m_RotationResetTapTime = 0.0f; + + m_CurrentInputState = InputState.Standard; + + m_SketchSurfacePanel.EnableSpecificTool(m_InitialTool); + m_SurfaceLockControllerBaseScalar = m_SketchSurfacePanel.m_PanelSensitivity; + + //after initializing, start with gaze objects hidden + m_CurrentGazeObject = -1; + m_EatInputGazeObject = false; + + // Previously set to 0 in experimental builds + int hidePanelsDelay = 1; + + StartCoroutine(DelayedHidePanels(hidePanelsDelay)); + + m_DropCam.Show(false); + + m_GrabWidgetState = GrabWidgetState.None; + + UpdateDraftingVisibility(); + + m_DisableWorldGrabbing = false; + } + + private IEnumerator DelayedHidePanels(int frames) + { + int stall = frames; + while (stall-- > 0) + { + yield return null; + } + + m_PanelManager.HidePanelsForStartup(); + RequestPanelsVisibility(false); + } + + void Update() + { + // TODO: we need to figure out what transform to pass in here! + // Maybe best _just for now_ to use the scene transform? + TrTransform scenePose = App.Scene.Pose; + Shader.SetGlobalMatrix("xf_CS", scenePose.ToMatrix4x4()); + Shader.SetGlobalMatrix("xf_I_CS", scenePose.inverse.ToMatrix4x4()); + } + + void LateUpdate() + { + // Gracefully exits if we're not recording a video. + VideoRecorderUtils.SerializerNewUsdFrame(); + } + + public bool IsFreepaintToolReady() + { + return + !m_PinCushion.IsShowing() && + !PointerManager.m_Instance.IsStraightEdgeProxyActive() && + !InputManager.m_Instance.ControllersAreSwapping() && + (m_SketchSurfacePanel.IsSketchSurfaceToolActive() || + (m_SketchSurfacePanel.GetCurrentToolType() == BaseTool.ToolType.FreePaintTool)) + ; + } + + public void UpdateControls() + { + UnityEngine.Profiling.Profiler.BeginSample("SketchControlsScript.UpdateControls"); + m_SketchSurfacePanel.m_UpdatedToolThisFrame = false; + + // Verify controllers are available and prune state if they're not. + if ((App.VrSdk.GetControllerDof() == VrSdk.DoF.Six && + App.VrSdk.IsInitializingUnityXR) && App.VrSdk.IsHmdInitialized()) + { + m_PanelManager.SetVisible(false); + PointerManager.m_Instance.RequestPointerRendering(false); + return; + } + + //mouse movement + Vector2 mv = InputManager.m_Instance.GetMouseMoveDelta(); + m_MouseDeltaX = mv.x; + m_MouseDeltaY = mv.y; + + UpdateGazeObjectsAnimationState(); + UpdateCurrentGazeRay(); + m_SketchSurfacePanel.SetBacksideActive(m_CurrentGazeRay.origin); + m_PanelManager.UpdatePanels(); + + m_MouseDeltaXScaled = m_MouseDeltaX * GetAppropriateMovementScalar(); + m_MouseDeltaYScaled = m_MouseDeltaY * GetAppropriateMovementScalar(); + + //this is used for one-shot inputs that don't require state and do not change state + UpdateBaseInput(); + + UpdatePinCushionVisibility(); + + //if the pointer manager is processing, we don't want to respond to input + if (!PointerManager.m_Instance.IsMainPointerProcessingLine()) + { + + //see if we're grabbing a widget + UpdateGrab(); + + //see if we're looking at a gaze object + RefreshCurrentGazeObject(); + + // Tools allowed when widgets aren't grabbed. + bool bWidgetGrabOK = m_GrabWidgetState == GrabWidgetState.None; + + // If we don't have a widget held and we're not grabbing the world with the brush controller, + // update tools. + if (bWidgetGrabOK && !m_GrabBrush.grabbingWorld) + { + if (m_CurrentGazeObject != -1 && !m_WorldBeingGrabbed) + { + UpdateActiveGazeObject(); + + // Allow for standard input (like Undo / Redo) even when gazing at a panel. + if (m_CurrentInputState == InputState.Standard) + { + UpdateStandardInput(); + } + } + else + { + //standard input, no gaze object + if (m_InputStateConfigs[(int)m_CurrentInputState].m_AllowMovement) + { + m_SketchSurfacePanel.UpdateReticleOffset(m_MouseDeltaX, m_MouseDeltaY); + } + + switch (m_CurrentInputState) + { + case InputState.Standard: + UpdateStandardInput(); + break; + case InputState.Pan: + UpdatePanInput(); + break; + case InputState.Rotation: + UpdateRotationInput(); + break; + case InputState.HeadLock: + UpdateHeadLockInput(); + break; + case InputState.ControllerLock: + UpdateControllerLock(); + break; + case InputState.PushPull: + UpdatePushPullInput(); + break; + case InputState.Save: + UpdateSaveInput(); + break; + case InputState.Load: + UpdateLoadInput(); + break; + } + + //keep pointer locked in the right spot, even if it's hidden + if (m_SketchSurfacePanel.ActiveTool.LockPointerToSketchSurface()) + { + Vector3 vPointerPos = Vector3.zero; + Vector3 vPointerForward = Vector3.zero; + m_SketchSurfacePanel.GetReticleTransform(out vPointerPos, out vPointerForward, + (m_ControlsType == ControlsType.ViewingOnly)); + PointerManager.m_Instance.SetMainPointerPosition(vPointerPos); + PointerManager.m_Instance.SetMainPointerForward(vPointerForward); + } + + m_SketchSurfacePanel.AllowDrawing(m_InputStateConfigs[(int)m_CurrentInputState].m_AllowDrawing); + m_SketchSurfacePanel.UpdateCurrentTool(); + + PointerManager.m_Instance.AllowPointerPreviewLine(IsFreepaintToolReady()); + //keep transform gizmo at sketch surface pos + m_TransformGizmo.transform.position = m_SketchSurface.transform.position; + bool bGizmoActive = m_InputStateConfigs[(int)m_CurrentInputState].m_ShowGizmo && m_SketchSurfacePanel.ShouldShowTransformGizmo(); + m_TransformGizmo.SetActive(bGizmoActive); + } + } + } + + // Update any transition to a scene transform reset. + UpdateWorldTransformReset(); + + //update our line after all input and tools have chimed in on the state of it + PointerManager.m_Instance.UpdateLine(); + UnityEngine.Profiling.Profiler.EndSample(); + } + + public void UpdateControlsPostIntro() + { + m_PanelManager.UpdatePanels(); + UpdateCurrentGazeRay(); + UpdateGazeObjectsAnimationState(); + RefreshCurrentGazeObject(); + UpdateSwapControllers(); + if (m_CurrentGazeObject > -1) + { + UpdateActiveGazeObject(); + } + } + + public void UpdateControlsForLoading() + { + UpdateCurrentGazeRay(); + m_PanelManager.UpdatePanels(); + UpdateGazeObjectsAnimationState(); + UpdateGrab(); + UpdateWorldTransformReset(); + + if (m_GrabWidgetState == GrabWidgetState.None && m_CurrentGazeObject == -1 && + m_SketchSurfacePanel.ActiveTool.AvailableDuringLoading() && + !m_GrabBrush.grabbingWorld) + { + m_SketchSurfacePanel.UpdateCurrentTool(); + } + } + + public void UpdateControlsForReset() + { + UpdateGrab(); + UpdateCurrentGazeRay(); + UpdatePinCushionVisibility(); + m_PanelManager.UpdatePanels(); + UpdateGazeObjectsAnimationState(); + PointerManager.m_Instance.UpdateLine(); + } + + public void UpdateControlsForUploading() + { + UpdateCurrentGazeRay(); + UpdatePinCushionVisibility(); + m_PanelManager.UpdatePanels(); + UpdateGazeObjectsAnimationState(); + } + + public void UpdateControlsForMemoryExceeded() + { + UpdateGrab(); + m_SketchSurfacePanel.m_UpdatedToolThisFrame = false; + m_PanelManager.UpdatePanels(); + UpdateCurrentGazeRay(); + UpdateGazeObjectsAnimationState(); + RefreshCurrentGazeObject(); + if (m_CurrentGazeObject > -1) + { + UpdateActiveGazeObject(); + } + } + + void UpdatePinCushionVisibility() + { + // If the pin cushion is showing and the user cancels, eat the input. + // if (m_PinCushion.IsShowing()) + // { + // if (InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Activate) || + // InputManager.Brush.GetControllerGrip() || + // InputManager.Wand.GetControllerGrip() || + // IsUserInteractingWithAnyWidget() || + // IsUserInteractingWithUI()) + // { + // m_EatPinCushionInput = true; + // } + // } + + // If our tool wants the input blocked, maintain the input eat state until + // after the user has let off input. + if (m_SketchSurfacePanel.ActiveTool.BlockPinCushion() || !CanUsePinCushion()) + { + m_EatPinCushionInput = true; + } + + bool show = + InputManager.m_Instance.GetCommand(InputManager.SketchCommands.ShowPinCushion); + m_PinCushion.ShowPinCushion(show && !m_EatPinCushionInput); + m_EatPinCushionInput = m_EatPinCushionInput && show; + } + + bool CanUsePinCushion() + { + return (m_ControlsType == ControlsType.SixDofControllers) && + m_PanelManager.AdvancedModeActive() && + !InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Activate) && + !InputManager.Brush.GetControllerGrip() && + !InputManager.Wand.GetControllerGrip() && + !IsUserInteractingWithAnyWidget() && + !IsUserInteractingWithUI() && + !m_SketchSurfacePanel.ActiveTool.BlockPinCushion() && + App.Instance.IsInStateThatAllowsPainting(); + } + + void UpdateCurrentGazeRay() + { + var head = ViewpointScript.Head; + m_CurrentGazeRay = new Ray(head.position, head.forward); + m_CurrentHeadOrientation = head.rotation; + + // We use the gaze ray for certain shader effects - like edge falloff. + Shader.SetGlobalVector("_WorldSpaceRootCameraPosition", m_CurrentGazeRay.origin); + bool hasController = m_ControlsType == ControlsType.SixDofControllers; + if (hasController) + { + if (InputManager.Brush.IsTrackedObjectValid) + { + Transform rAttachPoint = InputManager.m_Instance.GetBrushControllerAttachPoint(); + m_GazeControllerRay.direction = rAttachPoint.forward; + m_GazeControllerRay.origin = rAttachPoint.position; + } + else + { + // If the brush controller isn't tracked, put our controller ray out of the way. + float fBig = 9999999.0f; + m_GazeControllerRay.direction = Vector3.one; + m_GazeControllerRay.origin = new Vector3(fBig, fBig, fBig); + } + + m_GazeControllerRayActivePanel.direction = m_GazeControllerRay.direction; + m_GazeControllerRayActivePanel.origin = m_GazeControllerRay.origin; + m_GazeControllerRayActivePanel.origin -= (m_GazeControllerRayActivePanel.direction * 0.5f); + } + } + + public void UpdateGazeObjectsAnimationState() + { + // Are the panels allowed to be visible? + bool isSixDof = m_ControlsType == ControlsType.SixDofControllers; + if ((!isSixDof) || + (InputManager.Wand.IsTrackedObjectValid && + !m_SketchSurfacePanel.ActiveTool.HidePanels() && + !App.Instance.IsLoading())) + { + // Transition panels according to requested visibility. + m_PanelManager.SetVisible(m_PanelsVisibilityRequested); + } + else + { + // Transition out. + m_PanelManager.SetVisible(false); + } + } + + void UpdateBaseInput() + { + UnityEngine.Profiling.Profiler.BeginSample("SketchControlScript.UpdateBaseInput"); + if (m_ControlsType == ControlsType.SixDofControllers) + { + m_PanelManager.UpdateWandOrientationControls(); + } + + //allow tool scaling if we're not drawing and our input device is active + bool bScaleInputActive = InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Scale); + bool bScaleCommandActive = + bScaleInputActive + && !InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Activate) + && m_GrabBrush.grabbingWorld == false + && m_CurrentGazeObject == -1 // free up swipe for use by gaze object + && (m_ControlsType != ControlsType.SixDofControllers || InputManager.Brush.IsTrackedObjectValid) // TODO:Mikesky - very hacky + && SketchSurfacePanel.m_Instance.ActiveTool.m_Type != BaseTool.ToolType.MultiCamTool; + + if (m_EatToolScaleInput) + { + m_EatToolScaleInput = bScaleInputActive; + } + + if (bScaleCommandActive && !m_EatToolScaleInput) + { + if (m_GrabWidgetState == GrabWidgetState.None) + { + //send scale command down to current tool + m_SketchSurfacePanel.UpdateToolSize( + m_AdjustToolSizeScalar * InputManager.m_Instance.GetAdjustedBrushScrollAmount()); + } + + //ugly, but brush size is becoming not an input state + m_MouseDeltaX = 0.0f; + m_MouseDeltaY = 0.0f; + } + + UpdateSwapControllers(); + UnityEngine.Profiling.Profiler.EndSample(); + } + + void UpdateSwapControllers() + { + // Don't allow controller swap in first run intro. + // Don't allow controller swap if we're grabbing a widget. + // Don't allow controller swap if a Logitech pen is present. + if (!TutorialManager.m_Instance.TutorialActive() && + m_GrabWidgetState == GrabWidgetState.None && + !App.VrSdk.VrControls.LogitechPenIsPresent()) + { + if (InputManager.m_Instance.GetCommandDown(InputManager.SketchCommands.SwapControls)) + { + DoSwapControls(); + } + } + } + + public static void DoSwapControls() + { + InputManager.m_Instance.WandOnRight = !InputManager.m_Instance.WandOnRight; + InputManager.m_Instance.GetControllerBehavior(InputManager.ControllerName.Brush) + .DisplayControllerSwapAnimation(); + InputManager.m_Instance.GetControllerBehavior(InputManager.ControllerName.Wand) + .DisplayControllerSwapAnimation(); + AudioManager.m_Instance.PlayControllerSwapSound( + InputManager.m_Instance.GetControllerPosition(InputManager.ControllerName.Brush)); + } + + void UpdateStandardInput() + { + UnityEngine.Profiling.Profiler.BeginSample("SketchControlScript.UpdateStandardInput"); + //debug keys + if (App.UserConfig.Flags.AdvancedKeyboardShortcuts) + { + var camTool = SketchSurfacePanel.m_Instance.ActiveTool as MultiCamTool; + + if (InputManager.m_Instance.GetKeyboardShortcutDown(InputManager.KeyboardShortcut.SaveNew)) + { + IssueGlobalCommand(GlobalCommands.SaveNew, 1); + } + else if (InputManager.m_Instance.GetKeyboardShortcutDown( + InputManager.KeyboardShortcut.SwitchCamera) && camTool != null) + { + camTool.ExternalObjectNextCameraStyle(); // For monoscopic mode + } + else if (InputManager.m_Instance.GetKeyboardShortcutDown( + InputManager.KeyboardShortcut.ViewOnly)) + { + IssueGlobalCommand(GlobalCommands.ViewOnly); + } + else if (InputManager.m_Instance.GetKeyboardShortcutDown( + InputManager.KeyboardShortcut.ToggleScreenMirroring)) + { + ViewpointScript.m_Instance.ToggleScreenMirroring(); + } + else if (InputManager.m_Instance.GetKeyboardShortcutDown( + InputManager.KeyboardShortcut.PreviousTool)) + { + m_SketchSurfacePanel.PreviousTool(); + } + else if (InputManager.m_Instance.GetKeyboardShortcutDown( + InputManager.KeyboardShortcut.NextTool)) + { + m_SketchSurfacePanel.NextTool(); + } + else if (InputManager.m_Instance.GetKeyboardShortcutDown( + InputManager.KeyboardShortcut.CycleSymmetryMode)) + { + var cur = PointerManager.m_Instance.CurrentSymmetryMode; + var next = (cur == SymmetryMode.None) ? SymmetryMode.SinglePlane + : (cur == SymmetryMode.SinglePlane) ? SymmetryMode.DebugMultiple + : (cur == SymmetryMode.DebugMultiple) ? SymmetryMode.MultiMirror + : (cur == SymmetryMode.MultiMirror) ? SymmetryMode.TwoHanded + : SymmetryMode.None; + PointerManager.m_Instance.CurrentSymmetryMode = next; + } + else if (InputManager.m_Instance.GetKeyboardShortcutDown( + InputManager.KeyboardShortcut.Export)) + { + StartCoroutine(ExportCoroutine()); + } + else if (InputManager.m_Instance.GetKeyboardShortcutDown( + InputManager.KeyboardShortcut.StoreHeadTransform) && + InputManager.m_Instance.GetAnyShift()) + { + Transform head = ViewpointScript.Head; + PlayerPrefs.SetFloat("HeadOffset_localPositionX", head.localPosition.x); + PlayerPrefs.SetFloat("HeadOffset_localPositionY", head.localPosition.y); + PlayerPrefs.SetFloat("HeadOffset_localPositionZ", head.localPosition.z); + PlayerPrefs.SetFloat("HeadOffset_localRotationX", head.localRotation.x); + PlayerPrefs.SetFloat("HeadOffset_localRotationY", head.localRotation.y); + PlayerPrefs.SetFloat("HeadOffset_localRotationZ", head.localRotation.z); + PlayerPrefs.SetFloat("HeadOffset_localRotationW", head.localRotation.w); + } + else if (InputManager.m_Instance.GetKeyboardShortcutDown( + InputManager.KeyboardShortcut.RecallHeadTransform)) + { + Transform head = ViewpointScript.Head; + // Toggle the head offset. + if (m_HeadOffset) + { + // Remove the offset. + Transform originalParent = head.parent; + head.SetParent(head.parent.parent); + GameObject.DestroyImmediate(originalParent.gameObject); + m_HeadOffset = false; + } + else + { + // Add the offset. + GameObject newParent = new GameObject(); + newParent.transform.SetParent(head.parent); + newParent.transform.localPosition = Vector3.zero; + newParent.transform.localRotation = Quaternion.identity; + newParent.transform.localScale = Vector3.one; + head.SetParent(newParent.transform); + TrTransform offsetTransform = TrTransform.TR( + new Vector3( + PlayerPrefs.GetFloat("HeadOffset_localPositionX", 0), + PlayerPrefs.GetFloat("HeadOffset_localPositionY", 1.5f), + PlayerPrefs.GetFloat("HeadOffset_localPositionZ", 0)), + new Quaternion( + PlayerPrefs.GetFloat("HeadOffset_localRotationX", 0), + PlayerPrefs.GetFloat("HeadOffset_localRotationY", 0), + PlayerPrefs.GetFloat("HeadOffset_localRotationZ", 0), + PlayerPrefs.GetFloat("HeadOffset_localRotationW", 1))); + TrTransform originalTransformInverse = TrTransform.FromLocalTransform(head).inverse; + TrTransform newParentTransform = offsetTransform * originalTransformInverse; + newParent.transform.localPosition = newParentTransform.translation; + newParent.transform.localRotation = newParentTransform.rotation; + m_HeadOffset = true; + } + } + else if (InputManager.m_Instance.GetKeyboardShortcutDown( + InputManager.KeyboardShortcut.ToggleLightType)) + { + // Toggle between per-pixel & SH lighting on the secondary directional light + Light secondaryLight = App.Scene.GetLight((1)); + if (LightRenderMode.ForceVertex == secondaryLight.renderMode) + { + secondaryLight.renderMode = LightRenderMode.ForcePixel; + } + else + { + secondaryLight.renderMode = LightRenderMode.ForceVertex; + } + } + else if (InputManager.m_Instance.GetKeyboardShortcutDown( + InputManager.KeyboardShortcut.TossWidget)) + { + m_WidgetManager.TossNearestWidget(); + } + else if (InputManager.m_Instance.GetKeyboardShortcutDown( + InputManager.KeyboardShortcut.Reset)) + { + App.Instance.SetDesiredState(App.AppState.LoadingBrushesAndLighting); + } + else if (InputManager.m_Instance.GetKeyboardShortcutDown( + InputManager.KeyboardShortcut.FlyMode)) + { + SketchSurfacePanel.m_Instance.EnableSpecificTool(BaseTool.ToolType.FlyTool); + } + else if (App.Config.m_ToggleProfileOnAppButton && + (InputManager.Wand.GetVrInputDown(VrInput.Button03) || + InputManager.m_Instance.GetKeyboardShortcutDown( + InputManager.KeyboardShortcut.ToggleProfile))) + { + IssueGlobalCommand(GlobalCommands.ToggleProfiling); + } + } + +#if DEBUG + if (InputManager.m_Instance.GetKeyboardShortcutDown( + InputManager.KeyboardShortcut.CheckStrokes)) + { + bool value = !SketchMemoryScript.m_Instance.m_SanityCheckStrokes; + string feature = "Stroke determinism checking"; + SketchMemoryScript.m_Instance.m_SanityCheckStrokes = value; + OutputWindowScript.m_Instance.CreateInfoCardAtController( + InputManager.ControllerName.Brush, + feature + (value ? ": On" : ": Off")); + } +#endif + + bool hasController = m_ControlsType == ControlsType.SixDofControllers; + var mouse = Mouse.current; + + // Toggle default tool. + if (!m_PanelManager.AdvancedModeActive() && + InputManager.m_Instance.GetCommandDown(InputManager.SketchCommands.ToggleDefaultTool) && + !m_SketchSurfacePanel.IsDefaultToolEnabled() && + m_SketchSurfacePanel.ActiveTool.AllowDefaultToolToggle() && m_CurrentGazeObject == -1)// don't allow tool to change while pointing at panel because there is no visual indication + { + m_SketchSurfacePanel.EnableDefaultTool(); + AudioManager.m_Instance.PlayPinCushionSound(true); + } + // Pan. + else if (!hasController && mouse.rightButton.isPressed) + { + SwitchState(InputState.Pan); + } + // Controller lock (this must be before rotate/head lock!). + else if (!hasController && + InputManager.m_Instance.GetCommand(InputManager.SketchCommands.LockToController)) + { + SwitchState(InputState.ControllerLock); + } + // Rotate. + else if (!hasController && + InputManager.m_Instance.GetCommand(InputManager.SketchCommands.PivotRotation)) + { + SwitchState(InputState.Rotation); + } + // Head lock. + else if (!hasController && + InputManager.m_Instance.GetCommand(InputManager.SketchCommands.LockToHead)) + { + SwitchState(InputState.HeadLock); + } + // Push pull. + else if (!hasController && + InputManager.m_Instance.GetCommand(InputManager.SketchCommands.AltActivate)) + { + SwitchState(InputState.PushPull); + } + else if (!PointerManager.m_Instance.IsMainPointerCreatingStroke()) + { + // Reset surface. + if (!hasController && + InputManager.m_Instance.GetCommandDown(InputManager.SketchCommands.Reset)) + { + ResetGrabbedPose(); + } + // Undo. + else if (InputManager.m_Instance.GetCommandDown(InputManager.SketchCommands.Undo) && + CanUndo()) + { + IssueGlobalCommand(GlobalCommands.Undo); + } + else if (InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Undo) && + CanUndo() && ShouldRepeatUndo()) + { + m_UndoHold_Timer = m_UndoRedoHold_RepeatInterval; + IssueGlobalCommand(GlobalCommands.Undo); + } + // Redo. + else if (InputManager.m_Instance.GetCommandDown(InputManager.SketchCommands.Redo) && + CanRedo()) + { + IssueGlobalCommand(GlobalCommands.Redo); + } + else if (InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Redo) && + CanRedo() && ShouldRepeatRedo()) + { + m_RedoHold_Timer = m_UndoRedoHold_RepeatInterval; + IssueGlobalCommand(GlobalCommands.Redo); + } + // Reset scene. + else if (!hasController && + InputManager.m_Instance.GetKeyboardShortcutDown( + InputManager.KeyboardShortcut.ResetScene)) + { + // TODO: Should thsi go away? Seems like the "sweetspot" may no longer be used. + if (App.VrSdk.GetControllerDof() == VrSdk.DoF.Two) + { + m_PanelManager.SetSweetSpotPosition(m_CurrentGazeRay.origin); + ResetGrabbedPose(); + } + } + // Straight edge. + else if (!hasController && + InputManager.m_Instance.GetKeyboardShortcutDown( + InputManager.KeyboardShortcut.StraightEdge)) + { + IssueGlobalCommand(GlobalCommands.StraightEdge); + } + // Always fall back on switching tools. + else + { + m_SketchSurfacePanel.CheckForToolSelection(); + } + } + + // Reset undo/redo hold timers. + if (!InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Undo)) + { + m_UndoHold_Timer = m_UndoRedoHold_DurationBeforeStart; + } + if (!InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Redo)) + { + m_RedoHold_Timer = m_UndoRedoHold_DurationBeforeStart; + } + UnityEngine.Profiling.Profiler.EndSample(); + } + + bool CanUndo() + { + return SketchMemoryScript.m_Instance.CanUndo() && + !IsUIBlockingUndoRedo() && + m_PanelManager.GazePanelsAreVisible() && + !m_GrabWand.grabbingWorld && + !InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Activate) && + !SelectionManager.m_Instance.IsAnimatingTossFromGrabbingGroup; + } + + bool CanRedo() + { + return SketchMemoryScript.m_Instance.CanRedo() && + !IsUIBlockingUndoRedo() && + m_PanelManager.GazePanelsAreVisible() && + !m_GrabBrush.grabbingWorld && + !InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Activate) && + !SelectionManager.m_Instance.IsAnimatingTossFromGrabbingGroup; + } + + bool ShouldRepeatUndo() + { + m_UndoHold_Timer -= Time.deltaTime; + return (m_UndoHold_Timer <= 0.0f); + } + + bool ShouldRepeatRedo() + { + m_RedoHold_Timer -= Time.deltaTime; + return (m_RedoHold_Timer <= 0.0f); + } + + // Updates the global state: + // m_CurrentGrabWidget + void UpdateGrab() + { + UnityEngine.Profiling.Profiler.BeginSample("SketchControlScript.UpdateGrab"); + if (m_ControlsType != ControlsType.SixDofControllers) + { + UnityEngine.Profiling.Profiler.EndSample(); + return; + } + + GrabWidget rPrevGrabWidget = m_CurrentGrabWidget; + GrabWidget rPrevPotentialBrush = m_PotentialGrabWidgetBrush; + GrabWidget rPrevPotentialWand = m_PotentialGrabWidgetWand; + if (m_CurrentGrabWidget) + { + m_CurrentGrabWidget.Activate(false); + } + if (m_PotentialGrabWidgetBrush) + { + m_PotentialGrabWidgetBrush.Activate(false); + } + if (m_PotentialGrabWidgetWand) + { + m_PotentialGrabWidgetWand.Activate(false); + } + m_CurrentGrabWidget = null; + m_PotentialGrabWidgetBrush = null; + m_PotentialGrabWidgetWand = null; + m_PotentialGrabWidgetBrushValid = false; + m_PotentialGrabWidgetWandValid = false; + + m_WidgetManager.RefreshNearestWidgetLists(m_CurrentGazeRay, m_CurrentGazeObject); + + if (m_GrabWidgetState == GrabWidgetState.None) + { + UpdateGrab_WasNone(rPrevPotentialBrush, rPrevPotentialWand); + } + else if (m_GrabWidgetState == GrabWidgetState.OneHand) + { + UpdateGrab_WasOneHand(rPrevGrabWidget); + } + else if (m_GrabWidgetState == GrabWidgetState.TwoHands) + { + UpdateGrab_WasTwoHands(rPrevGrabWidget); + } + + // Update grab intersection state. + switch (m_CurrentGrabIntersectionState) + { + case GrabIntersectionState.RequestIntersections: + m_CurrentGrabIntersectionState = GrabIntersectionState.ReadBrush; + break; + case GrabIntersectionState.ReadBrush: + m_CurrentGrabIntersectionState = GrabIntersectionState.ReadWand; + break; + case GrabIntersectionState.ReadWand: + m_CurrentGrabIntersectionState = GrabIntersectionState.RequestIntersections; + break; + } + + if (!TutorialManager.m_Instance.TutorialActive() && m_CurrentGrabWidget == null) + { + UpdateGrab_World(); + } + + App.Instance.SelectionEffect.HighlightForGrab( + m_GrabWidgetState != GrabWidgetState.None || + (m_PotentialGrabWidgetBrush != null && m_PotentialGrabWidgetBrushValid) || + (m_PotentialGrabWidgetWand != null && m_PotentialGrabWidgetWandValid)); + UnityEngine.Profiling.Profiler.EndSample(); + } + + void UpdateGrab_WasNone(GrabWidget rPrevPotentialBrush, GrabWidget rPrevPotentialWand) + { + // if a panel isn't in focus, allow for widget grab + // We can grab a widget as long as we aren't trying to draw with that hand. + bool bActiveInput = + (InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Activate) && + App.Instance.IsInStateThatAllowsPainting()); + + //certain tools don't allow us to mess with widgets + bool bWidgetManipOK = m_SketchSurfacePanel.DoesCurrentToolAllowWidgetManipulation() && + !m_GrabWand.grabbingWorld && !m_GrabBrush.grabbingWorld && IsGrabWorldStateStable() && + App.Instance.IsInStateThatAllowsAnyGrabbing(); + + // Update EatInput flags if they're valid. + if (m_GrabBrush.eatInput) + { + m_GrabBrush.eatInput = InputManager.Brush.GetControllerGrip(); + } + if (m_GrabWand.eatInput) + { + m_GrabWand.eatInput = InputManager.Wand.GetControllerGrip(); + } + + bool bShouldClearWandInside = false; + if (m_CurrentInputState == InputState.Standard && bWidgetManipOK) + { + // If we're in the intersection request state, fire off a new intersection request. If + // we're in the read brush state, update our brush grab data structure. + List brushBests = m_WidgetManager.WidgetsNearBrush; + if (m_CurrentGrabIntersectionState == GrabIntersectionState.RequestIntersections) + { + RequestWidgetIntersection(brushBests, InputManager.ControllerName.Brush); + } + else if (m_CurrentGrabIntersectionState == GrabIntersectionState.ReadBrush) + { + m_BackupBrushGrabData = GetBestWidget(brushBests, m_BrushResults); + } + + if (m_BackupBrushGrabData != null) + { + m_PotentialGrabWidgetBrush = m_BackupBrushGrabData.m_WidgetScript; + + // Allow widget grab if we're not painting. + if (!bActiveInput) + { + m_PotentialGrabWidgetBrush.Activate(true); + m_PotentialGrabWidgetBrushValid = true; + m_PotentialGrabWidgetBrush.VisualizePinState(); + + if (!m_GrabBrush.eatInput && InputManager.Brush.GetControllerGrip()) + { + m_CurrentGrabWidget = m_PotentialGrabWidgetBrush; + if (m_CurrentGrabWidget.Group != SketchGroupTag.None) + { + m_GrabBrush.grabbingGroup = true; + m_CurrentGrabWidget = + SelectionManager.m_Instance.StartGrabbingGroupWithWidget(m_CurrentGrabWidget); + } + UpdateGrab_NoneToOne(InputManager.ControllerName.Brush); + bShouldClearWandInside = true; + m_GrabBrush.startedGrabInsideWidget = true; + } + } + } + m_GrabBrush.SetHadBestGrabAndTriggerHaptics(m_BackupBrushGrabData); + m_ControllerGrabVisuals.BrushInWidgetRange = m_BackupBrushGrabData != null; + + // If we're in the intersection request state, fire off a new intersection request. If + // we're in the read wand state, update our wand grab data structure. + List wandBests = m_WidgetManager.WidgetsNearWand; + if (m_CurrentGrabIntersectionState == GrabIntersectionState.RequestIntersections) + { + RequestWidgetIntersection(wandBests, InputManager.ControllerName.Wand); + } + else if (m_CurrentGrabIntersectionState == GrabIntersectionState.ReadWand) + { + m_BackupWandGrabData = GetBestWidget(wandBests, m_WandResults); + } + + if (m_BackupWandGrabData != null) + { + m_PotentialGrabWidgetWand = m_BackupWandGrabData.m_WidgetScript; + // Allow wand widget grab if brush grab failed. + bool bGrabAllowed = (m_GrabWidgetState == GrabWidgetState.None) && !bActiveInput; + if (bGrabAllowed) + { + m_PotentialGrabWidgetWand.Activate(true); + m_PotentialGrabWidgetWandValid = true; + m_PotentialGrabWidgetWand.VisualizePinState(); + + if (!m_GrabWand.eatInput && InputManager.Wand.GetControllerGrip()) + { + m_CurrentGrabWidget = m_PotentialGrabWidgetWand; + if (m_CurrentGrabWidget.Group != SketchGroupTag.None) + { + m_GrabWand.grabbingGroup = true; + m_CurrentGrabWidget = + SelectionManager.m_Instance.StartGrabbingGroupWithWidget(m_CurrentGrabWidget); + } + UpdateGrab_NoneToOne(InputManager.ControllerName.Wand); + m_GrabBrush.ClearInsideWidget(); + m_GrabWand.startedGrabInsideWidget = true; + } + } + } + m_GrabWand.SetHadBestGrabAndTriggerHaptics(m_BackupWandGrabData); + m_ControllerGrabVisuals.WandInWidgetRange = m_BackupWandGrabData != null; + + // Account for asymmetry in controller processing by clearing after wand has updated + // GrabState.insideWidget according to bestWandGrab. + if (bShouldClearWandInside) + { + m_GrabWand.ClearInsideWidget(); + } + } + + // Update widget collisions if we've got a drifter. + if (m_GrabWidgetState == GrabWidgetState.None) + { + if (m_WidgetManager.ShouldUpdateCollisions()) + { + m_PanelManager.DoCollisionSimulationForWidgetPanels(); + } + } + } + + void UpdateGrab_WasOneHand(GrabWidget rPrevGrabWidget) + { + var controller = InputManager.Controllers[(int)m_GrabWidgetOneHandInfo.m_Name]; + bool shouldRelease = !App.Instance.IsInStateThatAllowsAnyGrabbing(); + if (!InputManager.Controllers[(int)m_GrabWidgetOneHandInfo.m_Name].GetControllerGrip() || + shouldRelease) + { + if (shouldRelease) + { + EatGrabInput(); + } + + Vector3 vLinearVelocity; + Vector3 vAngularVelocity; + if (GetGrabWidgetHoldHistory(out vLinearVelocity, out vAngularVelocity)) + { + rPrevGrabWidget.SetVelocities( + vLinearVelocity, vAngularVelocity, + controller.Transform.position); + } + // One -> None + UpdateGrab_ToNone(rPrevGrabWidget); + } + else + { + // Keep holding on to our widget. + m_CurrentGrabWidget = rPrevGrabWidget; + m_CurrentGrabWidget.Activate(true); + m_CurrentGrabWidget.UserInteracting(true, m_GrabWidgetOneHandInfo.m_Name); + + if (!m_CurrentGrabWidget.Pinned) + { + var info = InputManager.Controllers[(int)m_GrabWidgetOneHandInfo.m_Name]; + var controllerXf = Coords.AsGlobal[info.Transform]; + var newWidgetXf = controllerXf * m_GrabWidgetOneHandInfo.m_BaseWidgetXf_LS; + m_CurrentGrabWidget.RecordAndSetPosRot(newWidgetXf); + + UpdateGrabWidgetHoldHistory(m_GrabWidgetOneHandInfo.m_Name); + } + + m_PanelManager.DoCollisionSimulationForWidgetPanels(); + + // Check for widget pinning. + if (m_CurrentGrabWidget.AllowPinning) + { + if (InputManager.Controllers[(int)m_GrabWidgetOneHandInfo.m_Name].GetCommandDown( + InputManager.SketchCommands.PinWidget)) + { + // If the user initiates a pin action, buzz a bit. + if (!m_CurrentGrabWidget.Pinned) + { + InputManager.m_Instance.TriggerHapticsPulse( + m_GrabWidgetOneHandInfo.m_Name, 3, 0.10f, 0.07f); + } + m_CurrentGrabWidget.Pin(!m_CurrentGrabWidget.Pinned); + SketchSurfacePanel.m_Instance.EatToolsInput(); + m_WidgetManager.RefreshPinAndUnpinLists(); + } + } + + if (m_CurrentGrabWidget is SelectionWidget) + { + if (InputManager.m_Instance.GetCommandDown( + InputManager.SketchCommands.DuplicateSelection)) + { + controller.LastHeldInput = + controller.GetCommandHoldInput(InputManager.SketchCommands.DuplicateSelection); + } + + if (controller.LastHeldInput != null && + InputManager.m_Instance.GetCommandHeld(InputManager.SketchCommands.DuplicateSelection)) + { + SketchControlsScript.m_Instance.IssueGlobalCommand( + SketchControlsScript.GlobalCommands.Duplicate); + } + } + + InputManager.ControllerName otherName = + (m_GrabWidgetOneHandInfo.m_Name == InputManager.ControllerName.Brush) ? + InputManager.ControllerName.Wand : InputManager.ControllerName.Brush; + bool otherInputEaten = + (m_GrabWidgetOneHandInfo.m_Name == InputManager.ControllerName.Brush) ? + m_GrabWand.eatInput : m_GrabBrush.eatInput; + + // See if the other controller decides to grab the widget (unless we're pinned). + if (!m_CurrentGrabWidget.Pinned) + { + if (m_CurrentGrabWidget.AllowTwoHandGrab) + { + if (InputManager.Controllers[(int)otherName].GetControllerGrip()) + { + RequestPanelsVisibility(false); + m_GrabWidgetState = GrabWidgetState.TwoHands; + // Figure out if the new grab starts inside the widget. + Vector3 vOtherGrabPos = TrTransform.FromTransform( + InputManager.m_Instance.GetController(otherName)).translation; + bool bOtherGrabInBounds = m_CurrentGrabWidget.GetActivationScore( + vOtherGrabPos, otherName) >= 0; + m_CurrentGrabWidget.SetUserTwoHandGrabbing( + true, m_GrabWidgetOneHandInfo.m_Name, otherName, bOtherGrabInBounds); + + if (otherName == InputManager.ControllerName.Brush) + { + m_GrabBrush.startedGrabInsideWidget = bOtherGrabInBounds; + } + else + { + m_GrabWand.startedGrabInsideWidget = bOtherGrabInBounds; + } + + m_GrabWidgetTwoHandBrushPrev = TrTransform.FromTransform( + InputManager.m_Instance.GetController(InputManager.ControllerName.Brush)); + m_GrabWidgetTwoHandWandPrev = TrTransform.FromTransform( + InputManager.m_Instance.GetController(InputManager.ControllerName.Wand)); + } + } + } + else if (!otherInputEaten && InputManager.Controllers[(int)otherName].GetControllerGrip()) + { + // If it's a two hand grab but the current grab widget is pinned, grab the world. + UpdateGrab_ToNone(m_CurrentGrabWidget); + m_CurrentGrabWidget = null; + m_ControllerGrabVisuals.SetDesiredVisualState(ControllerGrabVisuals.VisualState.Off); + } + } + } + + // Previous frame was a two-handed grab. + // Handles all the cases where this frame's grab is zero, one, or two hands. + void UpdateGrab_WasTwoHands(GrabWidget rPrevGrabWidget) + { + //keep holding on to our widget + m_CurrentGrabWidget = rPrevGrabWidget; + m_CurrentGrabWidget.Activate(true); + m_CurrentGrabWidget.UserInteracting(true, m_GrabWidgetOneHandInfo.m_Name); + + if (!App.Instance.IsInStateThatAllowsAnyGrabbing()) + { + m_CurrentGrabWidget.SetUserTwoHandGrabbing(false); + UpdateGrab_ToNone(rPrevGrabWidget); + } + else if (!InputManager.Wand.GetControllerGrip()) + { // Look for button release. + m_CurrentGrabWidget.SetUserTwoHandGrabbing(false); + // See if our Brush hand is still within grab range of the widget. + if (m_GrabBrush.startedGrabInsideWidget || + IsControllerNearWidget(InputManager.ControllerName.Brush, m_CurrentGrabWidget)) + { + m_GrabWidgetOneHandInfo.m_Name = InputManager.ControllerName.Brush; + RequestPanelsVisibility(true); + InitializeGrabWidgetControllerInfo(m_GrabWidgetOneHandInfo); + m_GrabWidgetState = GrabWidgetState.OneHand; + } + else + { + // If the Brush hand is beyond the widget, we're not holding it anymore. + UpdateGrab_ToNone(rPrevGrabWidget); + + // Eat input on the brush grip until we release the button. + m_GrabBrush.eatInput = true; + } + } + else if (!InputManager.Brush.GetControllerGrip()) + { + m_CurrentGrabWidget.SetUserTwoHandGrabbing(false); + if (m_GrabWand.startedGrabInsideWidget || + IsControllerNearWidget(InputManager.ControllerName.Wand, m_CurrentGrabWidget)) + { + m_GrabWidgetOneHandInfo.m_Name = InputManager.ControllerName.Wand; + InitializeGrabWidgetControllerInfo(m_GrabWidgetOneHandInfo); + m_GrabWidgetState = GrabWidgetState.OneHand; + } + else + { + UpdateGrab_ToNone(rPrevGrabWidget); + m_GrabWand.eatInput = true; + } + } + else + { + // Both hands still grabbing. + // Check for pin, which forcibly releases one of the hands. + if (m_CurrentGrabWidget.AllowPinning && + InputManager.Controllers[(int)m_GrabWidgetOneHandInfo.m_Name].GetCommandDown( + InputManager.SketchCommands.PinWidget)) + { + // If the user initiates a pin action, buzz a bit. + if (!m_CurrentGrabWidget.Pinned) + { + InputManager.m_Instance.TriggerHapticsPulse( + m_GrabWidgetOneHandInfo.m_Name, 3, 0.10f, 0.07f); + } + + m_CurrentGrabWidget.Pin(!m_CurrentGrabWidget.Pinned); + SketchSurfacePanel.m_Instance.EatToolsInput(); + m_WidgetManager.RefreshPinAndUnpinLists(); + + InitializeGrabWidgetControllerInfo(m_GrabWidgetOneHandInfo); + m_GrabWidgetState = GrabWidgetState.OneHand; + m_CurrentGrabWidget.SetUserTwoHandGrabbing(false); + + // Eat input on the off hand so we don't immediately jump in to world transform. + if (m_GrabWidgetOneHandInfo.m_Name == InputManager.ControllerName.Brush) + { + RequestPanelsVisibility(true); + m_GrabWand.eatInput = true; + } + else + { + m_GrabBrush.eatInput = true; + } + } + + if (!m_CurrentGrabWidget.Pinned) + { + UpdateGrab_ContinuesTwoHands(); + } + } + ClearGrabWidgetHoldHistory(); + m_PanelManager.DoCollisionSimulationForWidgetPanels(); + } + + // Common case for two-handed grab: both the previous and current frames are two-handed. + private void UpdateGrab_ContinuesTwoHands() + { + //holding with two hands, transform accordingly + TrTransform xfBrush = TrTransform.FromTransform(InputManager.Brush.Transform); + TrTransform xfWand = TrTransform.FromTransform(InputManager.Wand.Transform); + Vector2 vSizeRange = m_CurrentGrabWidget.GetWidgetSizeRange(); + + GrabWidget.Axis axis = m_CurrentGrabWidget.GetScaleAxis( + xfWand.translation, xfBrush.translation, + out Vector3 axisDirection, out float axisExtent); + + TrTransform newWidgetXf; + if (axis != GrabWidget.Axis.Invalid) + { + // Scale along a single axis + float deltaScale; + if (App.Config.m_AxisManipulationIsResize) + { + newWidgetXf = MathUtils.TwoPointObjectTransformationAxisResize( + axisDirection, axisExtent, + m_GrabWidgetTwoHandWandPrev, m_GrabWidgetTwoHandBrushPrev, + xfWand, xfBrush, + GetWorkingTransform(m_CurrentGrabWidget), + out deltaScale, + deltaScaleMin: vSizeRange.x / axisExtent, + deltaScaleMax: vSizeRange.y / axisExtent); + } + else + { + newWidgetXf = MathUtils.TwoPointObjectTransformationNonUniformScale( + axisDirection, + m_GrabWidgetTwoHandWandPrev, m_GrabWidgetTwoHandBrushPrev, + xfWand, xfBrush, + GetWorkingTransform(m_CurrentGrabWidget), + out deltaScale, + finalScaleMin: vSizeRange.x, + deltaScaleMin: vSizeRange.x / axisExtent, + deltaScaleMax: vSizeRange.y / axisExtent); + } + + // The above functions return undefined values in newWidgetXf.scale; but that's + // okay because RecordAndSetPosRot ignores xf.scale. + // TODO: do this more cleanly + m_CurrentGrabWidget.RecordAndApplyScaleToAxis(deltaScale, axis); + } + else + { + // Uniform scaling + TrTransform xfObject = GetWorkingTransform(m_CurrentGrabWidget); + Vector3 extents = (m_CurrentGrabWidget is StencilWidget) + ? (m_CurrentGrabWidget as StencilWidget).Extents + : Vector3.one * Mathf.Abs(m_CurrentGrabWidget.GetSignedWidgetSize()); + + // Delta-scale bounds should be based on the smallest/largest extent. + // Irritatingly, the API wants absolute rather than relative scale bounds, + // so they need even more conversion. + float deltaScaleMin = vSizeRange.x / extents.Min(); + float deltaScaleMax = vSizeRange.y / extents.Max(); + if (m_GrabWand.startedGrabInsideWidget && m_GrabBrush.startedGrabInsideWidget) + { + newWidgetXf = MathUtils.TwoPointObjectTransformation( + m_GrabWidgetTwoHandWandPrev, m_GrabWidgetTwoHandBrushPrev, + xfWand, xfBrush, + xfObject, + deltaScaleMin: deltaScaleMin, deltaScaleMax: deltaScaleMax); + } + else if (m_GrabWand.startedGrabInsideWidget) + { + // keep the wand inside the object + newWidgetXf = MathUtils.TwoPointObjectTransformation( + m_GrabWidgetTwoHandWandPrev, m_GrabWidgetTwoHandBrushPrev, + xfWand, xfBrush, + xfObject, + deltaScaleMin: deltaScaleMin, deltaScaleMax: deltaScaleMax, + bUseLeftAsPivot: true); + } + else + { + // keep the brush inside the object (note the brush is the left hand) + newWidgetXf = MathUtils.TwoPointObjectTransformation( + m_GrabWidgetTwoHandBrushPrev, m_GrabWidgetTwoHandWandPrev, + xfBrush, xfWand, + xfObject, + deltaScaleMin: deltaScaleMin, deltaScaleMax: deltaScaleMax, + bUseLeftAsPivot: true); + } + + // Must do separately becvause RecordAndSetPosRot ignores newWidgetXf.scale + m_CurrentGrabWidget.RecordAndSetSize(newWidgetXf.scale); + + float currentSize = Mathf.Abs(m_CurrentGrabWidget.GetSignedWidgetSize()); + if (currentSize == vSizeRange.x || currentSize == vSizeRange.y) + { + InputManager.m_Instance.TriggerHaptics(InputManager.ControllerName.Brush, 0.05f); + InputManager.m_Instance.TriggerHaptics(InputManager.ControllerName.Wand, 0.05f); + } + } + + // Ignores TrTransform.scale + m_CurrentGrabWidget.RecordAndSetPosRot(newWidgetXf); + + m_GrabWidgetTwoHandBrushPrev = xfBrush; + m_GrabWidgetTwoHandWandPrev = xfWand; + } + + void UpdateGrab_NoneToOne(InputManager.ControllerName controllerName) + { + if (m_MaybeDriftingGrabWidget != null && + m_MaybeDriftingGrabWidget.IsMoving() && + !m_MaybeDriftingGrabWidget.IsSpinningFreely) + { + // If a new widget is grabbed but the previous one is still drifting, end the drift. + // TODO: Simplify in the widget animation cleanup. + if (m_MaybeDriftingGrabWidget == m_CurrentGrabWidget) + { + SketchMemoryScript.m_Instance.PerformAndRecordCommand( + new MoveWidgetCommand(m_MaybeDriftingGrabWidget, + m_MaybeDriftingGrabWidget.LocalTransform, m_MaybeDriftingGrabWidget.CustomDimension, + final: true), + discardIfNotMerged: true); + } + m_MaybeDriftingGrabWidget.ClearVelocities(); + } + + // UserInteracting should be the first thing that happens here so OnUserBeginInteracting can + // be called before everything else. + m_CurrentGrabWidget.UserInteracting(true, controllerName); + m_CurrentGrabWidget.ClearVelocities(); + ClearGrabWidgetHoldHistory(); + + //set our info names according to this controller's name + m_GrabWidgetOneHandInfo.m_Name = controllerName; + InitializeGrabWidgetControllerInfo(m_GrabWidgetOneHandInfo); + + PointerManager.m_Instance.AllowPointerPreviewLine(false); + PointerManager.m_Instance.RequestPointerRendering(false); + m_SketchSurfacePanel.RequestHideActiveTool(true); + if (m_GrabWidgetOneHandInfo.m_Name == InputManager.ControllerName.Wand) + { + RequestPanelsVisibility(false); + } + + // Notify visuals. + ControllerGrabVisuals.VisualState visualState = + m_GrabWidgetOneHandInfo.m_Name == InputManager.ControllerName.Brush ? + ControllerGrabVisuals.VisualState.WidgetBrushGrip : + ControllerGrabVisuals.VisualState.WidgetWandGrip; + m_ControllerGrabVisuals.SetDesiredVisualState(visualState); + m_ControllerGrabVisuals.SetHeldWidget(m_CurrentGrabWidget.transform); + + //if a gaze object had focus when we grabbed this widget, take focus off the object + ResetActivePanel(); + m_UIReticle.SetActive(false); + + // Prep all other grab widgets for collision. + m_PanelManager.PrimeCollisionSimForWidgets(m_CurrentGrabWidget); + + m_GrabWidgetState = GrabWidgetState.OneHand; + m_WidgetManager.WidgetsDormant = false; + PointerManager.m_Instance.EatLineEnabledInput(); + + m_BackupWandGrabData = null; + m_BackupBrushGrabData = null; + } + + void UpdateGrab_ToNone(GrabWidget rPrevGrabWidget) + { + m_MaybeDriftingGrabWidget = rPrevGrabWidget; + + m_GrabWidgetState = GrabWidgetState.None; + PointerManager.m_Instance.RequestPointerRendering(!App.Instance.IsLoading() && + m_SketchSurfacePanel.ShouldShowPointer()); + RequestPanelsVisibility(true); + m_SketchSurfacePanel.RequestHideActiveTool(false); + rPrevGrabWidget.UserInteracting(false); + + // Disable grab visuals. + m_ControllerGrabVisuals.SetDesiredVisualState(ControllerGrabVisuals.VisualState.Off); + m_ControllerGrabVisuals.SetHeldWidget(null); + + if (m_GrabBrush.grabbingGroup || m_GrabWand.grabbingGroup) + { + SelectionManager.m_Instance.EndGrabbingGroupWithWidget(); + m_GrabBrush.grabbingGroup = false; + m_GrabWand.grabbingGroup = false; + } + } + + void RequestWidgetIntersection(List candidates, + InputManager.ControllerName controllerName) + { + // Get locals based off what controller we're using. + Queue resultQueue = null; + Vector3 controllerPos = Vector3.zero; + if (controllerName == InputManager.ControllerName.Brush) + { + resultQueue = m_BrushResults; + controllerPos = InputManager.m_Instance.GetBrushControllerAttachPoint().position; + } + else + { + resultQueue = m_WandResults; + controllerPos = InputManager.m_Instance.GetWandControllerAttachPoint().position; + } + + // If we don't have a candidate that has a GPU object, don't bother firing off a GPU request. + bool requestGpuIntersection = false; + + // Fire off a new GPU intersection with all widgets that can use it. + for (int i = 0; i < candidates.Count; ++i) + { + if (candidates[i].m_WidgetScript.HasGPUIntersectionObject()) + { + candidates[i].m_WidgetScript.SetGPUIntersectionObjectLayer(m_WidgetGpuIntersectionLayer); + requestGpuIntersection = true; + } + } + + if (requestGpuIntersection) + { + GpuIntersectionResult newRequest = new GpuIntersectionResult(); + newRequest.resultList = new List(); + newRequest.result = App.Instance.GpuIntersector.RequestModelIntersections( + controllerPos, m_WidgetGpuIntersectionRadius, newRequest.resultList, 8, + (1 << m_WidgetGpuIntersectionLayer)); + + // The new result will only be null when the intersector is disabled. + if (newRequest.result != null) + { + resultQueue.Enqueue(newRequest); + } + + for (int i = 0; i < candidates.Count; ++i) + { + if (candidates[i].m_WidgetScript.HasGPUIntersectionObject()) + { + candidates[i].m_WidgetScript.RestoreGPUIntersectionObjectLayer(); + } + } + } + } + + GrabWidgetData GetBestWidget(List candidates, + Queue resultQueue) + { + // Discard futures that are too old. + while (resultQueue.Count > 0) + { + if (Time.frameCount - resultQueue.Peek().result.StartFrame < 5) + { + break; + } + resultQueue.Dequeue(); + } + + // If the oldest future is ready, use its intersection result to update the candidates. + GpuIntersectionResult finishedResult; + if (resultQueue.Count > 0 && resultQueue.Peek().result.IsReady) + { + finishedResult = resultQueue.Dequeue(); + } + else + { + finishedResult.resultList = new List(); + } + + // TODO: Speed this up. + for (int i = 0; i < candidates.Count; ++i) + { + if (candidates[i].m_WidgetScript.HasGPUIntersectionObject()) + { + // If a candidate can't find itself in the finished results list, it's not eligible. + bool candidateValid = false; + for (int j = 0; j < finishedResult.resultList.Count; ++j) + { + if (candidates[i].m_WidgetScript.Equals(finishedResult.resultList[j].widget)) + { + candidateValid = true; + break; + } + } + + if (candidateValid) + { + // If a candidate has a GPU intersection object and we found it in this list, + // not only is it valid, but it's as valid as it can be. + candidates[i].m_ControllerScore = 1.0f; + } + else + { + candidates[i].m_NearController = false; + } + } + } + + // Run through the candidates and pick + GrabWidgetData best = null; + for (int i = 0; i < candidates.Count; ++i) + { + var candidate = candidates[i]; + if (!candidate.m_NearController) continue; + + // For media widgets - only select from the active layer + if (candidate.m_WidgetScript is MediaWidget + && candidate.m_WidgetScript.Canvas != App.Scene.ActiveCanvas) continue; + + if (best == null || candidate.m_ControllerScore > best.m_ControllerScore) + { + best = candidate; + } + } + return best; + } + + void InitializeGrabWidgetControllerInfo(GrabWidgetControllerInfo info) + { + Transform controller = InputManager.Controllers[(int)info.m_Name].Transform; + Transform widget = m_CurrentGrabWidget.GrabTransform_GS; + TrTransform newWidgetXf = Coords.AsGlobal[widget]; + + info.m_BaseControllerXf = Coords.AsGlobal[controller]; + info.m_BaseWidgetXf_LS = info.m_BaseControllerXf.inverse * newWidgetXf; + } + + // returns the transform of the true widget (not the snapped one for those that can be) + private TrTransform GetWorkingTransform(GrabWidget w) + { + TrTransform ret = w.GetGrabbedTrTransform(); + ret.scale = w.GetSignedWidgetSize(); + return ret; + } + + // Initiate the world transform reset animation. + public void RequestWorldTransformReset(bool toSavedXf = false) + { + if (WorldIsReset(toSavedXf)) + { + return; + } + + m_WorldTransformResetXf = + toSavedXf ? SketchMemoryScript.m_Instance.InitialSketchTransform : TrTransform.identity; + m_WorldTransformResetState = WorldTransformResetState.Requested; + + App.Scene.disableTiltProtection = false; + } + + void UpdateWorldTransformReset() + { + switch (m_WorldTransformResetState) + { + case WorldTransformResetState.Requested: + ViewpointScript.m_Instance.FadeToColor(Color.black, m_GrabWorldFadeSpeed); + m_WorldTransformResetState = WorldTransformResetState.FadingToBlack; + m_xfDropCamReset_RS = Coords.AsRoom[m_DropCam.transform]; + PointerManager.m_Instance.EatLineEnabledInput(); + PointerManager.m_Instance.AllowPointerPreviewLine(false); + break; + case WorldTransformResetState.FadingToBlack: + m_WorldTransformFadeAmount += m_GrabWorldFadeSpeed * Time.deltaTime; + if (m_WorldTransformFadeAmount >= 1.0f) + { + App.Scene.Pose = m_WorldTransformResetXf; + m_WorldTransformFadeAmount = 1.0f; + m_WorldTransformResetState = WorldTransformResetState.FadingToScene; + ViewpointScript.m_Instance.FadeToScene(m_GrabWorldFadeSpeed); + m_DropCam.transform.position = m_xfDropCamReset_RS.translation; + m_DropCam.transform.rotation = m_xfDropCamReset_RS.rotation; + PointerManager.m_Instance.AllowPointerPreviewLine(true); + } + break; + case WorldTransformResetState.FadingToScene: + m_WorldTransformFadeAmount -= m_GrabWorldFadeSpeed * Time.deltaTime; + if (m_WorldTransformFadeAmount <= 0.0f) + { + m_WorldTransformFadeAmount = 0.0f; + m_WorldTransformResetState = WorldTransformResetState.Default; + } + break; + } + } + + bool CheckToggleTiltProtection() + { + if ( + InputManager.Wand.GetCommandDown(InputManager.SketchCommands.Redo) || + InputManager.Brush.GetCommandDown(InputManager.SketchCommands.Redo) + ) + { + App.Scene.disableTiltProtection = !App.Scene.disableTiltProtection; + + return !App.Scene.disableTiltProtection; + } + + return false; + + } + + void UpdateGrab_World() + { + bool bAllowWorldTransform = m_SketchSurfacePanel.ActiveTool.AllowWorldTransformation() && + (m_GrabWorldState != GrabWorldState.ResetDone) && + (!PointerManager.m_Instance.IsMainPointerCreatingStroke() || App.Instance.IsLoading()) && + App.Instance.IsInStateThatAllowsAnyGrabbing() && + !m_DisableWorldGrabbing; + + bool bWorldGrabWandPrev = m_GrabWand.grabbingWorld; + bool bWorldGrabBrushPrev = m_GrabBrush.grabbingWorld; + m_GrabWand.grabbingWorld = bAllowWorldTransform && !m_GrabWand.eatInput && + InputManager.Wand.GetControllerGrip(); + m_GrabBrush.grabbingWorld = bAllowWorldTransform && !m_GrabBrush.eatInput && + InputManager.Brush.GetControllerGrip() && + (m_CurrentGazeObject == -1); + + bool grabsChanged = (bWorldGrabWandPrev != m_GrabWand.grabbingWorld) || + (bWorldGrabBrushPrev != m_GrabBrush.grabbingWorld); + bool bAllowWorldTransformChanged = + bAllowWorldTransform != m_AllowWorldTransformLastFrame; + int nGrabs = m_GrabWand.grabbingWorld ? 1 : 0; + nGrabs += m_GrabBrush.grabbingWorld ? 1 : 0; + + // Allow grabbing again if grabs have changed and we're done resetting. + if (m_GrabWorldState == GrabWorldState.ResetDone && grabsChanged) + { + m_GrabWorldState = GrabWorldState.Normal; + } + + // Update panels visibility if brush grip has changed. + if (bWorldGrabWandPrev != m_GrabWand.grabbingWorld) + { + RequestPanelsVisibility(!m_GrabWand.grabbingWorld); + } + + // Update tool visibility if brush grip has changed. + if (bWorldGrabBrushPrev != m_GrabBrush.grabbingWorld) + { + m_SketchSurfacePanel.RequestHideActiveTool(m_GrabBrush.grabbingWorld); + PointerManager.m_Instance.AllowPointerPreviewLine(!m_GrabBrush.grabbingWorld); + PointerManager.m_Instance.RequestPointerRendering(!m_GrabBrush.grabbingWorld + && m_SketchSurfacePanel.ShouldShowPointer() && !App.Instance.IsLoading()); + } + + // Reset m_WorldBeingGrabbed and only set it when world is actually being grabbed. + bool bWorldBeingGrabbedPrev = m_WorldBeingGrabbed; + m_WorldBeingGrabbed = false; + + // Move the world if it has been grabbed. + if (m_GrabWorldState == GrabWorldState.Normal && bAllowWorldTransform) + { + if (nGrabs == 2) + { + // Two-handed world movement. + m_WorldBeingGrabbed = true; + TrTransform grabXfWand = TrTransform.FromTransform( + InputManager.m_Instance.GetController(InputManager.ControllerName.Wand)); + TrTransform grabXfBrush = TrTransform.FromTransform( + InputManager.m_Instance.GetController(InputManager.ControllerName.Brush)); + + // Offset the controller positions so that they're centered on the grips. + Vector3 gripPos = InputManager.Controllers[(int)InputManager.ControllerName.Brush].Geometry.GripAttachPoint.localPosition; + gripPos.x = 0.0f; + grabXfWand.translation += grabXfWand.MultiplyVector(gripPos); + grabXfBrush.translation += grabXfBrush.MultiplyVector(gripPos); + + // Are we initiating two hand transform this frame? + if (!bWorldGrabWandPrev || !bWorldGrabBrushPrev) + { + PointerManager.m_Instance.EnableLine(false); + PointerManager.m_Instance.AllowPointerPreviewLine(false); + PointerManager.m_Instance.RequestPointerRendering(false); + // Initiate audio loop + m_WorldTransformSpeedSmoothed = 0.0f; + AudioManager.m_Instance.WorldGrabLoop(true); + } + else + { + TrTransform xfOld = GrabbedPose; + TrTransform xfNew; + float deltaScaleMin = WorldTransformMinScale / xfOld.scale; + float deltaScaleMax = WorldTransformMaxScale / xfOld.scale; + bool fixOffset = false; + fixOffset = CheckToggleTiltProtection(); + xfNew = MathUtils.TwoPointObjectTransformation( + m_GrabBrush.grabTransform, m_GrabWand.grabTransform, + grabXfBrush, grabXfWand, + xfOld, + rotationAxisConstraint: (App.Scene.disableTiltProtection ? default(Vector3) : Vector3.up), + deltaScaleMin: deltaScaleMin, deltaScaleMax: deltaScaleMax); + float fCurrentWorldTransformSpeed = + Mathf.Abs((xfNew.scale - xfOld.scale) / Time.deltaTime); + m_WorldTransformSpeedSmoothed = + Mathf.Lerp(m_WorldTransformSpeedSmoothed, fCurrentWorldTransformSpeed, + AudioManager.m_Instance.m_WorldGrabLoopSmoothSpeed * Time.deltaTime); + AudioManager.m_Instance.ChangeLoopVolume("WorldGrab", + Mathf.Clamp(m_WorldTransformSpeedSmoothed / + AudioManager.m_Instance.m_WorldGrabLoopAttenuation, 0f, + AudioManager.m_Instance.m_WorldGrabLoopMaxVolume)); + + if (fixOffset) + { + Vector3 midPoint = Vector3.Lerp(grabXfBrush.translation, grabXfWand.translation, 0.5f); + + Vector3 localMidPointOldXF = xfOld.inverse * midPoint; + + // assign this to force the axial protection + GrabbedPose = xfNew; + xfNew = GrabbedPose; + + Vector3 midPointXFNew = xfNew * localMidPointOldXF; + + TrTransform xfDelta1 = TrTransform.T(midPoint - midPointXFNew); + xfNew = xfDelta1 * xfNew; + } + GrabbedPose = xfNew; + } + + // Update last states. + m_GrabBrush.grabTransform = grabXfBrush; + m_GrabWand.grabTransform = grabXfWand; + } + } + else if (m_GrabWorldState == GrabWorldState.ResettingTransform) + { + if (m_WorldTransformResetState == WorldTransformResetState.FadingToScene) + { + ResetGrabbedPose(); + PanelManager.m_Instance.ExecuteOnPanel(x => x.OnPanelMoved()); + + // World can't be transformed right after a reset until grab states have changed. + if (bAllowWorldTransform) + { + bAllowWorldTransform = false; + bAllowWorldTransformChanged = + bAllowWorldTransform != m_AllowWorldTransformLastFrame; + } + + // Set the grab world state on exit. + if (nGrabs == 0) + { + m_GrabWorldState = GrabWorldState.Normal; + } + else + { + m_GrabWorldState = GrabWorldState.ResetDone; + } + } + } + + if (grabsChanged || bAllowWorldTransformChanged) + { + // Fade in grid when doing two handed spin. + if (nGrabs == 2 && !bAllowWorldTransformChanged) + { + ViewpointScript.m_Instance.FadeGroundPlaneIn(m_GrabWorldGridColor, m_GrabWorldFadeSpeed); + } + else + { + ViewpointScript.m_Instance.FadeGroundPlaneOut(m_GrabWorldFadeSpeed); + } + } + + // Update visuals for world transform + if (grabsChanged) + { + bool bDoubleGrip = m_GrabBrush.grabbingWorld && m_GrabWand.grabbingWorld; + bool bSingleGrip = m_GrabBrush.grabbingWorld || m_GrabWand.grabbingWorld; + Vector3 vControllersMidpoint = + (InputManager.m_Instance.GetControllerPosition(InputManager.ControllerName.Brush) + + InputManager.m_Instance.GetControllerPosition(InputManager.ControllerName.Wand)) * 0.5f; + + // Update transform line visuals + if (bDoubleGrip) + { + m_ControllerGrabVisuals.SetDesiredVisualState(ControllerGrabVisuals.VisualState.WorldDoubleGrip); + AudioManager.m_Instance.WorldGrabbed(vControllersMidpoint); + } + else if (bSingleGrip) + { + if (m_GrabWand.grabbingWorld) + { + m_ControllerGrabVisuals.SetDesiredVisualState(ControllerGrabVisuals.VisualState.WorldWandGrip); + } + else + { + m_ControllerGrabVisuals.SetDesiredVisualState(ControllerGrabVisuals.VisualState.WorldBrushGrip); + } + + if (!bWorldGrabWandPrev && !bWorldGrabBrushPrev) + { + AudioManager.m_Instance.WorldGrabbed(vControllersMidpoint); + } + else + { + AudioManager.m_Instance.WorldGrabLoop(false); + } + } + else + { + m_ControllerGrabVisuals.SetDesiredVisualState(ControllerGrabVisuals.VisualState.Off); + AudioManager.m_Instance.WorldGrabLoop(false); + } + + if (m_GrabWand.grabbingWorld || m_GrabBrush.grabbingWorld) + { + m_WidgetManager.WidgetsDormant = false; + PointerManager.m_Instance.EatLineEnabledInput(); + } + } + + // Reset scene transform if we're gripping and press the track pad. + bool wandReset = m_GrabWand.grabbingWorld && + InputManager.Wand.GetCommandDown(InputManager.SketchCommands.WorldTransformReset); + bool brushReset = m_GrabBrush.grabbingWorld && + InputManager.Brush.GetCommandDown(InputManager.SketchCommands.WorldTransformReset); + if ((wandReset || brushReset) && !WorldIsReset(toSavedXf: false)) + { + m_GrabBrush.eatInput = true; + m_GrabWand.eatInput = true; + m_EatToolScaleInput = true; + m_GrabWorldState = GrabWorldState.ResettingTransform; + RequestWorldTransformReset(); + AudioManager.m_Instance.PlayTransformResetSound(); + } + + // Update the skybox rotation with the new scene rotation. + if (RenderSettings.skybox) + { + Quaternion sceneQuaternion = App.Instance.m_SceneTransform.rotation; + RenderSettings.skybox.SetVector( + "_SkyboxRotation", + new Vector4(sceneQuaternion.x, sceneQuaternion.y, sceneQuaternion.z, sceneQuaternion.w)); + } + + // Update last frame members. + m_AllowWorldTransformLastFrame = bAllowWorldTransform; + } + + /// If lhs and rhs are overlapping, return the smallest vector that would + /// cause rhs to stop overlapping; otherwise, return 0. + /// lhs: an antisphere (solid outside, empty inside) + /// rhs: a sphere (empty outside, solid inside) + private static Vector3 GetOverlap_Antisphere_Sphere( + Vector3 lhsCenter, float lhsRadius, + Vector3 rhsCenter, float rhsRadius) + { + // If anyone passes negative values, they are a bad person + lhsRadius = Mathf.Abs(lhsRadius); + rhsRadius = Mathf.Abs(rhsRadius); + // Without loss of generality, can recenter on lhs + rhsCenter -= lhsCenter; + lhsCenter -= lhsCenter; + + float maxDistance = lhsRadius - rhsRadius; + + // Edge case: sphere does not fit in antisphere + if (maxDistance <= 0) + { + return -rhsCenter; + } + + float penetrationDistance = Mathf.Max(0, rhsCenter.magnitude - maxDistance); + return -penetrationDistance * rhsCenter.normalized; + } + + public static bool IsValidScenePose(TrTransform xf, float radialBounds) + { + // Simple and dumb implementation for now. + return xf == MakeValidScenePose(xf, radialBounds); + } + + /// This is like MakeValidScenePose, but it guarantees that: + /// - The return value is a valid result of Lerp(scene0, scene1, t), + /// for some handwavy definition of "lerp" + /// - The lerp "t" is in [0, 1] + /// - IsValidScenePose(return value) is true, subject to the previous constraints. + /// + /// Think of it as doing a cast from scene0 to scene1. + public static TrTransform MakeValidSceneMove( + TrTransform scene0, TrTransform scene1, float radialBounds) + { + if (IsValidScenePose(scene1, radialBounds)) + { + return scene1; + } + if (!IsValidScenePose(scene0, radialBounds)) + { + Debug.LogError("Invalid scene cast start"); + return scene0; + } + + // We don't support lerping either of these + Debug.Assert(scene0.rotation == scene1.rotation); + Debug.Assert(scene0.scale == scene1.scale); + + Vector3 vRoom0 = -scene0.translation; + Vector3 vRoom1 = -scene1.translation; + float radius = (scene0.scale + * radialBounds + * App.METERS_TO_UNITS) - App.Instance.RoomRadius; + + float t0, t1; + bool success = MathUtils.RaySphereIntersection( + vRoom0, vRoom1 - vRoom0, + Vector3.zero, radius, out t0, out t1); + if (!success) + { + // If this were more important, we could solve for the t of the closest approach + return scene0; + } + + // t0 is expected to be < 0 (room starts inside the fence) + // t1 is expected to be in [0, 1] (room ends outside the fence) + + // Constraints: + // - Lerp t must be in [0, 1]. (Do not move past the requested endpoint) + // - Lerp t should be as high as possible but < t1. (Do not exit the sphere) + float t = Mathf.Clamp(t1, 0, 1); + + TrTransform sceneT = TrTransform.TRS( + Vector3.Lerp(scene0.translation, scene1.translation, t), + scene0.rotation, + scene0.scale); + return MakeValidScenePose(sceneT, radialBounds); + } + + /// Returns a new ScenePose TrTransform that does not cause the room + /// to violate the hard scene bounds. + /// + /// scenePose - The current, possibly invalid scene pose + public static TrTransform MakeValidScenePose(TrTransform scenePose, float radialBounds) + { + scenePose.scale = Mathf.Clamp( + scenePose.scale, + SketchControlsScript.m_Instance.WorldTransformMinScale, + SketchControlsScript.m_Instance.WorldTransformMaxScale); + + // Anything not explicitly qualified is in room space. + + float roomRadius = App.Instance.RoomRadius; + Vector3 roomCenter = Vector3.zero; + + float fenceRadius = scenePose.scale * radialBounds + * App.METERS_TO_UNITS; + Vector3 fenceCenter = scenePose.translation; + + Vector3 moveRoom = GetOverlap_Antisphere_Sphere( + fenceCenter, fenceRadius, roomCenter, roomRadius); + Vector3 moveFence = -moveRoom; + + scenePose.translation += moveFence; + return scenePose; + } + + /// Clears data used by GetGrabWidgetHoldHistory() + /// Should be called any time m_GrabWidgetOneHandInfo changes + void ClearGrabWidgetHoldHistory() + { + m_GrabWidgetHoldHistory.Clear(); + } + + /// Collects data for use with GetGrabWidgetHoldHistory() + void UpdateGrabWidgetHoldHistory(InputManager.ControllerName name) + { + float t = Time.realtimeSinceStartup; + var info = InputManager.Controllers[(int)name]; + m_GrabWidgetHoldHistory.Enqueue(new GrabWidgetHoldPoint + { + m_Name = name, + m_BirthTime = t, + m_Pos = info.Transform.position, + m_Rot = info.Transform.rotation + }); + + // Trim the fat off our widget history + while (m_GrabWidgetHoldHistory.Count > 0 && + t - m_GrabWidgetHoldHistory.Peek().m_BirthTime >= kControlPointHistoryMaxTime) + { + m_GrabWidgetHoldHistory.Dequeue(); + } + } + + /// Returns possibly-smoothed linear and angular velocities. May fail. + /// Angular velocity is returned as an axial vector whose length() is degrees/second + bool GetGrabWidgetHoldHistory(out Vector3 vLinearVelocity, out Vector3 vAngularVelocity) + { + vLinearVelocity = vAngularVelocity = Vector3.zero; + if (m_GrabWidgetHoldHistory.Count < 2) + { + return false; + } + + // We need pairs of elements, so a simple foreach() won't quite work. + // Maybe using linq .First() and .Skip() would be okay. + using (IEnumerator enumerator = m_GrabWidgetHoldHistory.GetEnumerator()) + { + if (!enumerator.MoveNext()) + { + return false; + } + + // Infinitesimal rotations commute, and scaled-axis-angle rotations commute + // "better" than other rotation formats. + Vector3 totalDeltaTheta = Vector3.zero; + + GrabWidgetHoldPoint first = enumerator.Current; + GrabWidgetHoldPoint prev = first; + GrabWidgetHoldPoint current = first; + while (enumerator.MoveNext()) + { + current = enumerator.Current; + + // For our quaternion, find the difference, convert it to angle/axis, and sum it + // Find delta such that delta * prev = cur + // left-multiply because we want it in world-space. + // multiply vs prev since we want the delta that takes us forward in time + // rather than backward in time. + Quaternion dtheta = current.m_Rot * Quaternion.Inverse(prev.m_Rot); + // Assume the rotation took the shorter path + if (dtheta.w < 0) + { + dtheta.Set(-dtheta.x, -dtheta.y, -dtheta.z, -dtheta.w); + } + + float degrees; + Vector3 axis; + dtheta.ToAngleAxis(out degrees, out axis); + totalDeltaTheta += (axis * degrees); + prev = current; + } + + // Linear velocity calculation doesn't need to look at intermediate points + Vector3 totalDeltaPosition = current.m_Pos - first.m_Pos; + float totalDeltaTime = current.m_BirthTime - first.m_BirthTime; + if (totalDeltaTime == 0) + { + return false; + } + + vLinearVelocity = totalDeltaPosition / totalDeltaTime; + vAngularVelocity = totalDeltaTheta / totalDeltaTime; + return true; + } + } + + bool IsControllerNearWidget(InputManager.ControllerName name, GrabWidget widget) + { + Vector3 vControllerPos = InputManager.m_Instance.GetControllerAttachPointPosition(name); + return widget.GetActivationScore(vControllerPos, name) >= 0.0f; + } + + void RefreshCurrentGazeObject() + { + UnityEngine.Profiling.Profiler.BeginSample("SketchControlScript.RefreshCurrentGazeObject"); + int iPrevGazeObject = m_CurrentGazeObject; + m_CurrentGazeObject = -1; + bool bGazeAllowed = (m_CurrentInputState == InputState.Standard) + && !InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Activate) + && !m_SketchSurfacePanel.ActiveTool.InputBlocked() + && (m_GrabWidgetState == GrabWidgetState.None) + && !m_GrabBrush.grabbingWorld + && !m_PinCushion.IsShowing() + && !PointerManager.MainPointerIsPainting() + ; + + bool bGazeDeactivationOverrideWithInput = false; + List aAllPanels = m_PanelManager.GetAllPanels(); + + bool hasController = m_ControlsType == ControlsType.SixDofControllers; + + //if we're re-positioning a panel, keep it active + if (m_PositioningPanelWithHead) + { + m_CurrentGazeObject = iPrevGazeObject; + } + // Only activate gaze objects if we're in standard input mode, and if we don't have the 'draw' + // button held. + else if ((bGazeAllowed || (iPrevGazeObject != -1))) + { + //reset hit flags + for (int i = 0; i < m_GazeResults.Length; ++i) + { + m_GazeResults[i].m_HitWithGaze = false; + m_GazeResults[i].m_HitWithController = false; + m_GazeResults[i].m_WithinView = false; + } + + // If we're in controller mode, find the nearest colliding widget that might get in our way. + float fNearestWidget = 99999.0f; + if (hasController) + { + fNearestWidget = m_WidgetManager.DistanceToNearestWidget(m_GazeControllerRay); + } + + //check all panels for gaze hit + bool bRequireVisibilityCheck = !hasController || (iPrevGazeObject == -1); + if (m_PanelManager.PanelsAreStable()) + { + RaycastHit rHitInfo; + bool bRayHit = false; + int panelsHit = 0; + for (int i = 0; i < aAllPanels.Count; ++i) + { + // Ignore fixed panels when they are not visible. + if (!m_PanelManager.GazePanelsAreVisible() && aAllPanels[i].m_Panel.m_Fixed) + { + continue; + } + + if (aAllPanels[i].m_Panel.gameObject.activeSelf && aAllPanels[i].m_Panel.IsAvailable()) + { + //make sure this b-snap is in view + Vector3 vToPanel = aAllPanels[i].m_Panel.transform.position - m_CurrentGazeRay.origin; + vToPanel.Normalize(); + if (!bRequireVisibilityCheck || Vector3.Angle(vToPanel, m_CurrentGazeRay.direction) < m_GazeMaxAngleFromFacing) + { + if (hasController) + { + if (aAllPanels[i].m_Panel.HasMeshCollider()) + { + //make sure the angle between the pointer and the panel forward is below our max angle + if (Vector3.Angle(aAllPanels[i].m_Panel.transform.forward, m_GazeControllerRay.direction) < m_GazeMaxAngleFromPointing) + { + //make sure the angle between the user-to-panel and the panel forward is reasonable + if (Vector3.Angle(aAllPanels[i].m_Panel.transform.forward, vToPanel) < m_GazeMaxAngleFacingToForward) + { + m_GazeResults[i].m_WithinView = true; + + bRayHit = false; + bRayHit = aAllPanels[i].m_Panel.RaycastAgainstMeshCollider( + m_GazeControllerRay, out rHitInfo, m_GazeControllerPointingDistance); + + if (bRayHit) + { + //if the ray starts inside the panel, we won't get a good hit point, it'll just be zero + if (rHitInfo.point.sqrMagnitude > 0.1f) + { + if (rHitInfo.distance < fNearestWidget) + { + m_GazeResults[i].m_ControllerDistance = rHitInfo.distance; + m_GazeResults[i].m_ControllerPosition = rHitInfo.point; + m_GazeResults[i].m_HitWithController = true; + panelsHit++; + } + } + } + } + } + } + } + else + { + m_GazeResults[i].m_WithinView = true; + if (aAllPanels[i].m_Panel.GetCollider().Raycast(m_CurrentGazeRay, out rHitInfo, m_GazeMaxDistance)) + { + m_GazeResults[i].m_GazePosition = rHitInfo.point; + m_GazeResults[i].m_HitWithGaze = true; + } + } + } + } + } + + // No panels hit within normal ray distance. + // Check if previous panel still pointed to. + if (panelsHit == 0) + { + if (iPrevGazeObject != -1) + { + // Don't allow any panel to hold focus if it's facing away from the user. + Vector3 vToPanel = aAllPanels[iPrevGazeObject].m_Panel.transform.position - + m_CurrentGazeRay.origin; + vToPanel.Normalize(); + if (Vector3.Angle(aAllPanels[iPrevGazeObject].m_Panel.transform.forward, vToPanel) < + m_GazeMaxAngleFacingToForward) + { + float fDist = m_GazeControllerPointingDistance * 1.5f; + bRayHit = aAllPanels[iPrevGazeObject].m_Panel.RaycastAgainstMeshCollider( + m_GazeControllerRayActivePanel, out rHitInfo, fDist); + if (bRayHit) + { + if (rHitInfo.point.sqrMagnitude > 0.1f) + { + if (rHitInfo.distance < fNearestWidget) + { + m_GazeResults[iPrevGazeObject].m_ControllerDistance = rHitInfo.distance; + m_GazeResults[iPrevGazeObject].m_ControllerPosition = rHitInfo.point; + m_GazeResults[iPrevGazeObject].m_HitWithController = true; + } + } + } + } + } + } + } + + //determine what panel we hit, take the one with the lowest controller distance + float fControllerDist = 999.0f; + int iControllerIndex = -1; + if (hasController) + { + for (int i = 0; i < m_GazeResults.Length; ++i) + { + if (m_GazeResults[i].m_HitWithController) + { + if (m_GazeResults[i].m_ControllerDistance < fControllerDist) + { + iControllerIndex = i; + fControllerDist = m_GazeResults[i].m_ControllerDistance; + } + } + } + } + + //if we found something near our controller, take it + if (iControllerIndex != -1) + { + m_CurrentGazeObject = iControllerIndex; + m_CurrentGazeHitPoint = m_GazeResults[iControllerIndex].m_ControllerPosition; + + // TODO: This should not be hardcoded once multiple pointers are allowed. + m_GazeResults[m_CurrentGazeObject].m_ControllerName = InputManager.ControllerName.Brush; + if (m_GazeResults[m_CurrentGazeObject].m_HitWithGaze) + { + //average with the gaze position if we hit that too + m_CurrentGazeHitPoint += m_GazeResults[m_CurrentGazeObject].m_GazePosition; + m_CurrentGazeHitPoint *= 0.5f; + } + } + else + { + //nothing near the controller, see if we're looking at the previous + if (iPrevGazeObject != -1 && m_GazeResults[iPrevGazeObject].m_HitWithGaze) + { + m_CurrentGazeObject = iPrevGazeObject; + m_CurrentGazeHitPoint = m_GazeResults[m_CurrentGazeObject].m_GazePosition; + } + else + { + //controller and gaze not near panel, pick the first panel we're looking at + for (int i = 0; i < m_GazeResults.Length; ++i) + { + if (m_GazeResults[i].m_HitWithGaze) + { + m_CurrentGazeObject = i; + m_CurrentGazeHitPoint = m_GazeResults[i].m_GazePosition; + break; + } + } + } + } + + //forcing users to look away from gaze panel + if (m_EatInputGazeObject && m_CurrentGazeObject != -1) + { + m_CurrentGazeObject = -1; + } + else if (m_CurrentGazeObject == -1) + { + m_EatInputGazeObject = false; + } + } + + //if we're staring at a panel, keep our countdown fresh + if (m_CurrentGazeObject != -1 || m_ForcePanelActivation) + { + m_GazePanelDectivationCountdown = m_GazePanelDectivationDelay; + } + else + { + if (InputManager.m_Instance.GetCommandDown(InputManager.SketchCommands.Activate)) + { + bGazeDeactivationOverrideWithInput = true; + m_GazePanelDectivationCountdown = 0.0f; + } + else + { + m_GazePanelDectivationCountdown -= Time.deltaTime; + } + if (m_GazePanelDectivationCountdown > 0.0f) + { + m_CurrentGazeObject = iPrevGazeObject; + } + } + + //update our positioning timer + if (m_PositioningPanelWithHead) + { + m_PositioningTimer += m_PositioningSpeed * Time.deltaTime; + m_PositioningTimer = Mathf.Min(m_PositioningTimer, 1.0f); + } + else + { + m_PositioningTimer -= m_PositioningSpeed * Time.deltaTime; + m_PositioningTimer = Mathf.Max(m_PositioningTimer, 0.0f); + } + + //prime objects if we change targets + if (iPrevGazeObject != m_CurrentGazeObject) + { + //if we're switching panels, make sure the pointer doesn't streak + PointerManager.m_Instance.DisablePointerPreviewLine(); + + if (iPrevGazeObject != -1) + { + aAllPanels[iPrevGazeObject].m_Panel.PanelGazeActive(false); + aAllPanels[iPrevGazeObject].m_Panel.SetPositioningPercent(0.0f); + } + if (m_CurrentGazeObject != -1) + { + //make sure our line is disabled + if (m_GazeResults[m_CurrentGazeObject].m_ControllerName == InputManager.ControllerName.Brush) + { + PointerManager.m_Instance.EnableLine(false); + PointerManager.m_Instance.AllowPointerPreviewLine(false); + } + + aAllPanels[m_CurrentGazeObject].m_Panel.PanelGazeActive(true); + aAllPanels[m_CurrentGazeObject].m_Panel.SetPositioningPercent(0.0f); + + if (m_GazeResults[m_CurrentGazeObject].m_ControllerName == InputManager.ControllerName.Brush) + { + m_SketchSurfacePanel.RequestHideActiveTool(true); + } + } + else + { + //if we don't have a panel, we need to enable the pointer according to the current tool + PointerManager.m_Instance.RefreshFreePaintPointerAngle(); + PointerManager.m_Instance.RequestPointerRendering(m_SketchSurfacePanel.ShouldShowPointer()); + m_UIReticle.SetActive(false); + m_SketchSurfacePanel.RequestHideActiveTool(false); + if (!bGazeDeactivationOverrideWithInput) + { + m_SketchSurfacePanel.EatToolsInput(); + } + } + + m_PositioningPanelWithHead = false; + } + UnityEngine.Profiling.Profiler.EndSample(); + } + + void UpdateActiveGazeObject() + { + BasePanel currentPanel = m_PanelManager.GetPanel(m_CurrentGazeObject); + currentPanel.SetPositioningPercent(m_PositioningTimer); + bool hasController = m_ControlsType == ControlsType.SixDofControllers; + // Update positioning behavior. + if (m_PositioningPanelWithHead) + { + if (!InputManager.m_Instance.GetCommand(InputManager.SketchCommands.LockToHead) && + !InputManager.m_Instance.GetCommand(InputManager.SketchCommands.LockToController)) + { + // No more positioning. + m_PositioningPanelWithHead = false; + m_PanelManager.m_SweetSpot.EnableBorderSphere(false, Vector3.zero, 0.0f); + currentPanel.PanelHasStoppedMoving(); + } + else + { + //lock the panel to the sweet spot bounds in the direction the user is looking + Quaternion qDiff = m_CurrentHeadOrientation * Quaternion.Inverse(m_PositioningPanelBaseHeadRotation); + Vector3 vAdjustedOffset = qDiff * m_PositioningPanelOffset; + + Vector3 vNewPos = m_PanelManager.m_SweetSpot.transform.position + vAdjustedOffset; + currentPanel.transform.position = vNewPos; + + vAdjustedOffset.Normalize(); + currentPanel.transform.forward = vAdjustedOffset; + + float fHighlightRadius = currentPanel.m_BorderSphereHighlightRadius; + m_PanelManager.m_SweetSpot.EnableBorderSphere(true, vNewPos, fHighlightRadius * m_PositioningTimer); + + //once we've moved this panel, run the simulation on the other panels to resolve collisions + m_PanelManager.DoCollisionSimulationForKeyboardMouse(currentPanel); + } + } + else + { + // It's possible that, on this frame, before this function was called, active gaze was pulled + // from this panel. In this case, we want to skip updating this frame. + // This happens when a panel has gaze and world grab dismisses all panels, for example. + if (currentPanel.IsActive()) + { + //orient to gaze + if (hasController) + { + currentPanel.UpdatePanel(m_GazeControllerRay.direction, m_CurrentGazeHitPoint); + } + else + { + currentPanel.UpdatePanel(m_CurrentGazeRay.direction, m_CurrentGazeHitPoint); + } + } + + if (!hasController) + { + //lock to head if we're holding a lock button.. + bool bLockToHead = InputManager.m_Instance.GetCommand(InputManager.SketchCommands.LockToHead) || + InputManager.m_Instance.GetCommand(InputManager.SketchCommands.LockToController); + + if (bLockToHead) + { + m_PositioningPanelWithHead = true; + m_PositioningPanelBaseHeadRotation = m_CurrentHeadOrientation; + m_PositioningPanelOffset = currentPanel.transform.position - + m_PanelManager.m_SweetSpot.transform.position; + + currentPanel.ResetPanelFlair(); + + //prime all other panels for movement + m_PanelManager.PrimeCollisionSimForKeyboardMouse(); + } + } + + PointerManager.m_Instance.RequestPointerRendering(false); + currentPanel.UpdateReticleOffset(m_MouseDeltaX, m_MouseDeltaY); + } + + // Keep reticle locked in the right spot. + Vector3 reticlePos = Vector3.zero; + Vector3 reticleForward = Vector3.zero; + if (hasController) + { + currentPanel.GetReticleTransformFromPosDir(m_CurrentGazeHitPoint, + m_GazeControllerRay.direction, out reticlePos, out reticleForward); + } + else + { + currentPanel.GetReticleTransform(out reticlePos, out reticleForward, + (m_ControlsType == ControlsType.ViewingOnly)); + } + + SetUIReticleTransform(reticlePos, -reticleForward); + m_UIReticle.SetActive(GetGazePanelActivationRatio() >= 1.0f); + } + + public void ResetActivePanel() + { + m_PanelManager.ResetPanel(m_CurrentGazeObject); + PointerManager.m_Instance.DisablePointerPreviewLine(); + m_PositioningPanelWithHead = false; + m_CurrentGazeObject = -1; + } + + void UpdatePanInput() + { + if (Mouse.current.rightButton.isPressed) + { + Vector3 vPanDiff = Vector3.zero; + vPanDiff += (Vector3.right * m_MouseDeltaXScaled); + vPanDiff += (Vector3.up * m_MouseDeltaYScaled); + Vector3 vSurfacePos = m_SketchSurface.transform.position; + m_SketchSurface.transform.position = vSurfacePos + vPanDiff; + } + else + { + float fCurrentTime = Time.realtimeSinceStartup; + if (fCurrentTime - m_PositionOffsetResetTapTime < m_DoubleTapWindow) + { + if (m_CurrentGazeObject == -1) + { + ResetGrabbedPose(); + } + } + m_PositionOffsetResetTapTime = fCurrentTime; + + SwitchState(InputState.Standard); + } + } + + void UpdateRotationInput() + { + if (InputManager.m_Instance.GetCommand(InputManager.SketchCommands.PivotRotation)) + { + bool bAltInputActive = InputManager.m_Instance.GetCommand(InputManager.SketchCommands.AltActivate); + bool bRollRotation = m_RotationRollActive || bAltInputActive || m_CurrentRotationType == RotationType.RollOnly; + m_RotationIcon.SetActive(bRollRotation); + if (bRollRotation) + { + m_RotationCursorOffset.x += m_MouseDeltaXScaled; + float fRotationAmount = m_RotationCursorOffset.x * -m_RotationRollScalar; + + Quaternion qOffsetRotation = Quaternion.AngleAxis(fRotationAmount, m_SurfaceForward); + Quaternion qNewRotation = qOffsetRotation * m_RotationOrigin; + m_SketchSurface.transform.rotation = qNewRotation; + + m_RotationRollActive = true; + m_RotationCursor.gameObject.SetActive(false); + } + else + { + //update offset with mouse movement + m_RotationCursorOffset.x += m_MouseDeltaXScaled; + m_RotationCursorOffset.y += m_MouseDeltaYScaled; + + //get offset in model space + Vector3 vSurfaceBounds = m_SketchSurface.transform.localScale * 0.5f; + m_RotationCursorOffset.x = Mathf.Clamp(m_RotationCursorOffset.x, -vSurfaceBounds.x, vSurfaceBounds.x); + m_RotationCursorOffset.y = Mathf.Clamp(m_RotationCursorOffset.y, -vSurfaceBounds.y, vSurfaceBounds.y); + float fCursorOffsetDist = m_RotationCursorOffset.magnitude; + float fMaxCursorOffsetDist = vSurfaceBounds.x; + + //transform offset in to world space + Vector3 vTransformedOffset = m_RotationOrigin * m_RotationCursorOffset; + vTransformedOffset.Normalize(); + + //get world space rotation axis + Vector3 vSketchSurfaceRotationAxis = Vector3.Cross(vTransformedOffset, m_SurfaceForward); + vSketchSurfaceRotationAxis.Normalize(); + + //amount to rotate is determined by offset distance from origin + float fSketchSurfaceRotationAngle = Mathf.Min(fCursorOffsetDist / fMaxCursorOffsetDist, 1.0f); + fSketchSurfaceRotationAngle *= m_RotationMaxAngle; + + //set new surface rotation by combining base rotation with angle/axis rotation + Quaternion qOffsetRotation = Quaternion.AngleAxis(fSketchSurfaceRotationAngle, vSketchSurfaceRotationAxis); + Quaternion qNewRotation = qOffsetRotation * m_RotationOrigin; + m_SketchSurface.transform.rotation = qNewRotation; + + //set position of rotation cursor + Vector3 vNewTransformedOffset = qNewRotation * m_RotationCursorOffset; + m_RotationCursor.transform.position = m_SketchSurface.transform.position + vNewTransformedOffset; + m_RotationCursor.transform.rotation = qNewRotation; + + //set position of guide lines + Vector2 vToCenter = m_RotationCursorOffset; + vToCenter.Normalize(); + float fOffsetAngle = Vector2.Angle(vToCenter, Vector2.up); + m_RotationCursor.PositionCursorLines(m_SketchSurface.transform.position, m_SketchSurface.transform.forward, fOffsetAngle, vSurfaceBounds.x * 2.0f); + } + } + else + { + float fCurrentTime = Time.realtimeSinceStartup; + if (fCurrentTime - m_RotationResetTapTime < m_DoubleTapWindow) + { + //reset drawing surface rotation + m_SketchSurface.transform.rotation = Quaternion.identity; + } + m_RotationResetTapTime = fCurrentTime; + + m_SurfaceForward = m_SketchSurface.transform.forward; + m_SurfaceRight = m_SketchSurface.transform.right; + m_SurfaceUp = m_SketchSurface.transform.up; + + if (!m_RotationRollActive && m_AutoOrientAfterRotation && m_SketchSurfacePanel.IsSketchSurfaceToolActive()) + { + //get possible auto rotations + Quaternion qQuatUp = OrientSketchSurfaceToUp(); + Quaternion qQuatForward = OrientSketchSurfaceToForward(); + + //get the angle between our current and desired auto-rotation + float toUpAngle = Quaternion.Angle(qQuatUp, m_SketchSurface.transform.rotation); + float toForwardAngle = Quaternion.Angle(qQuatForward, m_SketchSurface.transform.rotation); + + //set our new rotation to be whichever autorotation is closeset + Quaternion qNewRotation; + if (Mathf.Abs(toUpAngle) < Mathf.Abs(toForwardAngle)) + { + qNewRotation = qQuatUp; + } + else + { + qNewRotation = qQuatForward; + } + + //update the sketch surface + m_SketchSurface.transform.rotation = qNewRotation; + + m_SurfaceForward = m_SketchSurface.transform.forward; + m_SurfaceRight = m_SketchSurface.transform.right; + m_SurfaceUp = m_SketchSurface.transform.up; + } + + SwitchState(InputState.Standard); + } + } + + void UpdateHeadLockInput() + { + if (InputManager.m_Instance.GetCommand(InputManager.SketchCommands.LockToHead)) + { + //compute new position/orientation of sketch surface + Vector3 vTransformedOffset = m_CurrentHeadOrientation * m_SurfaceLockOffset; + Vector3 vSurfacePos = m_CurrentGazeRay.origin + vTransformedOffset; + + Quaternion qDiff = m_CurrentHeadOrientation * Quaternion.Inverse(m_SurfaceLockBaseHeadRotation); + Quaternion qNewSurfaceRot = qDiff * m_SurfaceLockBaseSurfaceRotation; + + m_SketchSurface.transform.position = vSurfacePos; + m_SketchSurface.transform.rotation = qNewSurfaceRot; + } + else + { + m_SurfaceForward = m_SketchSurface.transform.forward; + m_SurfaceRight = m_SketchSurface.transform.right; + m_SurfaceUp = m_SketchSurface.transform.up; + + SwitchState(InputState.Standard); + } + } + + void UpdateControllerLock() + { + if (InputManager.m_Instance.GetCommand(InputManager.SketchCommands.LockToController)) + { + //compute new position/orientation of sketch surface + Vector3 vControllerDiff = InputManager.m_Instance.GetControllerPosition(m_SurfaceLockActingController) - m_SurfaceLockBaseControllerPosition; + m_SketchSurface.transform.position = m_SurfaceLockBaseSurfacePosition + (vControllerDiff * m_SurfaceLockControllerScalar); + + Quaternion qDiff = InputManager.m_Instance.GetControllerRotation(m_SurfaceLockActingController) * Quaternion.Inverse(m_SurfaceLockBaseControllerRotation); + m_SketchSurface.transform.rotation = qDiff * m_SurfaceLockBaseSurfaceRotation; + } + else + { + m_SurfaceForward = m_SketchSurface.transform.forward; + m_SurfaceRight = m_SketchSurface.transform.right; + m_SurfaceUp = m_SketchSurface.transform.up; + + SwitchState(InputState.Standard); + } + } + + void UpdatePushPullInput() + { + bool bRotationActive = InputManager.m_Instance.GetCommand(InputManager.SketchCommands.PivotRotation); + bool bInputActive = InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Activate); + bool bAltInputActive = InputManager.m_Instance.GetCommand(InputManager.SketchCommands.AltActivate); + + if (bRotationActive && bInputActive) + { + SwitchState(InputState.Rotation); + } + else if (bAltInputActive) + { + Vector3 vPos = m_SketchSurface.transform.position; + float fBigDiff = Mathf.Abs(m_MouseDeltaXScaled) > Mathf.Abs(m_MouseDeltaYScaled) ? -m_MouseDeltaXScaled : m_MouseDeltaYScaled; + vPos += Vector3.forward * fBigDiff; + + m_SketchSurface.transform.position = vPos; + } + else + { + SwitchState(InputState.Standard); + } + } + + void UpdateSaveInput() + { + if (!InputManager.m_Instance.GetKeyboardShortcut(InputManager.KeyboardShortcut.Save)) + { + SwitchState(InputState.Standard); + } + } + + void UpdateLoadInput() + { + if (!InputManager.m_Instance.GetKeyboardShortcut(InputManager.KeyboardShortcut.Load)) + { + SwitchState(InputState.Standard); + } + } + + void OnBrushSetToDefault() + { + BrushDescriptor rDefaultBrush = BrushCatalog.m_Instance.DefaultBrush; + PointerManager.m_Instance.SetBrushForAllPointers(rDefaultBrush); + PointerManager.m_Instance.SetAllPointersBrushSize01(0.5f); + PointerManager.m_Instance.MarkAllBrushSizeUsed(); + } + + public void AssignControllerMaterials(InputManager.ControllerName controller) + { + ControllerGeometry geometry = InputManager.GetControllerGeometry(controller); + + // Start from a clean state + geometry.ResetAll(); + + // If the tutorial is enabled, override all materials. + if (TutorialManager.m_Instance.TutorialActive()) + { + InputManager.m_Instance + .GetControllerTutorial(controller) + ?.AssignControllerMaterials(controller); + return; + } + + // If we're grabbing the world, get the materials from the world transform panel. + if (m_GrabBrush.grabbingWorld && controller == InputManager.ControllerName.Brush) + { + TrTransform scenePose = App.Scene.Pose; + if (scenePose.scale != 1 || scenePose.translation != Vector3.zero + || scenePose.rotation != Quaternion.identity) + { + geometry.ShowWorldTransformReset(); + } + return; + } + else if (m_GrabWand.grabbingWorld && controller == InputManager.ControllerName.Wand) + { + TrTransform scenePose = App.Scene.Pose; + if (scenePose.scale != 1 || scenePose.translation != Vector3.zero + || scenePose.rotation != Quaternion.identity) + { + geometry.ShowWorldTransformReset(); + } + return; + } + + // Not grabbing the world, so see if we're grabbing a widget. + if (m_GrabWidgetState != GrabWidgetState.None) + { + m_CurrentGrabWidget.AssignControllerMaterials(controller); + return; + } + + // See if we're highlighting a widget and if that matters. + if (m_CurrentGrabWidget != null && m_CurrentGrabWidget.HasHoverInteractions()) + { + m_CurrentGrabWidget.AssignHoverControllerMaterials(controller); + return; + } + + // Not grabbing the world or a widget, see if we're interacting with a panel. + if (controller == InputManager.ControllerName.Brush && m_CurrentGazeObject != -1) + { + BasePanel panel = m_PanelManager.GetPanel(m_CurrentGazeObject); + panel.AssignControllerMaterials(controller); + return; + } + + // Defaults. + if (controller == InputManager.ControllerName.Wand) + { + if (App.CurrentState != App.AppState.Standard || m_PanelManager.IntroSketchbookMode) + { + // If app is not in standard mode, the actions represented by subsequent material + // assigments cannot be taken. + return; + } + bool creatingStroke = PointerManager.m_Instance.IsMainPointerCreatingStroke(); + bool allowPainting = App.Instance.IsInStateThatAllowsPainting(); + + InputManager.Wand.Geometry.ShowRotatePanels(); + InputManager.Wand.Geometry.ShowUndoRedo(CanUndo() && !creatingStroke && allowPainting, + CanRedo() && !creatingStroke && allowPainting); + } + + // Show the pin cushion icon on the button if it's available. + if (controller == InputManager.ControllerName.Brush && CanUsePinCushion()) + { + InputManager.Brush.Geometry.ShowPinCushion(); + } + + // Finally, override with tools. + m_SketchSurfacePanel.AssignControllerMaterials(controller); + } + + public float GetControllerPadShaderRatio( + InputManager.ControllerName controller, VrInput input) + { + // If we're interacting with a panel, get touch ratio from the panel. + if (controller == InputManager.ControllerName.Brush && m_CurrentGazeObject != -1) + { + BasePanel panel = m_PanelManager.GetPanel(m_CurrentGazeObject); + return panel.GetControllerPadShaderRatio(controller); + } + return SketchSurfacePanel.m_Instance.GetCurrentToolSizeRatio(controller, input); + } + + void SwitchState(InputState rDesiredState) + { + //exit current state + switch (m_CurrentInputState) + { + case InputState.Pan: + m_TransformGizmoScript.ResetTransform(); + break; + case InputState.PushPull: + m_TransformGizmoScript.ResetTransform(); + break; + case InputState.Rotation: + m_RotationRollActive = false; + m_RotationIcon.SetActive(false); + m_RotationCursor.gameObject.SetActive(false); + break; + } + + bool bSketchSurfaceToolActive = m_SketchSurfacePanel.IsSketchSurfaceToolActive(); + + //enter new state + switch (rDesiredState) + { + case InputState.Pan: + m_TransformGizmoScript.SetTransformForPan(); + break; + case InputState.PushPull: + m_TransformGizmoScript.SetTransformForPushPull(); + break; + case InputState.Rotation: + if (bSketchSurfaceToolActive) + { + m_SketchSurface.transform.position = PointerManager.m_Instance.MainPointer.transform.position; + m_SketchSurfacePanel.ResetReticleOffset(); + } + m_RotationOrigin = m_SketchSurface.transform.rotation; + m_RotationCursorOffset = Vector2.zero; + m_RotationCursor.transform.position = m_SketchSurface.transform.position; + m_RotationCursor.transform.rotation = m_SketchSurface.transform.rotation; + m_RotationCursor.ClearCursorLines(m_SketchSurface.transform.position); + m_RotationCursor.gameObject.SetActive(bSketchSurfaceToolActive); + break; + case InputState.HeadLock: + m_SurfaceLockBaseHeadRotation = m_CurrentHeadOrientation; + m_SurfaceLockBaseSurfaceRotation = m_SketchSurface.transform.rotation; + m_SurfaceLockOffset = m_SketchSurface.transform.position - m_CurrentGazeRay.origin; + m_SurfaceLockOffset = Quaternion.Inverse(m_SurfaceLockBaseHeadRotation) * m_SurfaceLockOffset; + break; + case InputState.ControllerLock: + if (bSketchSurfaceToolActive) + { + m_SketchSurface.transform.position = PointerManager.m_Instance.MainPointer.transform.position; + m_SketchSurfacePanel.ResetReticleOffset(); + } + m_SurfaceLockActingController = InputManager.m_Instance.GetDominantController(InputManager.SketchCommands.LockToController); + m_SurfaceLockBaseSurfaceRotation = m_SketchSurface.transform.rotation; + m_SurfaceLockBaseControllerRotation = InputManager.m_Instance.GetControllerRotation(m_SurfaceLockActingController); + m_SurfaceLockBaseSurfacePosition = m_SketchSurface.transform.position; + m_SurfaceLockBaseControllerPosition = InputManager.m_Instance.GetControllerPosition(m_SurfaceLockActingController); + m_SurfaceLockControllerScalar = m_SketchSurfacePanel.m_PanelSensitivity / m_SurfaceLockControllerBaseScalar; + break; + case InputState.Save: + IssueGlobalCommand(GlobalCommands.Save); + break; + case InputState.Load: + IssueGlobalCommand(GlobalCommands.Load); + break; + } + + m_CurrentInputState = rDesiredState; + } + + public void RequestPanelsVisibility(bool bVisible) + { + // Always false in viewonly mode + bVisible = m_ViewOnly ? false : bVisible; + m_PanelsVisibilityRequested = bVisible; + } + + Quaternion OrientSketchSurfaceToUp() + { + //project the world up vector on to the surface plane + Vector3 vUpOnSurfacePlane = Vector3.up - (Vector3.Dot(Vector3.up, m_SurfaceForward) * m_SurfaceForward); + vUpOnSurfacePlane.Normalize(); + + //get the angle between the surface up and the projected world up + float fUpOnSurfacePlaneAngle = Vector3.Angle(vUpOnSurfacePlane, m_SurfaceUp); + Vector3 vUpCross = Vector3.Cross(vUpOnSurfacePlane, m_SurfaceUp); + vUpCross.Normalize(); + if (Vector3.Dot(vUpCross, m_SurfaceForward) > 0.0f) + { + fUpOnSurfacePlaneAngle *= -1.0f; + } + + //rotate around the surface foward by the angle diff + Quaternion qOrientToUp = Quaternion.AngleAxis(fUpOnSurfacePlaneAngle, m_SurfaceForward); + Quaternion qNewRotation = qOrientToUp * m_SketchSurface.transform.rotation; + return qNewRotation; + } + + Quaternion OrientSketchSurfaceToForward() + { + //project the world forward vector on to the surface plane + Vector3 vForwardOnSurfacePlane = Vector3.forward - (Vector3.Dot(Vector3.forward, m_SurfaceForward) * m_SurfaceForward); + vForwardOnSurfacePlane.Normalize(); + + //get the angle between the surface up and the projected world forward + float fForwardOnSurfacePlaneAngle = Vector3.Angle(vForwardOnSurfacePlane, m_SurfaceUp); + Vector3 vUpCross = Vector3.Cross(vForwardOnSurfacePlane, m_SurfaceUp); + vUpCross.Normalize(); + if (Vector3.Dot(vUpCross, m_SurfaceForward) > 0.0f) + { + fForwardOnSurfacePlaneAngle *= -1.0f; + } + + //rotate around the surface foward by the angle diff + Quaternion qOrientToForward = Quaternion.AngleAxis(fForwardOnSurfacePlaneAngle, m_SurfaceForward); + Quaternion qNewRotation = qOrientToForward * m_SketchSurface.transform.rotation; + return qNewRotation; + } + + /// Reset the scene or the canvas, depending on the current mode + public void ResetGrabbedPose(bool everything = false) + { + //update sketch surface position with offset to sweet spot + m_SketchSurface.transform.position = m_PanelManager.GetSketchSurfaceResetPos(); + if (everything) + { + App.Scene.Pose = TrTransform.identity; + Coords.CanvasLocalPose = TrTransform.identity; + } + App.Scene.Pose = TrTransform.identity; + + //reset orientation and pointer + ResetSketchSurfaceOrientation(); + m_SketchSurfacePanel.ResetReticleOffset(); + PointerManager.m_Instance.DisablePointerPreviewLine(); + PointerManager.m_Instance.SetPointerPreviewLineDelayTimer(); + } + + public void ResetSketchSurfaceOrientation() + { + m_SketchSurface.transform.rotation = Quaternion.identity; + m_SurfaceForward = m_SketchSurface.transform.forward; + m_SurfaceRight = m_SketchSurface.transform.right; + m_SurfaceUp = m_SketchSurface.transform.up; + } + + float GetAppropriateMovementScalar() + { + switch (m_CurrentInputState) + { + case InputState.Pan: return m_PanScalar; + case InputState.Rotation: return m_RotationScalar; + case InputState.PushPull: return m_PushPullScale; + } + + return 1.0f; + } + + // TODO - it'd be great if we could disentangle this from the multicam. + IEnumerator RenderPathAndQuit() + { +#if USD_SUPPORTED + App.Instance.SetDesiredState(App.AppState.OfflineRendering); + SketchSurfacePanel.m_Instance.EnableSpecificTool(BaseTool.ToolType.MultiCamTool); + MultiCamTool multiCam = SketchSurfacePanel.m_Instance.ActiveTool as MultiCamTool; + Debug.Assert(multiCam != null); // Something's gone wrong if we've been unable to find multicam! + if (multiCam == null) + { + yield break; + } + multiCam.ExternalObjectForceCameraStyle(MultiCamStyle.Video); + MultiCamCaptureRig.ForceClippingPlanes(MultiCamStyle.Video); + // Give the video tool time to switch - TODO - be a little more graceful here + yield return new WaitForSeconds(2); + // Make sure the videos have had time to load, and set playing ones to start + while (VideoCatalog.Instance.IsScanning) + { + yield return null; + } + foreach (var widget in WidgetManager.m_Instance.VideoWidgets) + { + if (widget.VideoController.Playing) + { + widget.VideoController.Position = 0; + } + } + yield return null; + var ssMgr = MultiCamCaptureRig.ManagerFromStyle(MultiCamStyle.Video); + ssMgr.SetScreenshotResolution(App.UserConfig.Video.OfflineResolution); + multiCam.StartVideoCapture(MultiCamTool.GetSaveName(MultiCamStyle.Video), offlineRender: true); + App.Instance.FrameCountDisplay.gameObject.SetActive(true); + App.Instance.FrameCountDisplay.SetFramesTotal(VideoRecorderUtils.NumFramesInUsdSerializer); + while (VideoRecorderUtils.ActiveVideoRecording != null) + { + App.Instance.FrameCountDisplay.SetCurrentFrame( + VideoRecorderUtils.ActiveVideoRecording.FrameCount); + yield return null; + } + ssMgr.SetScreenshotResolution(App.UserConfig.Video.Resolution); +#else + Debug.LogError("Render path requires USD support"); + yield return null; +#endif + QuitApp(); + } + + IEnumerator ExportListAndQuit() + { + App.Config.m_ForceDeterministicBirthTimeForExport = true; + List filesToExport = new List(); + foreach (string filePattern in App.Config.m_FilePatternsToExport) + { + bool absolute = Path.IsPathRooted(filePattern); + string directory = absolute ? Path.GetDirectoryName(filePattern) : App.UserSketchPath(); + string filename = Path.GetFileName(filePattern); + var tiltFiles = Directory.GetFiles(directory, filename); + filesToExport.AddRange(tiltFiles); + // Also look at .tilt files which have been unzipped into directory format + var tiltDirs = Directory.GetDirectories(directory, filename) + .Where(n => n.EndsWith(".tilt")); + filesToExport.AddRange(tiltDirs); + } + + using (var coroutine = LoadAndExportList(filesToExport)) + { + while (coroutine.MoveNext()) + { + yield return coroutine.Current; + } + } + QuitApp(); + } + + void QuitApp() + { + // We're done! Quit! +#if UNITY_EDITOR + UnityEditor.EditorApplication.isPlaying = false; +#else + Application.Quit(); +#endif + } + + // This coroutine must be run to completion or disposed. + IEnumerator LoadAndExportList(List filenames) + { + foreach (var filename in filenames) + { + using (var coroutine = LoadAndExport(filename)) + { + while (coroutine.MoveNext()) + { + yield return coroutine.Current; + } + } + } + } + + // This coroutine must be run to completion or disposed. + IEnumerator LoadAndExportAll() + { + SketchSet sketchSet = SketchCatalog.m_Instance.GetSet(SketchSetType.User); + for (int i = 0; i < SketchCatalog.m_Instance.GetSet(SketchSetType.User).NumSketches; ++i) + { + SceneFileInfo rInfo = sketchSet.GetSketchSceneFileInfo(i); + using (var coroutine = LoadAndExport(rInfo.FullPath)) + { + while (coroutine.MoveNext()) + { + yield return coroutine.Current; + } + } + } + } + + /// Loads a .tilt file completely. + /// This may be slightly buggy; it's not currently used for production. + /// This coroutine must be run to completion or disposed. + public IEnumerable LoadTiltFile(string filename) + { + using (var unused = new SceneSettings.RequestInstantSceneSwitch()) + { + IssueGlobalCommand( + GlobalCommands.LoadNamedFile, + iParam1: (int)LoadSpeed.Quick, sParam: filename); + yield return null; + while (App.Instance.IsLoading()) + { + yield return null; + } + + // I don't know why App.Instance.IsLoading() doesn't cover this, but it doesn't. + while (m_WidgetManager.CreatingMediaWidgets) + { + yield return null; + } + while (WidgetManager.m_Instance.AreMediaWidgetsStillLoading()) + { + yield return null; + } + + // This is kind of a hack. + // Despite the RequestInstantSceneSwitch above, I think scene colors still require + // a few frames to settle; also, GrabWidgets need to register themselves on the + // first frame, etc. + for (int i = 0; i < 10; ++i) + { + yield return null; + } + } + } + + // This coroutine must be run to completion or disposed. + IEnumerator LoadAndExport(string filename) + { + foreach (var val in LoadTiltFile(filename)) + { + yield return val; + } + using (var coroutine = ExportCoroutine()) + { + while (coroutine.MoveNext()) + { + yield return coroutine.Current; + } + } + } + + IEnumerator ExportCoroutine() + { + return OverlayManager.m_Instance.RunInCompositor( + OverlayType.Export, () => + { + // Sort of a kludge: put stuff back into the main canvas + SelectionManager.m_Instance.ClearActiveSelection(); + Export.ExportScene(); + }, 0.25f, false, true); + } + + private void SaveModel() + { +#if USD_SUPPORTED + var current = SaveLoadScript.m_Instance.SceneFile; + string basename = (current.Valid) + ? Path.GetFileNameWithoutExtension(current.FullPath) + : "Untitled"; + string directoryName = FileUtils.GenerateNonexistentFilename( + App.ModelLibraryPath(), basename, ""); + + string usdname = Path.Combine(directoryName, basename + ".usd"); + // TODO: export selection only, though this is still only experimental. The blocking + // issue to implement this is that the export collector needs to expose this as an option. + // + // SelectionManager.m_Instance.HasSelection + // ? SelectionManager.m_Instance.SelectedStrokes + // : null + ExportUsd.ExportPayload(usdname); + OutputWindowScript.m_Instance.CreateInfoCardAtController( + InputManager.ControllerName.Brush, "Model created!"); +#endif + } + + /// Generates a view from the previous thumbnail viewpoint. + public void GenerateReplacementSaveIcon() + { + if (SaveLoadScript.m_Instance.LastThumbnail_SS.HasValue) + { + TrTransform thumbnailInGlobalSpace = App.Scene.Pose * + SaveLoadScript.m_Instance.LastThumbnail_SS.Value; + + m_SaveIconTool.ProgrammaticCaptureSaveIcon(thumbnailInGlobalSpace.translation, + thumbnailInGlobalSpace.rotation); + } + else + { + GenerateBestGuessSaveIcon(); + } + } + + public void GenerateBestGuessSaveIcon() + { + TrTransform camXform = GenerateBestGuessSaveIconTransform(); + m_SaveIconTool.ProgrammaticCaptureSaveIcon(camXform.translation, camXform.rotation); + } + + /// This positions the save icon camera at the user's head position, and faces it towards + /// the most recent strokes the user has created. + /// If there are no strokes, it faces towards the 'most recent' models. + /// Sadly we cannot really mix the two as we don't know when the models were instantiated. + public TrTransform GenerateBestGuessSaveIconTransform(int itemsToEnumerate = 0) + { + if (itemsToEnumerate == 0) + { + itemsToEnumerate = m_NumStrokesForSaveIcon; + } + int startIndex = Mathf.Max(0, SketchMemoryScript.AllStrokesCount() - itemsToEnumerate); + var lastFewStrokes = SketchMemoryScript.AllStrokes().Skip(startIndex).ToArray(); + + Bounds bounds; + if (lastFewStrokes.Length > 0) + { + bounds = new Bounds(lastFewStrokes.First().m_ControlPoints.First().m_Pos, Vector3.zero); + foreach (var stroke in lastFewStrokes.Skip(1)) + { + bounds.Encapsulate(stroke.m_ControlPoints.First().m_Pos); + bounds.Encapsulate(stroke.m_ControlPoints.Last().m_Pos); + } + } + else + { + // If we have no strokes, just use the aggregates bounding boxes of the blocks models. + var models = m_WidgetManager.ModelWidgets.ToArray(); + // we should always have models to get here, but just in case... + if (models.Length > 0) + { + startIndex = Mathf.Max(0, models.Length - itemsToEnumerate); + bounds = models[startIndex].WorldSpaceBounds; + for (int i = startIndex + 1; i < models.Length; ++i) + { + bounds.Encapsulate(models[i].WorldSpaceBounds); + } + } + else + { + bounds = new Bounds(new Vector3(0, 1, -100000), Vector3.one); // some point in the distance + } + } + + Vector3 camPos = ViewpointScript.Head.position; + Vector3 worldPos = App.Scene.Pose.MultiplyPoint(bounds.center); + Quaternion direction = Quaternion.LookRotation(worldPos - camPos); + return TrTransform.TR(camPos, direction); + } + + + public void GenerateBoundingBoxSaveIcon() + { + Vector3 vNewCamPos; + { + Bounds rCanvasBounds = App.Scene.AllCanvases + .Select(canvas => canvas.GetCanvasBoundingBox()) + .Aggregate((b1, b2) => + { + b1.Encapsulate(b2); + return b1; + }); + + //position the camera at the center of the canvas bounds + vNewCamPos = rCanvasBounds.center; + + //back the camera up, along -z until we can see the extent of the bounds + float fCanvasWidth = rCanvasBounds.max.x - rCanvasBounds.min.x; + float fCanvasHeight = rCanvasBounds.max.y - rCanvasBounds.min.y; + float fLargerExtent = Mathf.Max(fCanvasHeight, fCanvasWidth); + + //half fov for camera + float fHalfFOV = m_SaveIconTool.ScreenshotManager.LeftEye.fieldOfView * 0.5f; + + //TODO: find the real reason this isn't working as it should + float fMagicNumber = 1.375f; + + //set new cam position and zero out orientation + float fBackupDistance = (fLargerExtent * 0.5f) + * Mathf.Tan(Mathf.Deg2Rad * fHalfFOV) * fMagicNumber; + vNewCamPos.z = rCanvasBounds.min.z - fBackupDistance; + } + + m_SaveIconTool.ProgrammaticCaptureSaveIcon(vNewCamPos, Quaternion.identity); + } + + private void MergeBrushStrokes(SceneFileInfo fileInfo) + { + m_PanelManager.ToggleSketchbookPanels(isLoadingSketch: true); + PointerManager.m_Instance.EnablePointerStrokeGeneration(true); + if (SaveLoadScript.m_Instance.Load(fileInfo, true)) + { + SketchMemoryScript.m_Instance.SetPlaybackMode(m_SketchPlaybackMode, m_DefaultSketchLoadSpeed); + SketchMemoryScript.m_Instance.BeginDrawingFromMemory(bDrawFromStart: true, false, false); + // the order of these two lines are important as ExitIntroSketch is setting the + // color of the pointer and we need the color to be set before we go to the Loading + // state. App script's ShouldTintControllers allow the controller to be tinted only + // when the app is in the standard mode. That was there to prevent the controller color + // from flickering while in the intro mode. + App.Instance.ExitIntroSketch(); + App.Instance.SetDesiredState(App.AppState.QuickLoad); + } + } + + public void LoadSketch(SceneFileInfo fileInfo, bool quickload = false, bool additive = false) + { + LightsControlScript.m_Instance.DiscoMode = false; + m_WidgetManager.FollowingPath = false; + m_WidgetManager.CameraPathsVisible = false; + m_WidgetManager.DestroyAllWidgets(); + m_PanelManager.ToggleSketchbookPanels(isLoadingSketch: true); + ResetGrabbedPose(everything: true); + PointerManager.m_Instance.EnablePointerStrokeGeneration(true); + if (SaveLoadScript.m_Instance.Load(fileInfo, additive)) + { + SketchMemoryScript.m_Instance.SetPlaybackMode(m_SketchPlaybackMode, m_DefaultSketchLoadSpeed); + SketchMemoryScript.m_Instance.BeginDrawingFromMemory(bDrawFromStart: true); + // the order of these two lines are important as ExitIntroSketch is setting the + // color of the pointer and we need the color to be set before we go to the Loading + // state. App script's ShouldTintControllers allow the controller to be tinted only + // when the app is in the standard mode. That was there to prevent the controller color + // from flickering while in the intro mode. + App.Instance.ExitIntroSketch(); + App.Instance.SetDesiredState(quickload ? App.AppState.QuickLoad : App.AppState.Loading); + } + QualityControls.m_Instance.ResetAutoQuality(); + m_WidgetManager.ValidateCurrentCameraPath(); + } + + public void IssueGlobalCommand(GlobalCommands rEnum, int iParam1 = -1, + int iParam2 = -1, string sParam = null) + { + switch (rEnum) + { + + // Keyboard command, for debugging and emergency use. + case GlobalCommands.Save: + { + if (!FileUtils.CheckDiskSpaceWithError(App.UserSketchPath())) + { + return; + } + // Disable active selection before saving. + // This looks fishy, here's what's going on: When an object is selected and it has moved, the + // the user is observing the selection canvas in the HMD, but we will be saving the main canvas. + // Because they haven't deselected yet, the selection canvas and the main canvas are out of sync + // so the strokes that will be saved will not match what the user sees. + // + // Here we deselect to force the main canvas to sync with the selection canvas, which is more + // correct from the user's perspective. Push the deselect operation onto the stack so the user + // can undo it after save, if desired. + SelectionManager.m_Instance.ClearActiveSelection(); + GenerateReplacementSaveIcon(); + if (iParam1 == -1) + { + if (iParam2 == 1) + { + // Do a save in Tiltasaurus mode, which creates a new filename prefixed with + // "Tiltasaurus_" and the current prompt. Also, don't eat gaze input so that the + // Tiltasaurus prompt stays open. + StartCoroutine(SaveLoadScript.m_Instance.SaveOverwrite(tiltasaurusMode: true)); + } + else + { + StartCoroutine(SaveLoadScript.m_Instance.SaveOverwrite()); + EatGazeObjectInput(); + } + } + else + { + StartCoroutine(SaveLoadScript.m_Instance.SaveMonoscopic(iParam1)); + } + break; + } + case GlobalCommands.SaveNew: + { + if (!FileUtils.CheckDiskSpaceWithError(App.UserSketchPath())) + { + return; + } + if (iParam1 == 1) + { + GenerateBoundingBoxSaveIcon(); + } + StartCoroutine(SaveLoadScript.m_Instance.SaveNewName()); + EatGazeObjectInput(); + break; + } + case GlobalCommands.SaveAndUpload: + { + if (!FileUtils.CheckDiskSpaceWithError(App.UserSketchPath())) + { + Debug.LogError("SaveAndUpload: Disk space error"); + return; + } + SelectionManager.m_Instance.ClearActiveSelection(); + m_PanelManager.GetPanel(m_CurrentGazeObject).CreatePopUp( + GlobalCommands.UploadToGenericCloud, (int)Cloud.None, -1); + EatGazeObjectInput(); + break; + } + case GlobalCommands.ExportAll: + { + StartCoroutine(LoadAndExportAll()); + break; + } + // Glen Keane request: a way to draw guidelines that can be toggled on and off + // at runtime. + case GlobalCommands.DraftingVisibility: + { + if (!Enum.IsDefined(typeof(DraftingVisibilityOption), iParam1)) + { + Debug.LogError("Unknown draft visibility value: " + iParam1); + return; + } + DraftingVisibilityOption option = (DraftingVisibilityOption)iParam1; + if (option != m_DraftingVisibility) + { + m_DraftingVisibility = option; + UpdateDraftingVisibility(); + } + break; + } + case GlobalCommands.MergeBrushStrokes: + { + // TODO Refactor with Load below + var index = iParam1; + var sketchSetType = (SketchSetType)iParam2; + SketchSet sketchSet = SketchCatalog.m_Instance.GetSet(sketchSetType); + SceneFileInfo rInfo = sketchSet.GetSketchSceneFileInfo(index); + if (rInfo != null) + { + MergeBrushStrokes(rInfo); + if (m_ControlsType != ControlsType.ViewingOnly) + { + EatGazeObjectInput(); + } + } + break; + } + case GlobalCommands.Load: + { + var index = iParam1; + var sketchSetType = (SketchSetType)iParam2; + SketchSet sketchSet = SketchCatalog.m_Instance.GetSet(sketchSetType); + SceneFileInfo rInfo = sketchSet.GetSketchSceneFileInfo(index); + if (rInfo != null) + { + LoadSketch(rInfo); + if (m_ControlsType != ControlsType.ViewingOnly) + { + EatGazeObjectInput(); + } + } + break; + } + case GlobalCommands.LoadNamedFile: + LoadNamed(sParam, iParam1 == (int)LoadSpeed.Quick, iParam2 != -1); + break; + case GlobalCommands.NewSketch: + NewSketch(fade: true); + Vector3 vTrashSoundPos = m_CurrentGazeRay.origin; + if (App.VrSdk.GetControllerDof() == VrSdk.DoF.Six) + { + vTrashSoundPos = InputManager.m_Instance.GetControllerPosition( + InputManager.ControllerName.Wand); + } + AudioManager.m_Instance.PlayTrashSound(vTrashSoundPos); + PromoManager.m_Instance.RequestAdvancedPanelsPromo(); + break; + case GlobalCommands.SymmetryPlane: + if (PointerManager.m_Instance.CurrentSymmetryMode != SymmetryMode.SinglePlane) + { + PointerManager.m_Instance.SetSymmetryMode(SymmetryMode.SinglePlane); + ControllerConsoleScript.m_Instance.AddNewLine("Mirror Enabled"); + } + else + { + PointerManager.m_Instance.SetSymmetryMode(SymmetryMode.None); + ControllerConsoleScript.m_Instance.AddNewLine("Mirror Off"); + } + break; + case GlobalCommands.MultiMirror: + if (PointerManager.m_Instance.CurrentSymmetryMode != SymmetryMode.MultiMirror) + { + PointerManager.m_Instance.SetSymmetryMode(SymmetryMode.MultiMirror); + ControllerConsoleScript.m_Instance.AddNewLine("Symmetry Enabled"); + } + else + { + PointerManager.m_Instance.SetSymmetryMode(SymmetryMode.None); + ControllerConsoleScript.m_Instance.AddNewLine("Symmetry Off"); + } + InputManager.m_Instance.TriggerHaptics(InputManager.ControllerName.Brush, 0.1f); + break; + case GlobalCommands.SymmetryTwoHanded: + if (PointerManager.m_Instance.CurrentSymmetryMode != SymmetryMode.TwoHanded) + { + PointerManager.m_Instance.SetSymmetryMode(SymmetryMode.TwoHanded); + ControllerConsoleScript.m_Instance.AddNewLine("Symmetry Enabled"); + } + else + { + PointerManager.m_Instance.SetSymmetryMode(SymmetryMode.None); + ControllerConsoleScript.m_Instance.AddNewLine("Symmetry Off"); + } + InputManager.m_Instance.TriggerHaptics(InputManager.ControllerName.Brush, 0.1f); + break; + case GlobalCommands.StraightEdge: + PointerManager.m_Instance.StraightEdgeModeEnabled = !PointerManager.m_Instance.StraightEdgeModeEnabled; + if (PointerManager.m_Instance.StraightEdgeModeEnabled) + { + ControllerConsoleScript.m_Instance.AddNewLine("Straight Edge On"); + } + else + { + ControllerConsoleScript.m_Instance.AddNewLine("Straight Edge Off"); + } + break; + case GlobalCommands.AutoOrient: + m_AutoOrientAfterRotation = !m_AutoOrientAfterRotation; + if (m_AutoOrientAfterRotation) + { + ControllerConsoleScript.m_Instance.AddNewLine("Auto-Orient On"); + } + else + { + ControllerConsoleScript.m_Instance.AddNewLine("Auto-Orient Off"); + } + break; + case GlobalCommands.Undo: + SketchMemoryScript.m_Instance.StepBack(); + break; + case GlobalCommands.Redo: + SketchMemoryScript.m_Instance.StepForward(); + break; + case GlobalCommands.AudioVisualization: // Intentionally blank. + break; + case GlobalCommands.ResetAllPanels: + m_PanelManager.ResetWandPanelsConfiguration(); + EatGazeObjectInput(); + break; + case GlobalCommands.SketchOrigin: + m_SketchSurfacePanel.EnableSpecificTool(BaseTool.ToolType.SketchOrigin); + EatGazeObjectInput(); + break; + case GlobalCommands.ViewOnly: + m_ViewOnly = !m_ViewOnly; + RequestPanelsVisibility(!m_ViewOnly); + PointerManager.m_Instance.RequestPointerRendering(!m_ViewOnly); + // TODO - decide if this is a permanent change + // With this line, you can't set a tool such as fly or teleport + // and switch to View Only mode as the mode change disables all tools + //m_SketchSurface.SetActive(!m_ViewOnly); + m_Decor.SetActive(!m_ViewOnly); + break; + case GlobalCommands.SaveGallery: + m_SketchSurfacePanel.EnableSpecificTool(BaseTool.ToolType.SaveIconTool); + break; + case GlobalCommands.DropCam: + // Want to enable this if in monoscopic or VR modes. + // TODO: seems odd to tie this switch to the controller type, should be based on some + // other build-time configuration setting. + if (App.VrSdk.GetControllerDof() != VrSdk.DoF.None) + { + m_DropCam.Show(!m_DropCam.gameObject.activeSelf); + } + break; + case GlobalCommands.AnalyticsEnabled_Deprecated: + break; + case GlobalCommands.ToggleAutosimplification: + QualityControls.AutosimplifyEnabled = !QualityControls.AutosimplifyEnabled; + break; + case GlobalCommands.Credits: + LoadSketch(new DiskSceneFileInfo(m_CreditsSketchFilename, embedded: true, readOnly: true)); + EatGazeObjectInput(); + break; + case GlobalCommands.AshleysSketch: + LoadSketch(new DiskSceneFileInfo(m_AshleysSketchFilename, embedded: true, readOnly: true)); + EatGazeObjectInput(); + break; + case GlobalCommands.FAQ: + OpenURLAndInformUser(m_HelpCenterURL); + break; + case GlobalCommands.ReleaseNotes: + OpenURLAndInformUser(m_ReleaseNotesURL); + break; + case GlobalCommands.ExportRaw: + if (!FileUtils.CheckDiskSpaceWithError(App.UserExportPath())) + { + return; + } + EatGazeObjectInput(); + StartCoroutine(ExportCoroutine()); + break; + case GlobalCommands.IRC: + if (m_IRCChatWidget == null) + { + GameObject widgetobject = (GameObject)Instantiate(m_IRCChatPrefab); + widgetobject.transform.parent = App.Instance.m_RoomTransform; + m_IRCChatWidget = widgetobject.GetComponent(); + m_IRCChatWidget.Show(true); + } + else + { + m_IRCChatWidget.Show(false); + m_IRCChatWidget = null; + } + break; + case GlobalCommands.YouTubeChat: + if (m_YouTubeChatWidget == null) + { + GameObject widgetobject = (GameObject)Instantiate(m_YouTubeChatPrefab); + widgetobject.transform.parent = App.Instance.m_RoomTransform; + m_YouTubeChatWidget = widgetobject.GetComponent(); + m_YouTubeChatWidget.Show(true); + } + else + { + m_YouTubeChatWidget.Show(false); + m_YouTubeChatWidget = null; + } + break; + case GlobalCommands.CameraOptions: + // If we're switching in to Camera mode, make sure Multicam is selected. + if (!m_PanelManager.CameraActive()) + { + SketchSurfacePanel.m_Instance.EnableSpecificTool(BaseTool.ToolType.MultiCamTool); + } + m_PanelManager.ToggleCameraPanels(); + PointerManager.m_Instance.EatLineEnabledInput(); + SketchSurfacePanel.m_Instance.EatToolsInput(); + break; + case GlobalCommands.ShowSketchFolder: + { + var index = iParam1; + var sketchSetType = (SketchSetType)iParam2; + SketchSet sketchSet = SketchCatalog.m_Instance.GetSet(sketchSetType); + SceneFileInfo rInfo = sketchSet.GetSketchSceneFileInfo(index); + EatGazeObjectInput(); + //launch external window and tell the user we did so + //this call is windows only + if ((Application.platform == RuntimePlatform.WindowsPlayer) || + (Application.platform == RuntimePlatform.WindowsEditor)) + { + OutputWindowScript.m_Instance.CreateInfoCardAtController( + InputManager.ControllerName.Brush, + kRemoveHeadsetFyi, fPopScalar: 0.5f); + System.Diagnostics.Process.Start("explorer.exe", + "/select," + rInfo.FullPath); + } + break; + } + case GlobalCommands.About: + OpenURLAndInformUser(m_ThirdPartyNoticesURL); + break; + case GlobalCommands.StencilsDisabled: + SketchMemoryScript.m_Instance.PerformAndRecordCommand(new StencilsVisibleCommand()); + break; + case GlobalCommands.StraightEdgeMeterDisplay: + PointerManager.m_Instance.StraightEdgeGuide.FlipMeter(); + break; + case GlobalCommands.Sketchbook: + m_PanelManager.ToggleSketchbookPanels(); + PointerManager.m_Instance.EatLineEnabledInput(); + SketchSurfacePanel.m_Instance.EatToolsInput(); + break; + case GlobalCommands.StraightEdgeShape: + // Previously experimental mode only. + // Untested and currently untriggerable. + PointerManager.m_Instance.StraightEdgeGuide.SetTempShape( + (StraightEdgeGuideScript.Shape)iParam1); + break; + case GlobalCommands.DeleteSketch: + { + var sketchSetType = (SketchSetType)iParam2; + SketchSet sketchSet = SketchCatalog.m_Instance.GetSet(sketchSetType); + sketchSet.DeleteSketch(iParam1); + DismissPopupOnCurrentGazeObject(false); + break; + } + case GlobalCommands.RenameSketch: + { + var sketchSetType = (SketchSetType)iParam2; + SketchSet sketchSet = SketchCatalog.m_Instance.GetSet(sketchSetType); + if (sketchSetType == SketchSetType.User) + { + sketchSet.RenameSketch(iParam1, KeyboardPopUpWindow.m_LastInput); + } + DismissPopupOnCurrentGazeObject(false); + break; + } + case GlobalCommands.RenameLayer: + { + var layer = App.Scene.GetCanvasByLayerIndex(iParam1); + App.Scene.RenameLayer(layer, KeyboardPopUpWindow.m_LastInput); + DismissPopupOnCurrentGazeObject(false); + break; + } + case GlobalCommands.EditMultiplayerRoomName: + { + var panel = (MultiplayerPanel)m_PanelManager.GetActivePanelByType(BasePanel.PanelType.Multiplayer); + panel.RoomName = KeyboardPopUpWindow.m_LastInput; + DismissPopupOnCurrentGazeObject(false); + break; + } + case GlobalCommands.EditMultiplayerNickName: + { + var panel = (MultiplayerPanel)m_PanelManager.GetActivePanelByType(BasePanel.PanelType.Multiplayer); + panel.NickName = KeyboardPopUpWindow.m_LastInput; + DismissPopupOnCurrentGazeObject(false); + break; + } + case GlobalCommands.ShowWindowGUI: + break; + case GlobalCommands.Disco: + LightsControlScript.m_Instance.DiscoMode = !LightsControlScript.m_Instance.DiscoMode; + break; + case GlobalCommands.AccountInfo: break; // Intentionally blank. + case GlobalCommands.LoginToGenericCloud: + { + var ident = App.GetIdentity((Cloud)iParam1); + if (!ident.LoggedIn) { ident.LoginAsync(); } + // iParam2 is being used as a UX flag. If not set to the default, it will cause the UI + // to lose focus. + if (iParam2 != -1) { EatGazeObjectInput(); } + break; + } + case GlobalCommands.LogOutOfGenericCloud: + { + var ident = App.GetIdentity((Cloud)iParam1); + if (ident.LoggedIn) { ident.Logout(); } + break; + } + case GlobalCommands.UploadToGenericCloud: + { + Cloud cloud = (Cloud)iParam1; + var ident = App.GetIdentity(cloud); + if (!ident.LoggedIn) + { + ident.LoginAsync(); + break; + } + SelectionManager.m_Instance.ClearActiveSelection(); + VrAssetService.m_Instance.UploadCurrentSketchAsync(cloud, isDemoUpload: false).AsAsyncVoid(); + EatGazeObjectInput(); + break; + } + case GlobalCommands.ViewOnlineGallery: + OpenURLAndInformUser(kTiltBrushGalleryUrl); + break; + case GlobalCommands.CancelUpload: + VrAssetService.m_Instance.CancelUpload(); + break; + case GlobalCommands.ViewLastUpload: + if (VrAssetService.m_Instance.LastUploadCompleteUrl != null) + { + var url = VrAssetService.m_Instance.LastUploadCompleteUrl; + App.OpenURL(url); + + // The upload flow is different on mobile and requires the user to manually accept + // that they'll go to the browser for publishing. In that case, we want to reset + // state when the leave to publish. This is automatically part of the + // UploadPopUpWindow state flow on PC. + if (App.Config.IsMobileHardware) + { + DismissPopupOnCurrentGazeObject(true); + } + } + break; + case GlobalCommands.ShowGoogleDrive: + string baseDriveUrl = "https://drive.google.com"; + string driveURL = !App.GoogleIdentity.LoggedIn ? baseDriveUrl : + string.Format( + "http://accounts.google.com/AccountChooser?Email={0}&continue={1}", + App.GoogleIdentity.Profile.email, baseDriveUrl); + OpenURLAndInformUser(driveURL); + break; + case GlobalCommands.GoogleDriveSync: + App.DriveSync.SyncEnabled = !App.DriveSync.SyncEnabled; + break; + case GlobalCommands.GoogleDriveSync_Folder: + App.DriveSync.ToggleSyncOnFolderOfType((DriveSync.SyncedFolderType)iParam1); + break; + case GlobalCommands.Duplicate: + { + int selectedVerts = SelectionManager.m_Instance.NumVertsInSelection; + + // TODO - this code has never taken imported models etc into account + if (PointerManager.m_Instance.CurrentSymmetryMode == SymmetryMode.MultiMirror) + { + selectedVerts *= PointerManager.m_Instance.CustomMirrorMatrices.Count; + } + + if (!SketchMemoryScript.m_Instance.MemoryWarningAccepted && + SketchMemoryScript.m_Instance.WillVertCountPutUsOverTheMemoryLimit(selectedVerts)) + { + AudioManager.m_Instance.PlayUploadCanceledSound(InputManager.Wand.Transform.position); + if (!m_PanelManager.MemoryWarningActive()) + { + m_PanelManager.ToggleMemoryWarningMode(); + } + } + else + { + ClipboardManager.Instance.DuplicateSelection( + stampMode: IsUserInteractingWithSelectionWidget()); + } + EatToolScaleInput(); + break; + } + case GlobalCommands.AdvancedPanelsToggle: + m_PanelManager.ToggleAdvancedPanels(); + // If we're now in basic mode, ensure we don't have advanced abilities. + if (!m_PanelManager.AdvancedModeActive()) + { + m_WidgetManager.StencilsDisabled = true; + m_WidgetManager.CameraPathsVisible = false; + App.Switchboard.TriggerStencilModeChanged(); + m_SketchSurfacePanel.EnsureUserHasBasicToolEnabled(); + if (PointerManager.m_Instance.CurrentSymmetryMode != SymmetryMode.None) + { + PointerManager.m_Instance.SetSymmetryMode(SymmetryMode.None, false); + } + } + PromoManager.m_Instance.RecordCompletion(PromoType.AdvancedPanels); + EatGazeObjectInput(); + break; + case GlobalCommands.Music: break; // Intentionally blank. + case GlobalCommands.ToggleGroupStrokesAndWidgets: + SelectionManager.m_Instance.ToggleGroupSelectedStrokesAndWidgets(); + EatToolScaleInput(); + break; + case GlobalCommands.SaveModel: + SaveModel(); + break; + case GlobalCommands.ViewPolyPage: + OpenURLAndInformUser(kPolyMainPageUri); + break; + case GlobalCommands.ViewPolyGallery: + OpenURLAndInformUser(kBlocksGalleryUrl); + break; + case GlobalCommands.ExportListed: + StartCoroutine(ExportListAndQuit()); + break; + case GlobalCommands.RenderCameraPath: + StartCoroutine(RenderPathAndQuit()); + break; + case GlobalCommands.ToggleProfiling: + ToggleProfiling(); + break; + case GlobalCommands.DoAutoProfile: + DoAutoProfile(); + break; + case GlobalCommands.DoAutoProfileAndQuit: + DoAutoProfileAndQuit(); + break; + case GlobalCommands.ToggleSettings: + m_PanelManager.ToggleSettingsPanels(); + PointerManager.m_Instance.EatLineEnabledInput(); + SketchSurfacePanel.m_Instance.EatToolsInput(); + break; + case GlobalCommands.SummonMirror: + PointerManager.m_Instance.BringSymmetryToUser(); + break; + case GlobalCommands.InvertSelection: + SelectionManager.m_Instance.InvertSelection(); + break; + case GlobalCommands.SelectAll: + SketchSurfacePanel.m_Instance.EnableSpecificTool(BaseTool.ToolType.SelectionTool); + SelectionManager.m_Instance.SelectAll(); + EatGazeObjectInput(); + break; + case GlobalCommands.FlipSelection: + SelectionManager.m_Instance.FlipSelection(); + break; + case GlobalCommands.ToggleBrushLab: + m_PanelManager.ToggleBrushLabPanels(); + PointerManager.m_Instance.EatLineEnabledInput(); + SketchSurfacePanel.m_Instance.EatToolsInput(); + break; + case GlobalCommands.ToggleCameraPostEffects: + CameraConfig.PostEffects = !CameraConfig.PostEffects; + break; + case GlobalCommands.ToggleWatermark: + if (PlayerPrefs.GetInt("Promo_Contribution", 0) == 0) + { + OutputWindowScript.m_Instance.CreateInfoCardAtController( + InputManager.ControllerName.Wand, + m_ContributionPromoText, fPopScalar: 1.0f); + PlayerPrefs.SetInt("Promo_Contribution", 1); + } + CameraConfig.Watermark = !CameraConfig.Watermark; + break; + case GlobalCommands.LoadConfirmComplexHigh: + IssueGlobalCommand(GlobalCommands.Load, iParam1, iParam2, null); + break; + case GlobalCommands.LoadConfirmComplex: + { + var index = iParam1; + var sketchSetType = (SketchSetType)iParam2; + bool loadSketch = true; + + // If the sketchbook is active, we may want to show a popup instead of load. + if (m_PanelManager.SketchbookActive()) + { + BasePanel sketchBook = m_PanelManager.GetSketchBookPanel(); + if (sketchBook != null) + { + // Get triangle count from cloud scene file info. + SketchSet sketchSet = SketchCatalog.m_Instance.GetSet(sketchSetType); + SceneFileInfo sfi = sketchSet.GetSketchSceneFileInfo(index); + int tris = sfi.TriangleCount ?? -1; + + // Show "this is bad" popup if we're over the triangle limit. + if (tris > QualityControls.m_Instance.AppQualityLevels.MaxPolySketchTriangles) + { + loadSketch = false; + sketchBook.CreatePopUp(GlobalCommands.LoadConfirmComplexHigh, iParam1, iParam2); + } + else if (tris > + QualityControls.m_Instance.AppQualityLevels.WarningPolySketchTriangles) + { + // Show, "this could be bad" popup if we're over the warning limit. + loadSketch = false; + sketchBook.CreatePopUp(GlobalCommands.Load, iParam1, iParam2); + } + } + } + + if (loadSketch) + { + IssueGlobalCommand(GlobalCommands.Load, iParam1, iParam2, null); + } + } + break; + case GlobalCommands.LoadConfirmUnsaved: + { + BasePanel sketchBook = m_PanelManager.GetSketchBookPanel(); + if ((sketchBook != null) && SketchMemoryScript.m_Instance.IsMemoryDirty()) + { + sketchBook.CreatePopUp(GlobalCommands.LoadWaitOnDownload, iParam1, iParam2, null); + } + else + { + IssueGlobalCommand(GlobalCommands.LoadWaitOnDownload, iParam1, iParam2, null); + } + } + break; + case GlobalCommands.LoadWaitOnDownload: + { + bool download = false; + if (iParam2 == (int)SketchSetType.Drive) + { + BasePanel sketchBook = m_PanelManager.GetSketchBookPanel(); + var googleSketchSet = SketchCatalog.m_Instance.GetSet(SketchSetType.Drive); + if (sketchBook != null + && googleSketchSet != null + && googleSketchSet.IsSketchIndexValid(iParam1) + && !googleSketchSet.GetSketchSceneFileInfo(iParam1).Available) + { + sketchBook.CreatePopUp(GlobalCommands.LoadConfirmComplex, iParam1, iParam2, null); + download = true; + } + } + if (!download) + { + IssueGlobalCommand(GlobalCommands.LoadConfirmComplex, iParam1, iParam2, null); + } + } + break; + case GlobalCommands.MemoryWarning: + if (iParam1 > 0) + { + SketchMemoryScript.m_Instance.MemoryWarningAccepted = true; + } + m_PanelManager.ToggleMemoryWarningMode(); + break; + case GlobalCommands.MemoryExceeded: + // If we're in the memory exceeded app state, exit. + if (App.CurrentState == App.AppState.MemoryExceeded) + { + App.Instance.SetDesiredState(App.AppState.Standard); + } + else + { + // If we're not in the full app state, just switch our panel mode. + m_PanelManager.ToggleMemoryWarningMode(); + } + break; + case GlobalCommands.ShowTos: + OpenURLAndInformUser(m_TosURL); + break; + case GlobalCommands.ShowPrivacy: + OpenURLAndInformUser(m_PrivacyURL); + break; + case GlobalCommands.ShowQuestSideLoading: + OpenURLAndInformUser(m_QuestSideLoadingHowToURL); + break; + case GlobalCommands.ShowContribution: + OpenURLAndInformUser(m_ContributionURL); + break; + case GlobalCommands.UnloadReferenceImageCatalog: + ReferenceImageCatalog.m_Instance.UnloadAllImages(); + break; + case GlobalCommands.ToggleCameraPathVisuals: + m_WidgetManager.CameraPathsVisible = !m_WidgetManager.CameraPathsVisible; + break; + case GlobalCommands.ToggleCameraPathPreview: + m_WidgetManager.FollowingPath = !m_WidgetManager.FollowingPath; + break; + case GlobalCommands.DeleteCameraPath: + { + var cameraPath = m_WidgetManager.GetCurrentCameraPath(); + GrabWidget cameraPathWidget = cameraPath == null ? null : cameraPath.m_WidgetScript; + m_WidgetManager.DeleteCameraPath(cameraPathWidget); + } + break; + case GlobalCommands.RecordCameraPath: + // Turn off MultiCam if we're going to record the camera path. + if (m_SketchSurfacePanel.GetCurrentToolType() == BaseTool.ToolType.MultiCamTool) + { + m_SketchSurfacePanel.EnableDefaultTool(); + } + CameraPathCaptureRig.RecordPath(); + EatGazeObjectInput(); + break; + case GlobalCommands.OpenScriptsCommandsList: + OpenURLAndInformUser($"http://localhost:{App.HttpServer.HttpPort}/help/commands"); + break; + case GlobalCommands.OpenScriptsList: + OpenURLAndInformUser($"http://localhost:{App.HttpServer.HttpPort}/scripts"); + break; + case GlobalCommands.OpenExampleScriptsList: + OpenURLAndInformUser($"http://localhost:{App.HttpServer.HttpPort}/examplescripts"); + break; + case GlobalCommands.MultiplayerTogglePanel: + m_PanelManager.ToggleMultiplayerPanels(); + PointerManager.m_Instance.EatLineEnabledInput(); + SketchSurfacePanel.m_Instance.EatToolsInput(); + break; + case GlobalCommands.RepaintOptions: break; // Intentionally blank. + case GlobalCommands.Null: break; // Intentionally blank. + case GlobalCommands.MultiplayerPanelOptions: break; // Intentionally blank. + case GlobalCommands.MultiplayerJoinRoom: break; // Intentionally blank. + case GlobalCommands.MultiplayerLeaveRoom: break; // Intentionally blank. + case GlobalCommands.MultiplayerConnect: break; // Intentionally blank. + case GlobalCommands.MultiplayerDisconnect: break; // Intentionally blank. + default: + Debug.LogError($"Unrecognized command {rEnum}"); + break; + } + } + + private void LoadNamed(string path, bool quickload, bool additive) + { + var fileInfo = new DiskSceneFileInfo(path); + fileInfo.ReadMetadata(); + if (SaveLoadScript.m_Instance.LastMetadataError != null) + { + ControllerConsoleScript.m_Instance.AddNewLine( + string.Format("Error detected in sketch '{0}'.\nTry re-saving.", + fileInfo.HumanName)); + Debug.LogWarning(string.Format("Error reading metadata for {0}.\n{1}", + fileInfo.FullPath, SaveLoadScript.m_Instance.LastMetadataError)); + } + LoadSketch(fileInfo, quickload, additive); + if (m_ControlsType != ControlsType.ViewingOnly) + { + EatGazeObjectInput(); + } + } + + public void OpenURLAndInformUser(string url) + { + // On desktop - launch external browser and inform the user + // On mobile - the browser appears over the app + if (!App.Config.IsMobileHardware) + { + OutputWindowScript.m_Instance.CreateInfoCardAtController( + InputManager.ControllerName.Brush, + kRemoveHeadsetFyi, fPopScalar: 0.5f); + } + App.OpenURL(url); + EatGazeObjectInput(); + } + + public bool IsCommandActive(GlobalCommands rEnum, int iParam = -1) + { + switch (rEnum) + { + case GlobalCommands.StraightEdge: return PointerManager.m_Instance.StraightEdgeModeEnabled; + case GlobalCommands.StraightEdgeMeterDisplay: return PointerManager.m_Instance.StraightEdgeGuide.IsShowingMeter(); + case GlobalCommands.SymmetryPlane: return PointerManager.m_Instance.CurrentSymmetryMode == SymmetryMode.SinglePlane; + case GlobalCommands.MultiMirror: return PointerManager.m_Instance.CurrentSymmetryMode == SymmetryMode.MultiMirror; + case GlobalCommands.SymmetryTwoHanded: return PointerManager.m_Instance.CurrentSymmetryMode == SymmetryMode.TwoHanded; + case GlobalCommands.AutoOrient: return m_AutoOrientAfterRotation; + case GlobalCommands.AudioVisualization: return VisualizerManager.m_Instance.VisualsRequested; + case GlobalCommands.AdvancedPanelsToggle: return m_PanelManager.AdvancedModeActive(); + case GlobalCommands.Music: return VisualizerManager.m_Instance.VisualsRequested; + case GlobalCommands.DropCam: return m_DropCam.gameObject.activeSelf; + case GlobalCommands.ToggleAutosimplification: return QualityControls.AutosimplifyEnabled; + case GlobalCommands.DraftingVisibility: return m_DraftingVisibility == (DraftingVisibilityOption)iParam; + case GlobalCommands.Cameras: + return SketchSurfacePanel.m_Instance.GetCurrentToolType() == BaseTool.ToolType.AutoGif || + SketchSurfacePanel.m_Instance.GetCurrentToolType() == BaseTool.ToolType.ScreenshotTool; + case GlobalCommands.IRC: return m_IRCChatWidget != null; + case GlobalCommands.YouTubeChat: return m_YouTubeChatWidget != null; + case GlobalCommands.StencilsDisabled: return m_WidgetManager.StencilsDisabled; + case GlobalCommands.StraightEdgeShape: + // Previously experimental mode only. + // Untested and currently untriggerable. + return PointerManager.m_Instance.StraightEdgeGuide.TempShape == (StraightEdgeGuideScript.Shape)iParam || + (PointerManager.m_Instance.StraightEdgeGuide.TempShape == StraightEdgeGuideScript.Shape.None + && PointerManager.m_Instance.StraightEdgeGuide.CurrentShape == (StraightEdgeGuideScript.Shape)iParam); + case GlobalCommands.Disco: return LightsControlScript.m_Instance.DiscoMode; + case GlobalCommands.ToggleGroupStrokesAndWidgets: return SelectionManager.m_Instance.UngroupingAllowed; + case GlobalCommands.ToggleProfiling: return UnityEngine.Profiling.Profiler.enabled; + case GlobalCommands.ToggleCameraPostEffects: return CameraConfig.PostEffects; + case GlobalCommands.ToggleWatermark: return CameraConfig.Watermark; + case GlobalCommands.ToggleCameraPathVisuals: return m_WidgetManager.CameraPathsVisible; + case GlobalCommands.ToggleCameraPathPreview: return m_WidgetManager.FollowingPath; + case GlobalCommands.SelectCameraPath: + return m_WidgetManager.IsCameraPathAtIndexCurrent(iParam) && + m_WidgetManager.CameraPathsVisible; + case GlobalCommands.GoogleDriveSync_Folder: + return App.DriveSync.IsFolderOfTypeSynced((DriveSync.SyncedFolderType)iParam); + case GlobalCommands.GoogleDriveSync: return App.DriveSync.SyncEnabled; + case GlobalCommands.RecordCameraPath: return VideoRecorderUtils.ActiveVideoRecording != null; + } + return false; + } + + public void NewSketch(bool fade) + { + LightsControlScript.m_Instance.DiscoMode = false; + m_WidgetManager.FollowingPath = false; + SketchMemoryScript.m_Instance.ClearMemory(); + ControllerConsoleScript.m_Instance.AddNewLine("Sketch Cleared"); + ResetGrabbedPose(everything: true); + QualityControls.m_Instance.ResetAutoQuality(); + InputManager.m_Instance.TriggerHaptics(InputManager.ControllerName.Brush, 0.1f); + SaveLoadScript.m_Instance.ResetLastFilename(); + SelectionManager.m_Instance.RemoveFromSelection(false); + PointerManager.m_Instance.ResetSymmetryToHome(); + App.Scene.ResetLayers(notify: true); + ApiManager.Instance.ResetBrushTransform(); + + // If we've got the camera path tool active, switch back to the default tool. + // I'm doing this because if we leave the camera path tool active, the camera path + // panel shows the button highlighted, which affects the user's flow for being + // invited to start a path. It looks weird. + if (m_SketchSurfacePanel.ActiveToolType == BaseTool.ToolType.CameraPathTool) + { + m_SketchSurfacePanel.EnableDefaultTool(); + } + + m_WidgetManager.DestroyAllWidgets(); + if (LightsControlScript.m_Instance.LightsChanged || + SceneSettings.m_Instance.EnvironmentChanged) + { + SceneSettings.m_Instance.RecordSkyColorsForFading(); + SceneSettings.m_Instance.SetDesiredPreset( + SceneSettings.m_Instance.GetDesiredPreset(), skipFade: !fade); + } + // Blank the thumbnail position so that autosave won't save the thumbnail position to be + // the one from the old sketch. + SaveLoadScript.m_Instance.LastThumbnail_SS = null; + + // Re-set the quality level to reset simplification level + QualityControls.m_Instance.QualityLevel = QualityControls.m_Instance.QualityLevel; + + App.PolyAssetCatalog.ClearLoadingQueue(); + App.PolyAssetCatalog.UnloadUnusedModels(); + } + + private bool WorldIsReset(bool toSavedXf) + { + return App.Scene.Pose == + (toSavedXf ? SketchMemoryScript.m_Instance.InitialSketchTransform : TrTransform.identity); + } + + public bool IsCommandAvailable(GlobalCommands rEnum, int iParam = -1) + { + // TODO: hide gallery view / publish if there are no saved sketches + switch (rEnum) + { + case GlobalCommands.Undo: return SketchMemoryScript.m_Instance.CanUndo(); + case GlobalCommands.Redo: return SketchMemoryScript.m_Instance.CanRedo(); + case GlobalCommands.Save: + bool canSave = + SaveLoadScript.m_Instance.SceneFile.Valid && + SaveLoadScript.m_Instance.IsSavingAllowed(); + return canSave && (!WorldIsReset(toSavedXf: true) || + (SketchHasChanges() && SketchMemoryScript.m_Instance.IsMemoryDirty())); + case GlobalCommands.SaveOptions: + case GlobalCommands.SaveNew: + case GlobalCommands.SaveGallery: + return SketchHasChanges(); + case GlobalCommands.SaveOnLocalChanges: + if (!SaveLoadScript.m_Instance.SceneFile.Valid) + { + // No save file, but something has changed. + return SketchHasChanges(); + } + else + { + if (SaveLoadScript.m_Instance.CanOverwriteSource) + { + // Save file, and it's our file. Whether we have changes is irrelevant. + return true; + } + // Save file, but it's not our file. Only make a copy if there are local changes. + return SketchMemoryScript.m_Instance.IsMemoryDirty(); + } + case GlobalCommands.UploadToGenericCloud: + return SketchMemoryScript.m_Instance.HasVisibleObjects() || + m_WidgetManager.ExportableModelWidgets.Any(w => w.gameObject.activeSelf) || + m_WidgetManager.ImageWidgets.Any(w => w.gameObject.activeSelf) || + VrAssetService.m_Instance.UploadProgress >= 1.0f || + VrAssetService.m_Instance.LastUploadFailed; + case GlobalCommands.SaveAndUpload: + return App.GoogleIdentity.LoggedIn && + (VrAssetService.m_Instance.UploadProgress <= 0.0f) && + IsCommandAvailable(GlobalCommands.UploadToGenericCloud); + case GlobalCommands.NewSketch: + return SketchHasChanges(); + case GlobalCommands.Credits: + case GlobalCommands.AshleysSketch: + return !SketchHasChanges() && !SketchMemoryScript.m_Instance.IsMemoryDirty(); + case GlobalCommands.Tiltasaurus: return TiltBrush.Tiltasaurus.m_Instance.TiltasaurusAvailable(); + case GlobalCommands.ExportRaw: + return SketchMemoryScript.m_Instance.HasVisibleObjects() || + m_WidgetManager.ModelWidgets.Any(w => w.gameObject.activeSelf) || + m_WidgetManager.ImageWidgets.Any(w => w.gameObject.activeSelf); + case GlobalCommands.ResetAllPanels: return m_PanelManager.PanelsHaveBeenCustomized(); + case GlobalCommands.Duplicate: return ClipboardManager.Instance.CanCopy; + case GlobalCommands.ToggleGroupStrokesAndWidgets: return SelectionManager.m_Instance.SelectionCanBeGrouped; + case GlobalCommands.SaveModel: return SelectionManager.m_Instance.HasSelection; + case GlobalCommands.SummonMirror: + return PointerManager.m_Instance.CurrentSymmetryMode != + SymmetryMode.None; + case GlobalCommands.InvertSelection: + case GlobalCommands.FlipSelection: + return SelectionManager.m_Instance.HasSelection; + case GlobalCommands.SelectAll: + return SketchMemoryScript.m_Instance.HasVisibleObjects() || + m_WidgetManager.HasSelectableWidgets(); + case GlobalCommands.UnloadReferenceImageCatalog: + return ReferenceImageCatalog.m_Instance.AnyImageValid(); + case GlobalCommands.ToggleCameraPathPreview: + return m_WidgetManager.CanRecordCurrentCameraPath(); + case GlobalCommands.DeleteCameraPath: + return CameraPathCaptureRig.Enabled && m_WidgetManager.AnyActivePathHasAKnot(); + case GlobalCommands.ToggleCameraPathVisuals: + return m_WidgetManager.AnyActivePathHasAKnot(); + case GlobalCommands.GoogleDriveSync: + return App.GoogleIdentity.LoggedIn; + case GlobalCommands.RecordCameraPath: + return m_WidgetManager.CameraPathsVisible; + case GlobalCommands.AdvancedPanelsToggle: + return !(MultiplayerManager.m_Instance.State == ConnectionState.IN_ROOM); + case GlobalCommands.MultiplayerConnect: + return MultiplayerManager.m_Instance.IsConnectable(); + case GlobalCommands.MultiplayerDisconnect: + return MultiplayerManager.m_Instance.IsDisconnectable(); + case GlobalCommands.MultiplayerJoinRoom: + return !PanelManager.m_Instance.AdvancedModeActive() && MultiplayerManager.m_Instance.CanJoinRoom(); + case GlobalCommands.MultiplayerLeaveRoom: + return MultiplayerManager.m_Instance.CanLeaveRoom(); + } + return true; + } + + public bool SketchHasChanges() + { + if (SceneSettings.m_Instance.IsTransitioning) { return false; } + return SketchMemoryScript.m_Instance.HasVisibleObjects() || + SceneSettings.m_Instance.EnvironmentChanged || + LightsControlScript.m_Instance.LightsChanged || + m_WidgetManager.ModelWidgets.Any(w => w.gameObject.activeSelf) || + m_WidgetManager.LightWidgets.Any(w => w.gameObject.activeSelf) || + m_WidgetManager.StencilWidgets.Any(w => w.gameObject.activeSelf) || + m_WidgetManager.ImageWidgets.Any(w => w.gameObject.activeSelf) || + m_WidgetManager.VideoWidgets.Any(w => w.gameObject.activeSelf) || + m_WidgetManager.AnyCameraPathWidgetsActive; + } + + public void OpenPanelOfType(BasePanel.PanelType type, TrTransform trSpawnXf) + { + m_PanelManager.OpenPanel(type, trSpawnXf); + EatGazeObjectInput(); + } + + public void RestoreFloatingPanels() + { + if (!m_SketchSurfacePanel.ActiveTool.HidePanels()) + { + m_PanelManager.RestoreHiddenPanels(); + } + } + + public void UpdateDraftingVisibility() + { + float value = 0; + switch (m_DraftingVisibility) + { + case DraftingVisibilityOption.Visible: + value = 1; + break; + case DraftingVisibilityOption.Transparent: + value = .5f; + break; + case DraftingVisibilityOption.Hidden: + value = 0; + break; + } + Shader.SetGlobalFloat("_DraftingVisibility01", value); + } + + private void ToggleProfiling() + { + if (Debug.isDebugBuild && ProfileDisplay.Instance != null) + { + ProfileDisplay.Instance.gameObject.SetActive(UnityEngine.Profiling.Profiler.enabled); + } + if (UnityEngine.Profiling.Profiler.enabled) + { + ProfilingManager.Instance.StopProfiling(); + } + else + { + ProfilingManager.Instance.StartProfiling(App.UserConfig.Profiling.ProfilingMode); + } + } + + private void DoAutoProfile() + { + StartCoroutine(DoProfiling()); + } + + private void DoAutoProfileAndQuit() + { + StartCoroutine(DoProfiling(andQuit: true)); + } + + private IEnumerator DoProfiling(bool andQuit = false) + { + TrTransform oldWandPose = TrTransform.FromTransform(InputManager.Wand.Geometry.transform); + TrTransform oldBrushPose = TrTransform.FromTransform(InputManager.Brush.Geometry.transform); + + App.AppState oldState = App.CurrentState; + App.Instance.SetDesiredState(App.AppState.AutoProfiling); + while (App.CurrentState != App.AppState.AutoProfiling) + { + yield return null; + } + + TrTransform camPose = App.Scene.Pose * SaveLoadScript.m_Instance.ReasonableThumbnail_SS; + camPose.ToTransform(App.VrSdk.GetVrCamera().transform); + float controllerDirection = App.UserConfig.Profiling.ShowControllers ? 1f : -1f; + Vector3 roffset = Camera.main.transform.right * 2f; + Vector3 fOffset = Camera.main.transform.forward * 4f * controllerDirection; + InputManager.Brush.Geometry.transform.position = Camera.main.transform.position + roffset + fOffset; + InputManager.Brush.Geometry.transform.rotation = Camera.main.transform.rotation; + InputManager.Wand.Geometry.transform.position = Camera.main.transform.position - roffset + fOffset; + InputManager.Wand.Geometry.transform.rotation = Camera.main.transform.rotation; + m_PanelManager.LockPanelsToController(); + + ProfilingManager.Instance.StartProfiling(App.UserConfig.Profiling.ProfilingMode); + yield return new WaitForSeconds(App.UserConfig.Profiling.Duration); + ProfilingManager.Instance.StopProfiling(); + + if (App.UserConfig.Profiling.TakeScreenshot) + { + GameObject camObj = new GameObject("ScreenShotter"); + Camera cam = camObj.AddComponent(); + cam.CopyFrom(App.VrSdk.GetVrCamera()); + cam.stereoTargetEye = StereoTargetEyeMask.None; + cam.clearFlags = CameraClearFlags.SolidColor; + camPose.ToTransform(camObj.transform); + int res = App.UserConfig.Profiling.ScreenshotResolution; + RenderTexture renderTexture = RenderTexture.GetTemporary(res, res, 24); + try + { + cam.targetTexture = renderTexture; + cam.Render(); + RenderTexture prev = RenderTexture.active; + RenderTexture.active = renderTexture; + var texture = new Texture2D(res, res, TextureFormat.RGB24, false); + texture.ReadPixels(new Rect(0, 0, texture.width, texture.height), 0, 0); + RenderTexture.active = prev; + byte[] jpegBytes = texture.EncodeToJPG(); + string filename = + Path.GetFileNameWithoutExtension(SaveLoadScript.m_Instance.SceneFile.FullPath); + File.WriteAllBytes(Path.Combine(App.UserPath(), filename + ".jpg"), jpegBytes); + } + finally + { + Destroy(camObj); + RenderTexture.ReleaseTemporary(renderTexture); + } + } + + oldWandPose.ToTransform(InputManager.Wand.Geometry.transform); + oldBrushPose.ToTransform(InputManager.Brush.Geometry.transform); + App.Instance.SetDesiredState(oldState); + + if (andQuit) + { + QuitApp(); + } + } + } + +} // namespace TiltBrush From 0bdd4ec47e037f871b61e6c9ac85cc4c3b4c50d9 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Thu, 31 Oct 2024 11:40:09 +0000 Subject: [PATCH 069/174] Force Windows to use LF line endings for .cs and .py files Force Windows to use LF line endings for .cs and .py files --- .editorconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/.editorconfig b/.editorconfig index e497deceac..69d4fd0505 100644 --- a/.editorconfig +++ b/.editorconfig @@ -10,6 +10,7 @@ indent_size = 2 [*.{cs,py}] indent_style = space indent_size = 4 +end_of_line = lf # Microsoft .NET properties csharp_new_line_before_members_in_object_initializers = false From c99e7c59dfb9b82cd3b74e36e618efaf4ae1cf19 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Mon, 4 Nov 2024 17:23:06 +0000 Subject: [PATCH 070/174] Update BaseCommand.cs Adding timestamps when photon is present --- Assets/Scripts/Commands/BaseCommand.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Assets/Scripts/Commands/BaseCommand.cs b/Assets/Scripts/Commands/BaseCommand.cs index 45492bcf57..90784696b5 100644 --- a/Assets/Scripts/Commands/BaseCommand.cs +++ b/Assets/Scripts/Commands/BaseCommand.cs @@ -29,6 +29,13 @@ public class BaseCommand : IDisposable private Guid m_Guid; private BaseCommand m_Parent; protected List m_Children; + private int m_Timestamp; // Updated to use an integer timestamp from Photon server + + public int Timestamp + { + get { return m_Timestamp; } + set { m_Timestamp = value; } + } public int ChildrenCount { @@ -77,6 +84,9 @@ public BaseCommand(BaseCommand parent = null) parent.m_Children.Add(this); m_Parent = parent; } +#if FUSION_WEAVER + m_Timestamp = Photon.Pun.PhotonNetwork.ServerTimestamp; // Use Photon server timestamp +#endif } /// True if this command changes the sketch in a saveable From 09c20cfdbd76f741fa5ae002c552a475934d7eeb Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Tue, 5 Nov 2024 17:20:52 +0000 Subject: [PATCH 071/174] Fix the mirror for multiplayer Fix the mirror widget for multiplayer The first problem is related to the fact that the current implementation of the multiplayer does not properly transmit and serialize the chain of commands especially the property of the basecommand m_Children and m_Parent. The second problem this commit address is the fact that In mirror mode, the brush stroke command is invoked twice. Initially, a single brush stroke command is sent. The second invocation sends a command chain, including the initial brush stroke as a parent and a second mirrored stroke as its child. Sending both instances through PhotonRPC causes issues, as the first brush stroke command is received twice, leading to duplication and processing errors on the receiving peer. To resolve this we use PerformAndRecordCommand in place of RecordCommand, and we change the PointerManager.FinalizeLine(), PointerScript.DetachLine(), SketchMemoryScript.MemorizeBatchedBrushStroke() to utilize a parameter --- Assets/Scripts/Commands/BaseCommand.cs | 6 +++++- .../Scripts/Multiplayer/Photon/PhotonManager.cs | 16 +++++++++++----- Assets/Scripts/PointerManager.cs | 6 +++++- Assets/Scripts/PointerScript.cs | 9 +++++++-- Assets/Scripts/SketchMemoryScript.cs | 9 ++++++--- 5 files changed, 34 insertions(+), 12 deletions(-) diff --git a/Assets/Scripts/Commands/BaseCommand.cs b/Assets/Scripts/Commands/BaseCommand.cs index 90784696b5..f264d0213e 100644 --- a/Assets/Scripts/Commands/BaseCommand.cs +++ b/Assets/Scripts/Commands/BaseCommand.cs @@ -31,6 +31,10 @@ public class BaseCommand : IDisposable protected List m_Children; private int m_Timestamp; // Updated to use an integer timestamp from Photon server + public void SetParent(BaseCommand parent) + { + m_Parent = parent; + } public int Timestamp { get { return m_Timestamp; } @@ -85,7 +89,7 @@ public BaseCommand(BaseCommand parent = null) m_Parent = parent; } #if FUSION_WEAVER - m_Timestamp = Photon.Pun.PhotonNetwork.ServerTimestamp; // Use Photon server timestamp + m_Timestamp = Photon.Pun.PhotonNetwork.ServerTimestamp; #endif } diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs index 0a295062e7..11555e3212 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs @@ -23,6 +23,7 @@ using Fusion.Photon.Realtime; using Fusion.Sockets; using TiltBrush; +using System.ComponentModel.Design; namespace OpenBrush.Multiplayer @@ -240,22 +241,22 @@ public async Task RpcSyncToSharedAnchor(string uuid) private bool ProcessCommand(BaseCommand command) { bool success = true; + switch (command) { case BrushStrokeCommand: - success = CommandBrushStroke(command as BrushStrokeCommand); + success &= CommandBrushStroke(command as BrushStrokeCommand); break; case DeleteStrokeCommand: - success = CommandDeleteStroke(command as DeleteStrokeCommand); + success &= CommandDeleteStroke(command as DeleteStrokeCommand); break; case SwitchEnvironmentCommand: - success = CommandSwitchEnvironment(command as SwitchEnvironmentCommand); + success &= CommandSwitchEnvironment(command as SwitchEnvironmentCommand); break; case BaseCommand: - success = CommandBase(command); + success &= CommandBase(command); break; default: - // Don't know how to process this command success = false; break; } @@ -264,6 +265,10 @@ private bool ProcessCommand(BaseCommand command) { foreach (var child in command.Children) { + if (child.ParentGuid == Guid.Empty) + { + child.SetParent(command); + } success &= ProcessCommand(child); } } @@ -271,6 +276,7 @@ private bool ProcessCommand(BaseCommand command) return success; } + private bool CommandBrushStroke(BrushStrokeCommand command) { var stroke = command.m_Stroke; diff --git a/Assets/Scripts/PointerManager.cs b/Assets/Scripts/PointerManager.cs index 85f9b8ff28..c7cafd1efc 100644 --- a/Assets/Scripts/PointerManager.cs +++ b/Assets/Scripts/PointerManager.cs @@ -1544,6 +1544,7 @@ void FinalizeLine(bool isContinue = false, bool discard = false) } else { + SketchMemoryScript.StrokeFlags flags = SketchMemoryScript.StrokeFlags.None; if (groupStart == null) { @@ -1557,7 +1558,10 @@ void FinalizeLine(bool isContinue = false, bool discard = false) // Verify IsGroupContinue invariant Debug.Assert(pointer.TimestampMs == groupStartTime); } - pointer.DetachLine(bDiscardLine, null, flags); + + // Set isFinalStroke to true only for the last pointer to ensure command chain invokes once all strokes are chained + bool isFinalStroke = (i == m_NumActivePointers - 1); + pointer.DetachLine(bDiscardLine, null, flags, isFinalStroke); } } } diff --git a/Assets/Scripts/PointerScript.cs b/Assets/Scripts/PointerScript.cs index a2d0a713df..ac9c22f53d 100644 --- a/Assets/Scripts/PointerScript.cs +++ b/Assets/Scripts/PointerScript.cs @@ -933,7 +933,8 @@ public void RecreateLineFromMemory(Stroke stroke) public void DetachLine( bool bDiscard, Stroke rMemoryObjectForPlayback, - SketchMemoryScript.StrokeFlags strokeFlags = SketchMemoryScript.StrokeFlags.None) + SketchMemoryScript.StrokeFlags strokeFlags = SketchMemoryScript.StrokeFlags.None, + bool isFinalStroke = false) { if (rMemoryObjectForPlayback != null) @@ -1001,7 +1002,11 @@ public void DetachLine( m_CurrentBrushSize, m_CurrentLine.StrokeScale, m_ControlPoints, strokeFlags, - WidgetManager.m_Instance.ActiveStencil, m_LineLength_CS, m_CurrentLine.RandomSeed); + WidgetManager.m_Instance.ActiveStencil, + m_LineLength_CS, + m_CurrentLine.RandomSeed, + isFinalStroke + ); } else { diff --git a/Assets/Scripts/SketchMemoryScript.cs b/Assets/Scripts/SketchMemoryScript.cs index 8239780bbe..a7b8e798e1 100644 --- a/Assets/Scripts/SketchMemoryScript.cs +++ b/Assets/Scripts/SketchMemoryScript.cs @@ -510,7 +510,8 @@ public void MemorizeBatchedBrushStroke( BatchSubset subset, Color rColor, Guid brushGuid, float fBrushSize, float brushScale, List rControlPoints, StrokeFlags strokeFlags, - StencilWidget stencil, float lineLength, int seed) + StencilWidget stencil, float lineLength, int seed, + bool isFinalStroke) { // NOTE: PointerScript calls ClearRedo() in batch case @@ -527,8 +528,10 @@ public void MemorizeBatchedBrushStroke( rNewStroke.m_Seed = seed; subset.m_Stroke = rNewStroke; - SketchMemoryScript.m_Instance.RecordCommand( - new BrushStrokeCommand(rNewStroke, stencil, lineLength)); + SketchMemoryScript.m_Instance.PerformAndRecordCommand( + new BrushStrokeCommand(rNewStroke, stencil, lineLength), + invoke: isFinalStroke + ); if (m_SanityCheckStrokes) { From 47701658f72cc5c01cce7ce662aee04577613ee3 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Thu, 7 Nov 2024 19:18:45 +0000 Subject: [PATCH 072/174] Fix Check if Room Exist Fix on MultiplayerManager.cs and extend the range of checks on MultiplayerPannel.cs --- Assets/Scripts/GUI/MultiplayerPanel.cs | 23 +++++++++++++++++-- .../Scripts/Multiplayer/MultiplayerManager.cs | 2 ++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/Assets/Scripts/GUI/MultiplayerPanel.cs b/Assets/Scripts/GUI/MultiplayerPanel.cs index 286156114a..0ac33adff4 100644 --- a/Assets/Scripts/GUI/MultiplayerPanel.cs +++ b/Assets/Scripts/GUI/MultiplayerPanel.cs @@ -77,6 +77,7 @@ public void Awake() { CheckAdvancedModeActive, CheckMultiplayerManagerErrors, + CheckIfRoomExist, }; if (MultiplayerManager.m_Instance != null) MultiplayerManager.m_Instance.StateUpdated += OnStateUpdated; @@ -155,10 +156,15 @@ private void OnStateUpdated(ConnectionState newState) private Tuple CheckAdvancedModeActive() { - bool isAdvancedModeActive = PanelManager.m_Instance.AdvancedModeActive(); - return Tuple.Create(isAdvancedModeActive, "Switch to beginner mode to Join Room"); + if (PanelManager.m_Instance != null) + { + bool isAdvancedModeActive = PanelManager.m_Instance.AdvancedModeActive(); + return Tuple.Create(isAdvancedModeActive, "Switch to beginner mode to Join Room"); + } + return Tuple.Create(false, ""); } + private Tuple CheckMultiplayerManagerErrors() { @@ -172,6 +178,19 @@ private Tuple CheckMultiplayerManagerErrors() } + private Tuple CheckIfRoomExist() + { + + if (MultiplayerManager.m_Instance != null && MultiplayerManager.m_Instance.State == ConnectionState.IN_LOBBY) + { + if (MultiplayerManager.m_Instance.DoesRoomNameExist(data.roomName)) + return Tuple.Create(true, $"The room {data.roomName} already exist your joining an existing session."); + } + + return Tuple.Create(false, ""); + + } + private void Alerts() { if (m_AlertsErrors) diff --git a/Assets/Scripts/Multiplayer/MultiplayerManager.cs b/Assets/Scripts/Multiplayer/MultiplayerManager.cs index 4372a862c6..8170a8a3cc 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerManager.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerManager.cs @@ -132,6 +132,7 @@ void Start() } if (m_VoiceManager != null && m_Manager != null) State = ConnectionState.INITIALIZED; + roomDataRefreshed += OnRoomDataRefreshed; localPlayerJoined += OnLocalPlayerJoined; remotePlayerJoined += OnRemotePlayerJoined; playerLeft += OnPlayerLeft; @@ -142,6 +143,7 @@ void Start() void OnDestroy() { + roomDataRefreshed -= OnRoomDataRefreshed; localPlayerJoined -= OnLocalPlayerJoined; remotePlayerJoined -= OnRemotePlayerJoined; playerLeft -= OnPlayerLeft; From 18560a78a30a00381f1a3077cea0154eb38ce18d Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Thu, 7 Nov 2024 22:50:56 +0000 Subject: [PATCH 073/174] Room Owner Update Photon Manager with a GetPlayerCount() method used to establish if a user is a room owner Update Multiplayer Manager with a isUserRoomOwner --- .../Multiplayer/MultiplayerInterfaces.cs | 1 + .../Scripts/Multiplayer/MultiplayerManager.cs | 16 +++++++- .../Multiplayer/Photon/PhotonManager.cs | 37 +++++++++++++------ 3 files changed, 40 insertions(+), 14 deletions(-) diff --git a/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs b/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs index 2f61908e5d..3786fade1d 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs @@ -34,6 +34,7 @@ public interface IDataConnectionHandler : IConnectionHandler void Update(); + int GetPlayerCount(); Task PerformCommand(BaseCommand command); Task UndoCommand(BaseCommand command); Task RedoCommand(BaseCommand command); diff --git a/Assets/Scripts/Multiplayer/MultiplayerManager.cs b/Assets/Scripts/Multiplayer/MultiplayerManager.cs index 8170a8a3cc..c3c11a8ae0 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerManager.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerManager.cs @@ -49,6 +49,7 @@ public class MultiplayerManager : MonoBehaviour public Action playerLeft; public Action> roomDataRefreshed; public event Action StateUpdated; + public event Action UserInfoStateUpdated; private List m_RoomData = new List(); ulong myOculusUserId; @@ -57,8 +58,8 @@ public class MultiplayerManager : MonoBehaviour internal string UserId; [HideInInspector] public string CurrentRoomName; - //public ConnectionState State => m_Manager?.State ?? ConnectionState.DISCONNECTED; private ConnectionState _state; + public ConnectionState State { get => _state; @@ -67,10 +68,11 @@ private set if (_state != value) { _state = value; - StateUpdated?.Invoke(_state); // Trigger the event when the state changes + StateUpdated?.Invoke(_state); } } } + public string LastError { get; private set; } public ConnectionUserInfo UserInfo @@ -87,6 +89,8 @@ public ConnectionUserInfo UserInfo public RoomCreateData data; + public bool isUserRoomOwner = false; //temporary public + void Awake() { m_Instance = this; @@ -327,7 +331,11 @@ void Update() void OnLocalPlayerJoined(int id, ITransientData playerData) { + // the user is the room owner if is the firt to get in + isUserRoomOwner = m_Manager.GetPlayerCount() == 1 ? true : false; + m_LocalPlayer = playerData; + } void OnRemotePlayerJoined(int id, ITransientData playerData) @@ -353,6 +361,10 @@ void OnPlayerLeft(int id) m_RemotePlayers.Remove(player); } } + + // TODO extend to more than two users case + if (m_RemotePlayers.Count == 0) isUserRoomOwner = true; // If there are no other players left, the local player becomes the room owner + } private async void OnCommandPerformed(BaseCommand command) diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs index 11555e3212..79640f8d83 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs @@ -85,6 +85,22 @@ public async Task Init() State = ConnectionState.INITIALIZED; 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(player.PlayerId, newPlayer.GetComponent()); + m_PlayersSpawning.Remove(player); + } + } + } + + #region IConnectionHandler Methods public async Task Connect() { @@ -194,21 +210,19 @@ public async Task LeaveRoom(bool force) } - public void Update() + #endregion + + #region IDataConnectionHandler Methods + + public int GetPlayerCount() { - var copy = m_PlayersSpawning.ToList(); - foreach (var player in copy) + if (m_Runner != null) { - var newPlayer = m_Runner.GetPlayerObject(player); - if (newPlayer != null) - { - m_Manager.remotePlayerJoined?.Invoke(player.PlayerId, newPlayer.GetComponent()); - m_PlayersSpawning.Remove(player); - } + return m_Runner.SessionInfo.PlayerCount; } + return 0; } - #region IConnectionHandler Methods public async Task PerformCommand(BaseCommand command) { await Task.Yield(); @@ -368,8 +382,7 @@ public void OnPlayerJoined(NetworkRunner runner, PlayerRef player) 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(player.PlayerId, m_LocalPlayer); + m_Manager.localPlayerJoined?.Invoke(player.PlayerId, m_LocalPlayer); } else { From 22e7df7676c69cbc56db8895e2e63b3b9e901480 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Fri, 8 Nov 2024 10:22:39 +0000 Subject: [PATCH 074/174] Implement basic history synchronization Upon a remote player joining, the room owner now transmits all commands in the operation stack, ensuring the new player receives the complete command history for a consistent session state. --- .../Scripts/Multiplayer/MultiplayerManager.cs | 20 ++++++++++- Assets/Scripts/SketchMemoryScript.cs | 34 ++++++++++++++++++- 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/Assets/Scripts/Multiplayer/MultiplayerManager.cs b/Assets/Scripts/Multiplayer/MultiplayerManager.cs index c3c11a8ae0..73f80d578e 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerManager.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerManager.cs @@ -261,7 +261,11 @@ public async Task Disconnect() public bool DoesRoomNameExist(string roomName) { - return m_RoomData.Any(room => room.roomName == roomName); + bool roomExist = m_RoomData.Any(room => room.roomName == roomName); + + if (roomExist) { isUserRoomOwner = false; } + + return roomExist; } void OnRoomDataRefreshed(List rooms) @@ -343,6 +347,10 @@ void OnRemotePlayerJoined(int id, ITransientData playerData) Debug.Log("Adding new player to track."); playerData.PlayerId = id; m_RemotePlayers.Add(playerData); + + //if i am the room owner I should send the command history + if (isUserRoomOwner) SendCommandHistory(); + } void OnPlayerLeft(int id) @@ -427,6 +435,16 @@ private void OnConnectionHandlerDisconnected() Disconnected?.Invoke(); } + private void SendCommandHistory() + { + + foreach (var command in SketchMemoryScript.m_Instance.GetOperationStack()) + { + OnCommandPerformed(command); + } + + } + public void StartSpeaking() { m_VoiceManager?.StartSpeaking(); diff --git a/Assets/Scripts/SketchMemoryScript.cs b/Assets/Scripts/SketchMemoryScript.cs index a7b8e798e1..58809d77ae 100644 --- a/Assets/Scripts/SketchMemoryScript.cs +++ b/Assets/Scripts/SketchMemoryScript.cs @@ -16,6 +16,8 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Security.Cryptography; +using System.Text; namespace TiltBrush { @@ -76,8 +78,9 @@ public static void SetFlag(ref SketchMemoryScript.StrokeFlags flags, } } - // stack of sketch operations this session + // stack of sketch operations this session\\\ private Stack m_OperationStack; + private HashSet m_GuidCache; // Cache of GUIDs for network transmission // stack of undone operations available for redo private Stack m_RedoStack; @@ -191,6 +194,11 @@ public LinkedList GetMemoryList get { return m_MemoryList; } } + public IEnumerable GetOperationStack() + { + return m_OperationStack; + } + public Stroke GetStrokeAtIndex(int index) { return m_Instance.m_MemoryList.ElementAt(index); @@ -282,6 +290,21 @@ public bool IsMemoryDirty() return false; } + public IReadOnlyCollection GetCommandGuids() + { + return m_GuidCache; + } + + private string CalculateGuidCacheHash() + { + using (var md5 = MD5.Create()) + { + var combined = string.Join("", m_GuidCache.OrderBy(g => g).Select(g => g.ToString())); + byte[] hashBytes = md5.ComputeHash(Encoding.UTF8.GetBytes(combined)); + return BitConverter.ToString(hashBytes).Replace("-", "").ToLowerInvariant(); + } + } + public bool CanUndo() { return m_OperationStack.Count > 0; } public bool CanRedo() { return m_RedoStack.Count > 0; } @@ -290,6 +313,7 @@ void Awake() m_OperationStack = new Stack(); m_LastOperationStackCount = 0; m_RedoStack = new Stack(); + m_GuidCache = new HashSet(); m_HasVisibleObjects = false; m_MemoryExceeded = false; m_MemoryWarningAccepted = false; @@ -383,9 +407,11 @@ public void PerformAndRecordCommand(BaseCommand command, bool discardIfNotMerged while (m_OperationStack.Any()) { BaseCommand top = m_OperationStack.Pop(); + m_GuidCache.Remove(top.Guid); if (!top.Merge(command)) { m_OperationStack.Push(top); + m_GuidCache.Add(top.Guid); break; } discardCommand = false; @@ -398,6 +424,7 @@ public void PerformAndRecordCommand(BaseCommand command, bool discardIfNotMerged } delta.Redo(); m_OperationStack.Push(command); + m_GuidCache.Add(command.Guid); OperationStackChanged?.Invoke(); if (invoke) @@ -414,14 +441,17 @@ public void RecordCommand(BaseCommand command) while (m_OperationStack.Any()) { BaseCommand top = m_OperationStack.Pop(); + m_GuidCache.Remove(top.Guid); if (!top.Merge(command)) { m_OperationStack.Push(top); + m_GuidCache.Add(top.Guid); break; } command = top; } m_OperationStack.Push(command); + m_GuidCache.Add(command.Guid); OperationStackChanged?.Invoke(); CommandPerformed?.Invoke(command); } @@ -800,6 +830,7 @@ public void ClearMemory() } } m_OperationStack.Clear(); + m_GuidCache.Clear(); OperationStackChanged?.Invoke(); m_LastOperationStackCount = 0; m_MemoryList.Clear(); @@ -919,6 +950,7 @@ public IEnumerator RepaintCoroutine() public void StepBack(bool invoke = true) { var comm = m_OperationStack.Pop(); + m_GuidCache.Remove(comm.Guid); comm.Undo(); m_RedoStack.Push(comm); OperationStackChanged?.Invoke(); From 2eaac258c3e0691ecf022d0bb584b2a0aed25ad8 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Fri, 8 Nov 2024 10:49:57 +0000 Subject: [PATCH 075/174] Clear scene for non-room owner players on join Clear scene for non-room owner players on join: If a player joins and is not the room owner, their scene is reset to ensure consistency with the room owner's state upon receiving the synchronized history. --- Assets/Scripts/Multiplayer/MultiplayerManager.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Assets/Scripts/Multiplayer/MultiplayerManager.cs b/Assets/Scripts/Multiplayer/MultiplayerManager.cs index 73f80d578e..d017b3ac72 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerManager.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerManager.cs @@ -186,6 +186,8 @@ public async Task JoinRoom(RoomCreateData RoomData) { State = ConnectionState.JOINING_ROOM; + if (!isUserRoomOwner) SketchMemoryScript.m_Instance.ClearMemory(); + bool successData = false; if (m_Manager != null) successData = await m_Manager.JoinRoom(RoomData); From ff80520e1e7d866470ea7e7ff817616b4298a927 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Fri, 8 Nov 2024 15:10:59 +0000 Subject: [PATCH 076/174] Improving Remote Player Registration Remote players were not correctly registered when already present in the room because `INetworkRunnerCallbacks.OnPlayerJoined` in Fusion 1 is not called for users who are already in the room. To address this, we added and registered existing users using the `CheckExistingUsers()` method. Additionally, in `MultiplayerManager.cs`, the local player ID was not registered correctly, which has now been fixed. A display for both the local and remote user IDs was added to `MultiplayerManagerEditor.cs` to facilitate debugging. --- Assets/Editor/MultiplayerManagerEditor.cs | 25 +++++++++++++++++++ Assets/Scripts/GUI/MultiplayerPanel.cs | 1 - .../Scripts/Multiplayer/MultiplayerManager.cs | 7 +++--- .../Multiplayer/Photon/PhotonManager.cs | 23 ++++++++++++++--- 4 files changed, 47 insertions(+), 9 deletions(-) diff --git a/Assets/Editor/MultiplayerManagerEditor.cs b/Assets/Editor/MultiplayerManagerEditor.cs index cb4a7e07c2..b101c292bd 100644 --- a/Assets/Editor/MultiplayerManagerEditor.cs +++ b/Assets/Editor/MultiplayerManagerEditor.cs @@ -24,6 +24,31 @@ public override void OnInspectorGUI() // Room data input fields roomName = EditorGUILayout.TextField("Room Name", roomName); + // Display m_LocalPlayer if assigned + if (multiplayerManager.m_LocalPlayer != null) + { + EditorGUILayout.LabelField("Local Player Data", EditorStyles.boldLabel); + EditorGUILayout.LabelField("UserId", multiplayerManager.m_LocalPlayer.PlayerId.ToString()); + } + else + { + EditorGUILayout.LabelField("Local Player is not assigned."); + } + + // Display each entry in m_RemotePlayers list + if (multiplayerManager.m_RemotePlayers != null && multiplayerManager.m_RemotePlayers.Count > 0) + { + EditorGUILayout.LabelField("Remote Players Data", EditorStyles.boldLabel); + foreach (var remotePlayer in multiplayerManager.m_RemotePlayers) + { + EditorGUILayout.LabelField("UserId", remotePlayer.PlayerId.ToString()); + } + } + else + { + EditorGUILayout.LabelField("Remote Players are not assigned."); + } + // Button to join the lobby if (GUILayout.Button("Join Lobby") ) { diff --git a/Assets/Scripts/GUI/MultiplayerPanel.cs b/Assets/Scripts/GUI/MultiplayerPanel.cs index 0ac33adff4..19c7622eb0 100644 --- a/Assets/Scripts/GUI/MultiplayerPanel.cs +++ b/Assets/Scripts/GUI/MultiplayerPanel.cs @@ -107,7 +107,6 @@ private static string GenerateRandomRoomName() return random.Next(100000, 999999).ToString(); } - private void UpdateDisplay() { if (m_RoomNumber) m_RoomNumber.text = "RoomName: " + data.roomName; diff --git a/Assets/Scripts/Multiplayer/MultiplayerManager.cs b/Assets/Scripts/Multiplayer/MultiplayerManager.cs index d017b3ac72..d62cef75c5 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerManager.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerManager.cs @@ -41,8 +41,8 @@ public class MultiplayerManager : MonoBehaviour private IDataConnectionHandler m_Manager; private IVoiceConnectionHandler m_VoiceManager; - private ITransientData m_LocalPlayer; - private List> m_RemotePlayers; + public ITransientData m_LocalPlayer; + public List> m_RemotePlayers; public Action> localPlayerJoined; public Action> remotePlayerJoined; @@ -339,14 +339,13 @@ void OnLocalPlayerJoined(int id, ITransientData playerData) { // the user is the room owner if is the firt to get in isUserRoomOwner = m_Manager.GetPlayerCount() == 1 ? true : false; - m_LocalPlayer = playerData; + m_LocalPlayer.PlayerId = id; } void OnRemotePlayerJoined(int id, ITransientData playerData) { - Debug.Log("Adding new player to track."); playerData.PlayerId = id; m_RemotePlayers.Add(playerData); diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs index 79640f8d83..6d5b2e5f98 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs @@ -23,7 +23,7 @@ using Fusion.Photon.Realtime; using Fusion.Sockets; using TiltBrush; -using System.ComponentModel.Design; + namespace OpenBrush.Multiplayer @@ -161,6 +161,20 @@ public async Task JoinRoom(RoomCreateData roomCreateData) } + public void CheckExistingUsers() + { + int playercount = m_Runner.SessionInfo.PlayerCount; + + foreach (PlayerRef player in m_Runner.ActivePlayers) + { + if (player != m_Runner.LocalPlayer) + { + m_PlayersSpawning.Add(player); + } + } + + } + public async Task Disconnect() { State = ConnectionState.DISCONNECTING; @@ -363,6 +377,7 @@ private bool CommandSwitchEnvironment(SwitchEnvironmentCommand command) #endregion #region Photon Callbacks + public void OnConnectedToServer(NetworkRunner runner) { var rpc = m_Runner.gameObject.AddComponent(); @@ -371,8 +386,7 @@ public void OnConnectedToServer(NetworkRunner runner) public void OnPlayerJoined(NetworkRunner runner, PlayerRef player) { - Debug.Log($"OnPlayerJoined called. PlayerRef: {player.PlayerId}"); - + try { @@ -382,7 +396,8 @@ public void OnPlayerJoined(NetworkRunner runner, PlayerRef player) 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(player.PlayerId, m_LocalPlayer); + m_Manager.localPlayerJoined?.Invoke(player.PlayerId, m_LocalPlayer); + CheckExistingUsers(); } else { From cd2eab324548b0c35dc5eac4a8d68def41e28083 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Fri, 8 Nov 2024 20:11:53 +0000 Subject: [PATCH 077/174] Fix Remote User List Clearing Fixed issue where remote user list was not cleared on disconnect or room exit. --- Assets/Scripts/Multiplayer/MultiplayerManager.cs | 15 ++++++++------- .../Scripts/Multiplayer/Photon/PhotonManager.cs | 3 ++- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/Assets/Scripts/Multiplayer/MultiplayerManager.cs b/Assets/Scripts/Multiplayer/MultiplayerManager.cs index d62cef75c5..0756784c1d 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerManager.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerManager.cs @@ -87,9 +87,9 @@ public ConnectionUserInfo UserInfo } } - public RoomCreateData data; + [HideInInspector] public RoomCreateData data; - public bool isUserRoomOwner = false; //temporary public + [HideInInspector] public bool isUserRoomOwner = false; //temporary public void Awake() { @@ -429,11 +429,12 @@ async void ShareAnchors() private void OnConnectionHandlerDisconnected() { - // Clean up local player reference - m_LocalPlayer = null; - - // Invoke the Disconnected event - Disconnected?.Invoke(); + m_LocalPlayer = null;// Clean up local player reference + m_RemotePlayers.Clear();// Clean up remote player references + LastError = null; + State = ConnectionState.DISCONNECTED; + StateUpdated?.Invoke(State); + Disconnected?.Invoke();// Invoke the Disconnected event } private void SendCommandHistory() diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs index 6d5b2e5f98..f5e3f28b99 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs @@ -187,6 +187,7 @@ public async Task Disconnect() m_Runner.Despawn(m_LocalPlayer.Object); m_LocalPlayer = null; } + m_PlayersSpawning.Clear(); await m_Runner.Shutdown(forceShutdownProcedure: false); GameObject.Destroy(m_Runner.gameObject); @@ -194,7 +195,7 @@ public async Task Disconnect() if (m_Runner.IsShutdown) { State = ConnectionState.DISCONNECTED; - ControllerConsoleScript.m_Instance.AddNewLine("[PhotonManager] Left Room"); + ControllerConsoleScript.m_Instance.AddNewLine("[PhotonManager] Disconnected successfully"); UserInfo = new ConnectionUserInfo { UserId = m_Runner.UserId }; } else From 3445c02c5b8f1ea2df6dde6c72b3cef28aeaee0e Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Fri, 8 Nov 2024 20:12:13 +0000 Subject: [PATCH 078/174] Update MultiplayerManagerEditor.cs Enhanced MultiplayerManager Editor to streamline debugging with clearer information display and controls. --- Assets/Editor/MultiplayerManagerEditor.cs | 118 +++++++++++++++------- 1 file changed, 81 insertions(+), 37 deletions(-) diff --git a/Assets/Editor/MultiplayerManagerEditor.cs b/Assets/Editor/MultiplayerManagerEditor.cs index b101c292bd..2abb6ff3dc 100644 --- a/Assets/Editor/MultiplayerManagerEditor.cs +++ b/Assets/Editor/MultiplayerManagerEditor.cs @@ -3,6 +3,7 @@ using UnityEngine; using OpenBrush.Multiplayer; using System.Threading.Tasks; +using System.ComponentModel.Composition; #if UNITY_EDITOR [CustomEditor(typeof(MultiplayerManager))] @@ -16,65 +17,99 @@ public class MultiplayerManagerInspector : Editor public override void OnInspectorGUI() { - // Get the target object (MultiplayerManager) multiplayerManager = (MultiplayerManager)target; - GUILayout.Label("Multiplayer Manager Controls", EditorStyles.boldLabel); + DrawDefaultInspector(); + + GUILayout.Space(10); + EditorGUILayout.LabelField("", GUI.skin.horizontalSlider); + GUILayout.Space(10); - // Room data input fields roomName = EditorGUILayout.TextField("Room Name", roomName); - // Display m_LocalPlayer if assigned - if (multiplayerManager.m_LocalPlayer != null) - { - EditorGUILayout.LabelField("Local Player Data", EditorStyles.boldLabel); - EditorGUILayout.LabelField("UserId", multiplayerManager.m_LocalPlayer.PlayerId.ToString()); - } - else - { - EditorGUILayout.LabelField("Local Player is not assigned."); - } + //State + string connectionState = ""; + if (multiplayerManager != null) connectionState = multiplayerManager.State.ToString(); + else connectionState = "Not Assigned"; - // Display each entry in m_RemotePlayers list - if (multiplayerManager.m_RemotePlayers != null && multiplayerManager.m_RemotePlayers.Count > 0) - { - EditorGUILayout.LabelField("Remote Players Data", EditorStyles.boldLabel); - foreach (var remotePlayer in multiplayerManager.m_RemotePlayers) - { - EditorGUILayout.LabelField("UserId", remotePlayer.PlayerId.ToString()); - } - } - else - { - EditorGUILayout.LabelField("Remote Players are not assigned."); - } + GUILayout.Space(10); + EditorGUILayout.LabelField("", GUI.skin.horizontalSlider); + EditorGUILayout.BeginHorizontal(); + GUILayout.Label("Connection State: ", EditorStyles.boldLabel); + EditorGUILayout.LabelField($"{connectionState}"); + EditorGUILayout.EndHorizontal(); + GUILayout.Space(5); - // Button to join the lobby - if (GUILayout.Button("Join Lobby") ) + if (GUILayout.Button("Connect") ) { ConnectToLobby(); - EditorUtility.SetDirty(target); // Mark the object as dirty to recognize state changes + EditorUtility.SetDirty(target); } - // Button to join the room + if (GUILayout.Button("Join Room")) { ConnectToRoom(); - EditorUtility.SetDirty(target); // Update inspector on state change + EditorUtility.SetDirty(target); } - // Button to exit the room + if (GUILayout.Button("Exit Room") ) { DisconnectFromRoom(); - EditorUtility.SetDirty(target); // Update inspector on state change + EditorUtility.SetDirty(target); } - // Force the inspector to repaint to reflect the latest state - Repaint(); - // Draw default inspector below - DrawDefaultInspector(); + if (GUILayout.Button("Disconnect")) + { + Disconnect(); + EditorUtility.SetDirty(target); + } + + + //Local Player Id + string localPlayerId = ""; + if (multiplayerManager.m_LocalPlayer != null) localPlayerId = multiplayerManager.m_LocalPlayer.PlayerId.ToString(); + else localPlayerId = "Not Assigned"; + + GUILayout.Space(10); + EditorGUILayout.LabelField("", GUI.skin.horizontalSlider); + EditorGUILayout.BeginHorizontal(); + GUILayout.Label("Local Player ID: ", EditorStyles.boldLabel); + EditorGUILayout.LabelField($"{localPlayerId}"); + EditorGUILayout.EndHorizontal(); + + //Room Ownership + string ownership = ""; + if (multiplayerManager != null && multiplayerManager.isUserRoomOwner) ownership = "Yes"; + else if (multiplayerManager != null && !multiplayerManager.isUserRoomOwner) ownership = "No"; + else ownership = "Not Assigned"; + + EditorGUILayout.BeginHorizontal(); + GUILayout.Label("Is Local Player Room Owner:", EditorStyles.boldLabel); + EditorGUILayout.LabelField($"{ownership}"); + EditorGUILayout.EndHorizontal(); + + //Remote Users + string remoteUsersRegistered = ""; + if (multiplayerManager.m_RemotePlayers != null && multiplayerManager.m_RemotePlayers.Count > 0) + { + remoteUsersRegistered = "UserIds:[ "; + foreach (var remotePlayer in multiplayerManager.m_RemotePlayers) + remoteUsersRegistered += remotePlayer.PlayerId.ToString()+","; + remoteUsersRegistered += "]"; + } + else remoteUsersRegistered = "Not Assigned"; + + //Registered remote players + EditorGUILayout.BeginHorizontal(); + GUILayout.Label("Registered Remote Players IDs:", EditorStyles.boldLabel); + EditorGUILayout.LabelField($"{remoteUsersRegistered}"); + EditorGUILayout.EndHorizontal(); + + Repaint(); + } private async void ConnectToLobby() @@ -110,5 +145,14 @@ private async void DisconnectFromRoom() } } + + private async void Disconnect() + { + if (multiplayerManager != null) + { + bool success = await multiplayerManager.Disconnect(); + + } + } } #endif From 88447dead4c91c47538733c18ff10a6872897112 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Mon, 11 Nov 2024 11:02:41 +0000 Subject: [PATCH 079/174] Introduce coroutine for sending command history via Photon network This update refactors `SendCommandHistory` to use a coroutine, sending commands in batches with a delay between each batch. This approach reduces the likelihood of Photon RPC message delivery failures that occur when too many messages are sent within the same frame. The coroutine yields between batches and includes a configurable delay to ensure smooth, reliable transmission over the network. --- .../Scripts/Multiplayer/MultiplayerManager.cs | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/Assets/Scripts/Multiplayer/MultiplayerManager.cs b/Assets/Scripts/Multiplayer/MultiplayerManager.cs index 0756784c1d..09c08d811e 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerManager.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerManager.cs @@ -17,6 +17,8 @@ using System.Linq; using System.Threading.Tasks; using UnityEngine; +using System.Collections; + #if OCULUS_SUPPORTED using OVRPlatform = Oculus.Platform; @@ -34,6 +36,9 @@ public enum MultiplayerType public class MultiplayerManager : MonoBehaviour { + public int batchSize = 1; + public float delayBetweenBatches = 0.25f; + public static MultiplayerManager m_Instance; public MultiplayerType m_MultiplayerType; public event Action Disconnected; @@ -350,7 +355,7 @@ void OnRemotePlayerJoined(int id, ITransientData playerData) m_RemotePlayers.Add(playerData); //if i am the room owner I should send the command history - if (isUserRoomOwner) SendCommandHistory(); + if (isUserRoomOwner) StartCoroutine(SendCommandHistory()); } @@ -437,16 +442,26 @@ private void OnConnectionHandlerDisconnected() Disconnected?.Invoke();// Invoke the Disconnected event } - private void SendCommandHistory() + private IEnumerator SendCommandHistory() { + IEnumerable commands = SketchMemoryScript.m_Instance.GetOperationStack().Reverse(); - foreach (var command in SketchMemoryScript.m_Instance.GetOperationStack()) + int counter = 0; + + foreach (BaseCommand command in commands) { OnCommandPerformed(command); - } + counter++; + if (counter % batchSize == 0) // Using batch size for pacing; consider calculating payload size as an improvement + { + yield return null; + yield return new WaitForSeconds(delayBetweenBatches); + } + } } + public void StartSpeaking() { m_VoiceManager?.StartSpeaking(); From 4517258597857c1d9b119309c6f5094b1edeaad4 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Mon, 11 Nov 2024 12:51:29 +0000 Subject: [PATCH 080/174] GUIDs check and consistency between peers Add support for initializing commands with existing GUIDs Check memory script stack for guid commands before performing the command --- Assets/Scripts/Commands/BaseCommand.cs | 15 ++++++++--- Assets/Scripts/Commands/BrushStrokeCommand.cs | 11 ++++++++ .../Scripts/Multiplayer/Photon/PhotonRPC.cs | 25 ++++++++++++++++--- Assets/Scripts/SketchMemoryScript.cs | 15 +++++++++++ 4 files changed, 60 insertions(+), 6 deletions(-) diff --git a/Assets/Scripts/Commands/BaseCommand.cs b/Assets/Scripts/Commands/BaseCommand.cs index f264d0213e..623b24c756 100644 --- a/Assets/Scripts/Commands/BaseCommand.cs +++ b/Assets/Scripts/Commands/BaseCommand.cs @@ -88,9 +88,18 @@ public BaseCommand(BaseCommand parent = null) parent.m_Children.Add(this); m_Parent = parent; } -#if FUSION_WEAVER - m_Timestamp = Photon.Pun.PhotonNetwork.ServerTimestamp; -#endif + } + + // constructor that takes an existing Guid used in multiplayer to mantain consistences of commands across peers + public BaseCommand(Guid existingGuid, BaseCommand parent = null) + { + m_Guid = existingGuid; + m_Children = new List(); + if (parent != null) + { + parent.m_Children.Add(this); + m_Parent = parent; + } } /// True if this command changes the sketch in a saveable diff --git a/Assets/Scripts/Commands/BrushStrokeCommand.cs b/Assets/Scripts/Commands/BrushStrokeCommand.cs index 3cce0caa6b..0c92ae8c58 100644 --- a/Assets/Scripts/Commands/BrushStrokeCommand.cs +++ b/Assets/Scripts/Commands/BrushStrokeCommand.cs @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +using System; using UnityEngine; namespace TiltBrush @@ -35,6 +36,16 @@ public BrushStrokeCommand(Stroke stroke, StencilWidget widget = null, m_LineLength_CS = lineLength; } + // New constructor that accepts an existing Guid + public BrushStrokeCommand(Stroke stroke, Guid existingGuid, StencilWidget widget = null, + float lineLength = -1, BaseCommand parent = null) + : base(existingGuid, parent) + { + m_Stroke = stroke; + m_Widget = widget; + m_LineLength_CS = lineLength; + } + public override string Serialize() { //var data = Newtonsoft.Json.JsonConvert.SerializeObject(m_Stroke); diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs b/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs index f65d80b16c..b8e33eb62d 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs @@ -105,7 +105,6 @@ private void TryProcessCommands() InvokePreCommands(command); - SketchMemoryScript.m_Instance.PerformAndRecordCommand(command.Command, invoke: false); TryProcessCommands(); @@ -113,6 +112,7 @@ private void 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)) @@ -124,6 +124,12 @@ private static void AddPendingCommand(Action preAction, Guid commandGuid, Guid p m_pendingCommands.Add(pendingCommand); } + private static bool CheckifCommandGuidIsInStack(Guid commandGuid) { + + if (SketchMemoryScript.m_Instance.IsCommandInStack(commandGuid)) return true; + return false; + } + private static BaseCommand FindParentCommand(Guid parentGuid) { PendingCommand pendingParent = m_pendingCommands.FirstOrDefault(x => x.Guid == parentGuid); @@ -147,7 +153,7 @@ public static void CreateBrushStroke(Stroke stroke, Guid commandGuid, Guid paren var parentCommand = FindParentCommand(parentGuid); - var command = new BrushStrokeCommand(stroke, parent: parentCommand); + var command = new BrushStrokeCommand( stroke, commandGuid, parent: parentCommand); AddPendingCommand(preAction, commandGuid, parentGuid, command, childCount); } @@ -165,6 +171,7 @@ public static void RPC_SyncToSharedAnchor(NetworkRunner runner, string uuid) 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); @@ -207,6 +214,8 @@ public static void RPC_Redo(NetworkRunner runner, string commandName) [Rpc(InvokeLocal = false)] public static void RPC_BaseCommand(NetworkRunner runner, Guid commandGuid, Guid parentGuid = default, int childCount = 0) { + if (CheckifCommandGuidIsInStack(commandGuid)) return; + Debug.Log($"Base command child count: {childCount}"); var parentCommand = FindParentCommand(parentGuid); var command = new BaseCommand(parent: parentCommand); @@ -217,6 +226,9 @@ public static void RPC_BaseCommand(NetworkRunner runner, Guid commandGuid, Guid [Rpc(InvokeLocal = false)] public static void RPC_BrushStrokeFull(NetworkRunner runner, NetworkedStroke strokeData, Guid commandGuid, Guid parentGuid = default, int childCount = 0) { + + if (CheckifCommandGuidIsInStack(commandGuid)) return; + var decode = NetworkedStroke.ToStroke(strokeData); CreateBrushStroke(decode, commandGuid, parentGuid, childCount); @@ -263,7 +275,10 @@ public static void RPC_BrushStrokeContinue(NetworkRunner runner, Guid id, int of [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)) + + if (CheckifCommandGuidIsInStack(commandGuid)) return; + + if (!m_inProgressStrokes.ContainsKey(id)) { Debug.LogError("shouldn't be here!"); return; @@ -279,6 +294,8 @@ public static void RPC_BrushStrokeComplete(NetworkRunner runner, Guid id, Guid c [Rpc(InvokeLocal = false)] public static void RPC_DeleteStroke(NetworkRunner runner, int seed, Guid commandGuid, Guid parentGuid = default, int childCount = 0) { + if (CheckifCommandGuidIsInStack(commandGuid)) return; + var foundStroke = SketchMemoryScript.m_Instance.GetMemoryList.Where(x => x.m_Seed == seed).First(); if (foundStroke != null) @@ -297,6 +314,8 @@ public static void RPC_DeleteStroke(NetworkRunner runner, int seed, Guid command [Rpc(InvokeLocal = false)] public static void RPC_SwitchEnvironment(NetworkRunner runner, Guid environmentGuid, Guid commandGuid, Guid parentGuid = default, int childCount = 0) { + if (CheckifCommandGuidIsInStack(commandGuid)) return; + TiltBrush.Environment environment = EnvironmentCatalog.m_Instance.GetEnvironment(environmentGuid); if (environment != null) diff --git a/Assets/Scripts/SketchMemoryScript.cs b/Assets/Scripts/SketchMemoryScript.cs index 58809d77ae..c1f108daa4 100644 --- a/Assets/Scripts/SketchMemoryScript.cs +++ b/Assets/Scripts/SketchMemoryScript.cs @@ -1406,5 +1406,20 @@ public static List GetStrokesBetween(int start, int end) return result; } + + public bool IsCommandInStack(Guid commandGuid) + { + return IsCommandInOperationStack(commandGuid) || IsCommandInRedoStack(commandGuid); + } + + public bool IsCommandInOperationStack(Guid commandGuid) + { + return m_OperationStack.Any(command => command.Guid == commandGuid); + } + + public bool IsCommandInRedoStack(Guid commandGuid) + { + return m_RedoStack.Any(command => command.Guid == commandGuid); + } } } // namespace TiltBrush From bdcea97e0d5bdb5ccbc2f9a03d1ce688679b7a13 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Mon, 11 Nov 2024 15:40:53 +0000 Subject: [PATCH 081/174] Networking isRoomOwner To correctly define who is the room owner beyond the two users case we need to share this property, we do this by extending the PhotonPlayerRig NetworkBehaviour component --- Assets/Scripts/Multiplayer/MultiplayerDataStructs.cs | 1 + Assets/Scripts/Multiplayer/MultiplayerManager.cs | 3 ++- Assets/Scripts/Multiplayer/Photon/PhotonPlayerRig.cs | 3 +++ Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs | 5 ++++- 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Assets/Scripts/Multiplayer/MultiplayerDataStructs.cs b/Assets/Scripts/Multiplayer/MultiplayerDataStructs.cs index 52642af439..359e9241f7 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerDataStructs.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerDataStructs.cs @@ -30,6 +30,7 @@ public struct PlayerRigData public BrushData BrushData; public ExtraData ExtraData; + public bool IsRoomOwner; } diff --git a/Assets/Scripts/Multiplayer/MultiplayerManager.cs b/Assets/Scripts/Multiplayer/MultiplayerManager.cs index 09c08d811e..22cd396007 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerManager.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerManager.cs @@ -308,7 +308,8 @@ void Update() ExtraData = new ExtraData { OculusPlayerId = myOculusUserId, - } + }, + IsRoomOwner = isUserRoomOwner }; if (m_LocalPlayer != null) diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonPlayerRig.cs b/Assets/Scripts/Multiplayer/Photon/PhotonPlayerRig.cs index ca7a3bab6d..76e1d8eaee 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonPlayerRig.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonPlayerRig.cs @@ -34,6 +34,7 @@ public class PhotonPlayerRig : NetworkBehaviour, ITransientData [Networked] private float brushSize { get; set; } [Networked] private NetworkString<_64> brushGuid { get; set; } [Networked] public ulong oculusPlayerId { get; set; } + [Networked] public bool IsRoomOwner { get; set; } PointerScript transientPointer; // The offset transforms. @@ -56,6 +57,7 @@ public void TransmitData(PlayerRigData data) brushColor = data.BrushData.Color; brushSize = data.BrushData.Size; brushGuid = data.BrushData.Guid; + IsRoomOwner = data.IsRoomOwner; } public PlayerRigData RecieveData() @@ -64,6 +66,7 @@ public PlayerRigData RecieveData() { HeadPosition = m_PlayerHead.InterpolationTarget.position, HeadRotation = m_PlayerHead.InterpolationTarget.rotation, + IsRoomOwner = this.IsRoomOwner, ExtraData = new ExtraData { OculusPlayerId = this.oculusPlayerId diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs b/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs index b8e33eb62d..f5a3f51f35 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs @@ -126,7 +126,10 @@ private static void AddPendingCommand(Action preAction, Guid commandGuid, Guid p private static bool CheckifCommandGuidIsInStack(Guid commandGuid) { - if (SketchMemoryScript.m_Instance.IsCommandInStack(commandGuid)) return true; + if (SketchMemoryScript.m_Instance.IsCommandInStack(commandGuid)) { + Debug.Log($"Command with Guid {commandGuid} already in stack."); + return true; + } return false; } From ac4c2406a5806a8eaacbffda135cd296fac57d0e Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Mon, 11 Nov 2024 16:22:30 +0000 Subject: [PATCH 082/174] Room Ownership logic updated --- Assets/Editor/MultiplayerManagerEditor.cs | 4 +- .../Multiplayer/MultiplayerInterfaces.cs | 1 + .../Scripts/Multiplayer/MultiplayerManager.cs | 23 ++++++++++- .../Multiplayer/Photon/PhotonManager.cs | 40 ++++++++++++------- 4 files changed, 50 insertions(+), 18 deletions(-) diff --git a/Assets/Editor/MultiplayerManagerEditor.cs b/Assets/Editor/MultiplayerManagerEditor.cs index 2abb6ff3dc..386a0ae623 100644 --- a/Assets/Editor/MultiplayerManagerEditor.cs +++ b/Assets/Editor/MultiplayerManagerEditor.cs @@ -97,7 +97,9 @@ public override void OnInspectorGUI() { remoteUsersRegistered = "UserIds:[ "; foreach (var remotePlayer in multiplayerManager.m_RemotePlayers) - remoteUsersRegistered += remotePlayer.PlayerId.ToString()+","; + { + remoteUsersRegistered += remotePlayer.PlayerId.ToString() + ","; + } remoteUsersRegistered += "]"; } else remoteUsersRegistered = "Not Assigned"; diff --git a/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs b/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs index 3786fade1d..4c9e758748 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs @@ -35,6 +35,7 @@ public interface IDataConnectionHandler : IConnectionHandler void Update(); int GetPlayerCount(); + bool GetPlayerRoomOwnershipStatus(int playerId); Task PerformCommand(BaseCommand command); Task UndoCommand(BaseCommand command); Task RedoCommand(BaseCommand command); diff --git a/Assets/Scripts/Multiplayer/MultiplayerManager.cs b/Assets/Scripts/Multiplayer/MultiplayerManager.cs index 22cd396007..0a788983e9 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerManager.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerManager.cs @@ -377,8 +377,27 @@ void OnPlayerLeft(int id) } } - // TODO extend to more than two users case - if (m_RemotePlayers.Count == 0) isUserRoomOwner = true; // If there are no other players left, the local player becomes the room owner + // Ownership logic + // If the player that left was a room owner + if (m_Manager.GetPlayerRoomOwnershipStatus(id) == false) return; + + // If there are no other players left, the local player becomes the room owner + if (m_RemotePlayers.Count == 0) + { + isUserRoomOwner = true; + return; + } + + // There are other players left + // Determine the new room owner by the lowest PlayerId + var allPlayers = new List> { m_LocalPlayer }; + allPlayers.AddRange(m_RemotePlayers); + + // Find the player with the lowest PlayerId + var newOwner = allPlayers.OrderBy(player => player.PlayerId).First(); + + // If the new owner is the local player, set the flag + if (m_LocalPlayer.PlayerId == newOwner.PlayerId) isUserRoomOwner = true; } diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs index f5e3f28b99..8deee57914 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs @@ -100,6 +100,18 @@ public void Update() } } + public void CheckExistingUsers() + { + foreach (PlayerRef player in m_Runner.ActivePlayers) + { + if (player != m_Runner.LocalPlayer) + { + m_PlayersSpawning.Add(player); + } + } + + } + #region IConnectionHandler Methods public async Task Connect() @@ -161,20 +173,6 @@ public async Task JoinRoom(RoomCreateData roomCreateData) } - public void CheckExistingUsers() - { - int playercount = m_Runner.SessionInfo.PlayerCount; - - foreach (PlayerRef player in m_Runner.ActivePlayers) - { - if (player != m_Runner.LocalPlayer) - { - m_PlayersSpawning.Add(player); - } - } - - } - public async Task Disconnect() { State = ConnectionState.DISCONNECTING; @@ -238,6 +236,19 @@ public int GetPlayerCount() return 0; } + public bool GetPlayerRoomOwnershipStatus(int playerId) + { + // Check local player + if (m_LocalPlayer != null && m_LocalPlayer.PlayerId == playerId) return m_LocalPlayer.IsRoomOwner; + + // Check among remote players + var remotePlayer = m_PlayersSpawning + .Select(playerRef => m_Runner.GetPlayerObject(playerRef)?.GetComponent()) + .FirstOrDefault(playerRig => playerRig != null && playerRig.PlayerId == playerId); + + return remotePlayer != null && remotePlayer.IsRoomOwner; + } + public async Task PerformCommand(BaseCommand command) { await Task.Yield(); @@ -305,7 +316,6 @@ private bool ProcessCommand(BaseCommand command) return success; } - private bool CommandBrushStroke(BrushStrokeCommand command) { var stroke = command.m_Stroke; From 00033e2be4ef817020e62f56d13233b3601c72b5 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Mon, 11 Nov 2024 16:22:30 +0000 Subject: [PATCH 083/174] Room Ownership logic updated --- Assets/Editor/MultiplayerManagerEditor.cs | 4 +- .../Multiplayer/MultiplayerInterfaces.cs | 1 + .../Scripts/Multiplayer/MultiplayerManager.cs | 27 +++++++++++- .../Multiplayer/Photon/PhotonManager.cs | 42 ++++++++++++------- 4 files changed, 56 insertions(+), 18 deletions(-) diff --git a/Assets/Editor/MultiplayerManagerEditor.cs b/Assets/Editor/MultiplayerManagerEditor.cs index 2abb6ff3dc..386a0ae623 100644 --- a/Assets/Editor/MultiplayerManagerEditor.cs +++ b/Assets/Editor/MultiplayerManagerEditor.cs @@ -97,7 +97,9 @@ public override void OnInspectorGUI() { remoteUsersRegistered = "UserIds:[ "; foreach (var remotePlayer in multiplayerManager.m_RemotePlayers) - remoteUsersRegistered += remotePlayer.PlayerId.ToString()+","; + { + remoteUsersRegistered += remotePlayer.PlayerId.ToString() + ","; + } remoteUsersRegistered += "]"; } else remoteUsersRegistered = "Not Assigned"; diff --git a/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs b/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs index 3786fade1d..4c9e758748 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs @@ -35,6 +35,7 @@ public interface IDataConnectionHandler : IConnectionHandler void Update(); int GetPlayerCount(); + bool GetPlayerRoomOwnershipStatus(int playerId); Task PerformCommand(BaseCommand command); Task UndoCommand(BaseCommand command); Task RedoCommand(BaseCommand command); diff --git a/Assets/Scripts/Multiplayer/MultiplayerManager.cs b/Assets/Scripts/Multiplayer/MultiplayerManager.cs index 22cd396007..e186c48dad 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerManager.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerManager.cs @@ -377,8 +377,31 @@ void OnPlayerLeft(int id) } } - // TODO extend to more than two users case - if (m_RemotePlayers.Count == 0) isUserRoomOwner = true; // If there are no other players left, the local player becomes the room owner + // Ownership logic + // Check if any remaining player is the room owner + bool anyRoomOwner = m_RemotePlayers.Any(player => m_Manager.GetPlayerRoomOwnershipStatus(player.PlayerId)) + || (m_LocalPlayer != null && m_Manager.GetPlayerRoomOwnershipStatus(m_LocalPlayer.PlayerId)); + // If there's still a room owner, no reassignment is needed + if (anyRoomOwner) return; + Debug.LogError("No room owner left after a player left, reassigning ownership."); + + // If there are no other players left, the local player becomes the room owner + if (m_RemotePlayers.Count == 0) + { + isUserRoomOwner = true; + return; + } + + // There are other players left + // Determine the new room owner by the lowest PlayerId + var allPlayers = new List> { m_LocalPlayer }; + allPlayers.AddRange(m_RemotePlayers); + + // Find the player with the lowest PlayerId + var newOwner = allPlayers.OrderBy(player => player.PlayerId).First(); + + // If the new owner is the local player, set the flag + if (m_LocalPlayer.PlayerId == newOwner.PlayerId) isUserRoomOwner = true; } diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs index f5e3f28b99..6858ff7bf1 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs @@ -100,6 +100,18 @@ public void Update() } } + public void CheckExistingUsers() + { + foreach (PlayerRef player in m_Runner.ActivePlayers) + { + if (player != m_Runner.LocalPlayer) + { + m_PlayersSpawning.Add(player); + } + } + + } + #region IConnectionHandler Methods public async Task Connect() @@ -161,20 +173,6 @@ public async Task JoinRoom(RoomCreateData roomCreateData) } - public void CheckExistingUsers() - { - int playercount = m_Runner.SessionInfo.PlayerCount; - - foreach (PlayerRef player in m_Runner.ActivePlayers) - { - if (player != m_Runner.LocalPlayer) - { - m_PlayersSpawning.Add(player); - } - } - - } - public async Task Disconnect() { State = ConnectionState.DISCONNECTING; @@ -238,6 +236,21 @@ public int GetPlayerCount() return 0; } + public bool GetPlayerRoomOwnershipStatus(int playerId) + { + // Check local player + if (m_LocalPlayer != null && m_LocalPlayer.PlayerId == playerId) return m_LocalPlayer.IsRoomOwner; + + // Check among remote players + var remotePlayer = m_PlayersSpawning + .Select(playerRef => m_Runner.GetPlayerObject(playerRef)?.GetComponent()) + .FirstOrDefault(playerRig => playerRig != null && playerRig.PlayerId == playerId); + + Debug.LogError($"Remote Player: {remotePlayer.PlayerId} is Room Owner: {remotePlayer.IsRoomOwner}"); + + return remotePlayer != null && remotePlayer.IsRoomOwner; + } + public async Task PerformCommand(BaseCommand command) { await Task.Yield(); @@ -305,7 +318,6 @@ private bool ProcessCommand(BaseCommand command) return success; } - private bool CommandBrushStroke(BrushStrokeCommand command) { var stroke = command.m_Stroke; From 2ba33c7601dca833d6fafca5649b86ccf5142fbc Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Mon, 11 Nov 2024 21:00:13 +0000 Subject: [PATCH 084/174] Room Ownership logic updated Taking care of few edge cases and cleaning logs --- Assets/Scripts/Multiplayer/MultiplayerManager.cs | 8 ++++---- Assets/Scripts/Multiplayer/Photon/PhotonManager.cs | 10 +++------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/Assets/Scripts/Multiplayer/MultiplayerManager.cs b/Assets/Scripts/Multiplayer/MultiplayerManager.cs index e186c48dad..86d548dc55 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerManager.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerManager.cs @@ -377,13 +377,13 @@ void OnPlayerLeft(int id) } } - // Ownership logic + // Reassign Ownership if needed // Check if any remaining player is the room owner bool anyRoomOwner = m_RemotePlayers.Any(player => m_Manager.GetPlayerRoomOwnershipStatus(player.PlayerId)) - || (m_LocalPlayer != null && m_Manager.GetPlayerRoomOwnershipStatus(m_LocalPlayer.PlayerId)); + || isUserRoomOwner; + // If there's still a room owner, no reassignment is needed if (anyRoomOwner) return; - Debug.LogError("No room owner left after a player left, reassigning ownership."); // If there are no other players left, the local player becomes the room owner if (m_RemotePlayers.Count == 0) @@ -392,7 +392,7 @@ void OnPlayerLeft(int id) return; } - // There are other players left + // Since There are other players left // Determine the new room owner by the lowest PlayerId var allPlayers = new List> { m_LocalPlayer }; allPlayers.AddRange(m_RemotePlayers); diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs index 6858ff7bf1..69c6a9c9e1 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs @@ -238,17 +238,13 @@ public int GetPlayerCount() public bool GetPlayerRoomOwnershipStatus(int playerId) { - // Check local player - if (m_LocalPlayer != null && m_LocalPlayer.PlayerId == playerId) return m_LocalPlayer.IsRoomOwner; - - // Check among remote players var remotePlayer = m_PlayersSpawning .Select(playerRef => m_Runner.GetPlayerObject(playerRef)?.GetComponent()) .FirstOrDefault(playerRig => playerRig != null && playerRig.PlayerId == playerId); - Debug.LogError($"Remote Player: {remotePlayer.PlayerId} is Room Owner: {remotePlayer.IsRoomOwner}"); - - return remotePlayer != null && remotePlayer.IsRoomOwner; + if (remotePlayer != null && remotePlayer.Object != null && remotePlayer.Object.IsValid) + return remotePlayer.IsRoomOwner; + else return false; } public async Task PerformCommand(BaseCommand command) From 15e1f1a3f111a773ac2274997912607cc2152245 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Wed, 13 Nov 2024 11:19:47 +0000 Subject: [PATCH 085/174] Headset scale added to player rig --- .../Multiplayer/Photon/PhotonPlayerRig.prefab | 1 + .../Multiplayer/MultiplayerDataStructs.cs | 8 +++- .../Multiplayer/MultiplayerInterfaces.cs | 1 - .../Scripts/Multiplayer/MultiplayerManager.cs | 28 ++++++++++- .../Multiplayer/Photon/PhotonPlayerRig.cs | 46 ++++++++++++++++--- 5 files changed, 73 insertions(+), 11 deletions(-) diff --git a/Assets/Resources/Multiplayer/Photon/PhotonPlayerRig.prefab b/Assets/Resources/Multiplayer/Photon/PhotonPlayerRig.prefab index 35a6e9432e..ad58259adc 100644 --- a/Assets/Resources/Multiplayer/Photon/PhotonPlayerRig.prefab +++ b/Assets/Resources/Multiplayer/Photon/PhotonPlayerRig.prefab @@ -56,6 +56,7 @@ MonoBehaviour: m_Right: {fileID: 1621605232836574636} m_Tool: {fileID: 4282572128721117942} _oculusPlayerId: 0 + _IsRoomOwner: 0 headTransform: {fileID: 5041384770245166357} m_PlayerId: 0 --- !u!114 &2967658244113361874 diff --git a/Assets/Scripts/Multiplayer/MultiplayerDataStructs.cs b/Assets/Scripts/Multiplayer/MultiplayerDataStructs.cs index 359e9241f7..68922d0186 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerDataStructs.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerDataStructs.cs @@ -23,14 +23,20 @@ public struct PlayerRigData { public Vector3 HeadPosition; public Quaternion HeadRotation; - public Vector3 HeadScale; public Vector3 ToolPosition; public Quaternion ToolRotation; + public Vector3 LeftHandPosition; + public Quaternion LeftHandRotation; + + public Vector3 RightHandPosition; + public Quaternion RightHandRotation; + public BrushData BrushData; public ExtraData ExtraData; public bool IsRoomOwner; + public float SceneScale; } diff --git a/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs b/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs index 4c9e758748..f95b1693aa 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs @@ -33,7 +33,6 @@ public interface IDataConnectionHandler : IConnectionHandler { void Update(); - int GetPlayerCount(); bool GetPlayerRoomOwnershipStatus(int playerId); Task PerformCommand(BaseCommand command); diff --git a/Assets/Scripts/Multiplayer/MultiplayerManager.cs b/Assets/Scripts/Multiplayer/MultiplayerManager.cs index 86d548dc55..a98d47399b 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerManager.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerManager.cs @@ -292,6 +292,25 @@ void 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 headScale = App.VrSdk.GetVrCamera().transform.localScale; + var leftController = InputManager.m_Instance.GetController(InputManager.ControllerName.Brush).transform; + var rightController = InputManager.m_Instance.GetController(InputManager.ControllerName.Wand).transform; + + //if (leftController == null || rightController == null) + //{ + // Debug.LogWarning("Left or right controller is null."); + // return; + //} + + //InputDevice headDevice = InputDevices.GetDeviceAtXRNode(XRNode.Head); + //if (headDevice.isValid) + //{ + // deviceModel = headDevice.name; + // Debug.Log("Headset model: " + deviceModel); + //} + + var leftHandRelativeToScene = App.Scene.AsScene[leftController]; + var rightHandRelativeToScene = App.Scene.AsScene[rightController]; var data = new PlayerRigData { @@ -299,6 +318,11 @@ void Update() HeadRotation = headRelativeToScene.rotation, ToolPosition = pointerRelativeToScene.translation, ToolRotation = pointerRelativeToScene.rotation, + LeftHandPosition = leftHandRelativeToScene.translation, + LeftHandRotation = leftHandRelativeToScene.rotation, + RightHandPosition = rightHandRelativeToScene.translation, + RightHandRotation = rightHandRelativeToScene.rotation, + BrushData = new BrushData { Color = PointerManager.m_Instance.MainPointer.GetCurrentColor(), @@ -309,7 +333,8 @@ void Update() { OculusPlayerId = myOculusUserId, }, - IsRoomOwner = isUserRoomOwner + IsRoomOwner = isUserRoomOwner, + SceneScale = App.Scene.Pose.scale }; if (m_LocalPlayer != null) @@ -485,7 +510,6 @@ private IEnumerator SendCommandHistory() } } - public void StartSpeaking() { m_VoiceManager?.StartSpeaking(); diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonPlayerRig.cs b/Assets/Scripts/Multiplayer/Photon/PhotonPlayerRig.cs index 76e1d8eaee..6217c7afa2 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonPlayerRig.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonPlayerRig.cs @@ -17,7 +17,6 @@ using UnityEngine; using Fusion; using TiltBrush; -using System; namespace OpenBrush.Multiplayer { @@ -35,6 +34,7 @@ public class PhotonPlayerRig : NetworkBehaviour, ITransientData [Networked] private NetworkString<_64> brushGuid { get; set; } [Networked] public ulong oculusPlayerId { get; set; } [Networked] public bool IsRoomOwner { get; set; } + [Networked] public float SceneScale { get; set; } PointerScript transientPointer; // The offset transforms. @@ -53,11 +53,11 @@ public void TransmitData(PlayerRigData data) { transmitData = data; oculusPlayerId = data.ExtraData.OculusPlayerId; - brushColor = data.BrushData.Color; brushSize = data.BrushData.Size; brushGuid = data.BrushData.Guid; IsRoomOwner = data.IsRoomOwner; + SceneScale = data.SceneScale; } public PlayerRigData RecieveData() @@ -66,11 +66,22 @@ public PlayerRigData RecieveData() { HeadPosition = m_PlayerHead.InterpolationTarget.position, HeadRotation = m_PlayerHead.InterpolationTarget.rotation, + + ToolPosition = m_Tool.InterpolationTarget.position, + ToolRotation = m_Tool.InterpolationTarget.rotation, + + LeftHandPosition = m_Left.InterpolationTarget.position, + LeftHandRotation = m_Left.InterpolationTarget.rotation, + + RightHandPosition = m_Right.InterpolationTarget.position, + RightHandRotation = m_Right.InterpolationTarget.rotation, + IsRoomOwner = this.IsRoomOwner, ExtraData = new ExtraData { OculusPlayerId = this.oculusPlayerId - } + }, + SceneScale = this.SceneScale }; return data; } @@ -100,6 +111,12 @@ public override void FixedUpdateNetwork() m_Tool.transform.position = transmitData.ToolPosition; m_Tool.transform.rotation = transmitData.ToolRotation; + + m_Left.transform.position = transmitData.LeftHandPosition; + m_Left.transform.rotation = transmitData.LeftHandRotation; + + m_Right.transform.position = transmitData.RightHandPosition; + m_Right.transform.rotation = transmitData.RightHandRotation; } } @@ -109,12 +126,21 @@ public override void Render() if (Object.HasStateAuthority) { + var remoteTR = TrTransform.TR( + m_PlayerHead.InterpolationTarget.position, + m_PlayerHead.InterpolationTarget.rotation + ); + App.Scene.AsScene[headTransform] = remoteTR; } else { - var toolTR = TrTransform.TR(m_Tool.InterpolationTarget.position, m_Tool.InterpolationTarget.rotation); + // Remote pointer + var toolTR = TrTransform.TR( + m_Tool.InterpolationTarget.position, + m_Tool.InterpolationTarget.rotation + ); App.Scene.AsScene[transientPointer.transform] = toolTR; transientPointer.SetColor(brushColor); @@ -123,10 +149,16 @@ public override void Render() 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; + // Remote head + var remoteTR = TrTransform.TRS( + m_PlayerHead.InterpolationTarget.position, + m_PlayerHead.InterpolationTarget.rotation, + 1/SceneScale + ); + App.Scene.AsScene[headTransform] = remoteTR; + + } } void OnDestroy() From c3e3ce62c1264e8ba8a2f19842529e7e93cbe93f Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Wed, 13 Nov 2024 16:09:20 +0000 Subject: [PATCH 086/174] Controllers Added to the player rig --- .../Multiplayer/Photon/PhotonPlayerRig.prefab | 2302 ++++++++++++++++- .../Multiplayer/Photon/PhotonPlayerRig.cs | 39 + 2 files changed, 2259 insertions(+), 82 deletions(-) diff --git a/Assets/Resources/Multiplayer/Photon/PhotonPlayerRig.prefab b/Assets/Resources/Multiplayer/Photon/PhotonPlayerRig.prefab index ad58259adc..3a92c74328 100644 --- a/Assets/Resources/Multiplayer/Photon/PhotonPlayerRig.prefab +++ b/Assets/Resources/Multiplayer/Photon/PhotonPlayerRig.prefab @@ -57,8 +57,13 @@ MonoBehaviour: m_Tool: {fileID: 4282572128721117942} _oculusPlayerId: 0 _IsRoomOwner: 0 + _SceneScale: 0 headTransform: {fileID: 5041384770245166357} + rightHandTransform: {fileID: 7668165333132264083} + leftHandTransform: {fileID: 5887326793658353859} m_PlayerId: 0 + m_DummyLeftController: {fileID: 4048656607324710010} + m_DummyRightController: {fileID: 2292257892099998613} --- !u!114 &2967658244113361874 MonoBehaviour: m_ObjectHideFlags: 0 @@ -245,6 +250,1069 @@ Transform: m_Children: [] m_Father: {fileID: 8617970912018780043} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &2292257892099998613 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2292257892099907573} + - component: {fileID: 2292257892107399219} + m_Layer: 0 + m_Name: quest_controller_R + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &2292257892099907573 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2292257892099998613} + serializedVersion: 2 + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0.15, y: 0.15, z: 0.15} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 2292257892099907457} + - {fileID: 2292257892099907461} + - {fileID: 2292257892099907473} + - {fileID: 2292257892099907463} + - {fileID: 2292257892099907467} + - {fileID: 2292257892099907479} + - {fileID: 2292257892099907477} + - {fileID: 2292257892099907483} + - {fileID: 2292257892099907459} + - {fileID: 2292257892099907481} + - {fileID: 2292257892099907487} + - {fileID: 2292257892099907485} + m_Father: {fileID: 4501806461527917157} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!95 &2292257892107399219 +Animator: + serializedVersion: 5 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2292257892099998613} + m_Enabled: 1 + m_Avatar: {fileID: 9000000, guid: f85ad479a7d3ac54d91b088b924de5ae, type: 3} + m_Controller: {fileID: 0} + m_CullingMode: 1 + m_UpdateMode: 0 + m_ApplyRootMotion: 0 + m_LinearVelocityBlending: 0 + m_StabilizeFeet: 0 + m_WarningMessage: + m_HasTransformHierarchy: 1 + m_AllowConstantClipSamplingOptimization: 1 + m_KeepAnimatorStateOnDisable: 0 + m_WriteDefaultValuesOnDisable: 0 +--- !u!1 &2292257892099998625 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2292257892099907457} + - component: {fileID: 2292257892097007513} + - component: {fileID: 2292257892098006489} + m_Layer: 0 + m_Name: r_A + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &2292257892099907457 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2292257892099998625} + serializedVersion: 2 + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 0, z: 0} + m_LocalScale: {x: 100, y: 100, z: 100} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 2292257892099907573} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &2292257892097007513 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2292257892099998625} + m_Mesh: {fileID: 4300054, guid: f85ad479a7d3ac54d91b088b924de5ae, type: 3} +--- !u!23 &2292257892098006489 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2292257892099998625} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 0febb0642a50e644ab2b4f075fe32a9b, 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: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + 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!1 &2292257892099998627 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2292257892099907459} + - component: {fileID: 2292257892097007515} + - component: {fileID: 2292257892098006491} + m_Layer: 0 + m_Name: r_Stick + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &2292257892099907459 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2292257892099998627} + serializedVersion: 2 + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 0, z: 0} + m_LocalScale: {x: 100, y: 100, z: 100} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 2292257892099907573} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &2292257892097007515 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2292257892099998627} + m_Mesh: {fileID: 4300050, guid: f85ad479a7d3ac54d91b088b924de5ae, type: 3} +--- !u!23 &2292257892098006491 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2292257892099998627} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 0febb0642a50e644ab2b4f075fe32a9b, 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: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + 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!1 &2292257892099998629 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2292257892099907461} + - component: {fileID: 2292257892097007517} + - component: {fileID: 2292257892098006493} + m_Layer: 0 + m_Name: r_A_Highlight + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &2292257892099907461 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2292257892099998629} + serializedVersion: 2 + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 0, z: 0} + m_LocalScale: {x: 100, y: 100, z: 100} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 2292257892099907573} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &2292257892097007517 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2292257892099998629} + m_Mesh: {fileID: 4300056, guid: f85ad479a7d3ac54d91b088b924de5ae, type: 3} +--- !u!23 &2292257892098006493 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2292257892099998629} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 7b9cf867faf6d814a825b3507c7f61ac, 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: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + 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!1 &2292257892099998631 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2292257892099907463} + - component: {fileID: 2292257892097007519} + - component: {fileID: 2292257892098006495} + m_Layer: 0 + m_Name: r_B + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &2292257892099907463 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2292257892099998631} + serializedVersion: 2 + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 0, z: 0} + m_LocalScale: {x: 100, y: 100, z: 100} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 2292257892099907573} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &2292257892097007519 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2292257892099998631} + m_Mesh: {fileID: 4300052, guid: f85ad479a7d3ac54d91b088b924de5ae, type: 3} +--- !u!23 &2292257892098006495 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2292257892099998631} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 0febb0642a50e644ab2b4f075fe32a9b, 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: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + 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!1 &2292257892099998635 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2292257892099907467} + - component: {fileID: 2292257892097007491} + - component: {fileID: 2292257892098006467} + m_Layer: 0 + m_Name: r_B_Highlight + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &2292257892099907467 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2292257892099998635} + serializedVersion: 2 + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 0, z: 0} + m_LocalScale: {x: 100, y: 100, z: 100} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 2292257892099907573} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &2292257892097007491 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2292257892099998635} + m_Mesh: {fileID: 4300058, guid: f85ad479a7d3ac54d91b088b924de5ae, type: 3} +--- !u!23 &2292257892098006467 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2292257892099998635} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 7b9cf867faf6d814a825b3507c7f61ac, 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: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + 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!1 &2292257892099998641 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2292257892099907473} + - component: {fileID: 2292257892097007599} + - component: {fileID: 2292257892098006319} + m_Layer: 0 + m_Name: r_A_Slice + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &2292257892099907473 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2292257892099998641} + serializedVersion: 2 + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 0, z: 0} + m_LocalScale: {x: 100, y: 100, z: 100} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 2292257892099907573} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &2292257892097007599 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2292257892099998641} + m_Mesh: {fileID: 4300038, guid: f85ad479a7d3ac54d91b088b924de5ae, type: 3} +--- !u!23 &2292257892098006319 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2292257892099998641} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 0febb0642a50e644ab2b4f075fe32a9b, 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: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + 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!1 &2292257892099998645 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2292257892099907477} + - component: {fileID: 2292257892097007507} + - component: {fileID: 2292257892098006483} + m_Layer: 0 + m_Name: r_Controller_Body + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &2292257892099907477 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2292257892099998645} + serializedVersion: 2 + m_LocalRotation: {x: 0.000000043711392, y: 1, z: 0.000000081460335, w: -0.000000043711385} + m_LocalPosition: {x: -0, y: 0, z: 0} + m_LocalScale: {x: 100, y: 100, z: 100} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 2292257892099907573} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &2292257892097007507 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2292257892099998645} + m_Mesh: {fileID: 4300046, guid: f85ad479a7d3ac54d91b088b924de5ae, type: 3} +--- !u!23 &2292257892098006483 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2292257892099998645} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + 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: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + 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!1 &2292257892099998647 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2292257892099907479} + - component: {fileID: 2292257892097007597} + - component: {fileID: 2292257892098006317} + m_Layer: 0 + m_Name: r_B_Slice + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &2292257892099907479 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2292257892099998647} + serializedVersion: 2 + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 0, z: 0} + m_LocalScale: {x: 100, y: 100, z: 100} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 2292257892099907573} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &2292257892097007597 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2292257892099998647} + m_Mesh: {fileID: 4300040, guid: f85ad479a7d3ac54d91b088b924de5ae, type: 3} +--- !u!23 &2292257892098006317 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2292257892099998647} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 0febb0642a50e644ab2b4f075fe32a9b, 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: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + 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!1 &2292257892099998649 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2292257892099907481} + - component: {fileID: 2292257892097007511} + - component: {fileID: 2292257892098006487} + m_Layer: 0 + m_Name: r_Stick_Slice + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &2292257892099907481 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2292257892099998649} + serializedVersion: 2 + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: 0.0000000018348941, y: 0, z: 0} + m_LocalScale: {x: 100, y: 100, z: 100} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 2292257892099907573} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &2292257892097007511 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2292257892099998649} + m_Mesh: {fileID: 4300036, guid: f85ad479a7d3ac54d91b088b924de5ae, type: 3} +--- !u!23 &2292257892098006487 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2292257892099998649} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 0febb0642a50e644ab2b4f075fe32a9b, 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: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + 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!1 &2292257892099998651 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2292257892099907483} + - component: {fileID: 2292257892097007505} + - component: {fileID: 2292257892098006481} + m_Layer: 0 + m_Name: r_Grip + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &2292257892099907483 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2292257892099998651} + serializedVersion: 2 + m_LocalRotation: {x: 0.000000043711385, y: 1, z: 0.000000081460335, w: 0.000000043711392} + m_LocalPosition: {x: 0.0000000045872355, y: 0.000000014900893, z: 0.00000007824109} + m_LocalScale: {x: 100, y: 100, z: 100} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 2292257892099907573} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &2292257892097007505 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2292257892099998651} + m_Mesh: {fileID: 4300048, guid: f85ad479a7d3ac54d91b088b924de5ae, type: 3} +--- !u!23 &2292257892098006481 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2292257892099998651} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 0febb0642a50e644ab2b4f075fe32a9b, 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: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + 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!1 &2292257892099998653 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2292257892099907485} + - component: {fileID: 2292257892111503075} + m_Layer: 0 + m_Name: r_Trigger + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &2292257892099907485 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2292257892099998653} + serializedVersion: 2 + m_LocalRotation: {x: 0.000000043711392, y: 1, z: 0.000000081460335, w: -0.000000043711385} + m_LocalPosition: {x: -1.323489e-23, y: -1.8087828e-23, z: 1.110223e-16} + m_LocalScale: {x: 100, y: 100, z: 100} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 2292257892099907573} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!137 &2292257892111503075 +SkinnedMeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2292257892099998653} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 3 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + 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: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + 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 + serializedVersion: 2 + m_Quality: 0 + m_UpdateWhenOffscreen: 1 + m_SkinnedMotionVectors: 1 + m_Mesh: {fileID: 4300044, guid: f85ad479a7d3ac54d91b088b924de5ae, type: 3} + m_Bones: [] + m_BlendShapeWeights: [] + m_RootBone: {fileID: 0} + m_AABB: + m_Center: {x: 0, y: -0.022013616, z: -0.0046860487} + m_Extent: {x: 0.013755702, y: 0.012507235, z: 0.01091928} + m_DirtyAABB: 0 +--- !u!1 &2292257892099998655 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2292257892099907487} + - component: {fileID: 2292257892097007509} + - component: {fileID: 2292257892098006485} + m_Layer: 0 + m_Name: r_Transform_Visuals + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &2292257892099907487 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2292257892099998655} + serializedVersion: 2 + m_LocalRotation: {x: 0.000000043711392, y: 1, z: 0.000000081460335, w: -0.000000043711385} + m_LocalPosition: {x: -0, y: 0, z: 0} + m_LocalScale: {x: 100, y: 100, z: 100} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 2292257892099907573} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &2292257892097007509 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2292257892099998655} + m_Mesh: {fileID: 4300042, guid: f85ad479a7d3ac54d91b088b924de5ae, type: 3} +--- !u!23 &2292257892098006485 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2292257892099998655} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: e5104658cc13cef4097a511ebaf6f3aa, 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: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + 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!1 &2546328019691961597 GameObject: m_ObjectHideFlags: 0 @@ -253,62 +1321,654 @@ GameObject: m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - - component: {fileID: 1030654070696327489} - - component: {fileID: 2866855712475842972} + - component: {fileID: 1030654070696327489} + - component: {fileID: 2866855712475842972} + 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} + serializedVersion: 2 + 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_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &2866855712475842972 +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: + WordOffset: 0 + WordCount: 0 + _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: 6993845821128094923} + 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} + serializedVersion: 2 + 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_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &6993845821128094923 +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: + WordOffset: 0 + WordCount: 0 + _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} + serializedVersion: 2 + 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_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &4048656607324709984 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4048656607325141056} + - component: {fileID: 4048656607323846756} + - component: {fileID: 4048656607322716836} + m_Layer: 0 + m_Name: l_Y + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &4048656607325141056 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4048656607324709984} + serializedVersion: 2 + m_LocalRotation: {x: 0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 0, z: 0} + m_LocalScale: {x: -100, y: -100, z: -100} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 4048656607325141082} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &4048656607323846756 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4048656607324709984} + m_Mesh: {fileID: 4300018, guid: a725a200199f50f45989f92aa2e2c840, type: 3} +--- !u!23 &4048656607322716836 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4048656607324709984} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 0febb0642a50e644ab2b4f075fe32a9b, 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: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + 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!1 &4048656607324709988 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4048656607325141060} + - component: {fileID: 4048656607323846776} + - component: {fileID: 4048656607322716856} + m_Layer: 0 + m_Name: l_Stick + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &4048656607325141060 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4048656607324709988} + serializedVersion: 2 + m_LocalRotation: {x: 0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 0, z: 0} + m_LocalScale: {x: -100, y: -100, z: -100} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 4048656607325141082} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &4048656607323846776 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4048656607324709988} + m_Mesh: {fileID: 4300016, guid: a725a200199f50f45989f92aa2e2c840, type: 3} +--- !u!23 &4048656607322716856 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4048656607324709988} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 0febb0642a50e644ab2b4f075fe32a9b, 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: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + 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!1 &4048656607324709990 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4048656607325141062} + - component: {fileID: 4048656607323846778} + - component: {fileID: 4048656607322716858} + m_Layer: 0 + m_Name: l_X + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &4048656607325141062 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4048656607324709990} + serializedVersion: 2 + m_LocalRotation: {x: 0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 0, z: 0} + m_LocalScale: {x: -100, y: -100, z: -100} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 4048656607325141082} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &4048656607323846778 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4048656607324709990} + m_Mesh: {fileID: 4300014, guid: a725a200199f50f45989f92aa2e2c840, type: 3} +--- !u!23 &4048656607322716858 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4048656607324709990} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 0febb0642a50e644ab2b4f075fe32a9b, 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: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + 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!1 &4048656607324709992 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4048656607325141064} + - component: {fileID: 4048656607323846764} + - component: {fileID: 4048656607322716844} + m_Layer: 0 + m_Name: l_Y_Highlight + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &4048656607325141064 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4048656607324709992} + serializedVersion: 2 + m_LocalRotation: {x: 0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 0, z: 0} + m_LocalScale: {x: -100, y: -100, z: -100} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 4048656607325141082} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &4048656607323846764 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4048656607324709992} + m_Mesh: {fileID: 4300026, guid: a725a200199f50f45989f92aa2e2c840, type: 3} +--- !u!23 &4048656607322716844 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4048656607324709992} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 7b9cf867faf6d814a825b3507c7f61ac, 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: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + 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!1 &4048656607324709998 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4048656607325141070} + - component: {fileID: 4048656607323846754} + - component: {fileID: 4048656607322716834} + m_Layer: 0 + m_Name: l_X_Highlight + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &4048656607325141070 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4048656607324709998} + serializedVersion: 2 + m_LocalRotation: {x: 0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 0, z: 0} + m_LocalScale: {x: -100, y: -100, z: -100} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 4048656607325141082} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &4048656607323846754 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4048656607324709998} + m_Mesh: {fileID: 4300024, guid: a725a200199f50f45989f92aa2e2c840, type: 3} +--- !u!23 &4048656607322716834 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4048656607324709998} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 7b9cf867faf6d814a825b3507c7f61ac, 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: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + 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!1 &4048656607324710000 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4048656607325141072} + - component: {fileID: 4048656607323846768} + - component: {fileID: 4048656607322716848} m_Layer: 0 - m_Name: Head + m_Name: l_Stick_Slice m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!4 &1030654070696327489 +--- !u!4 &4048656607325141072 Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 2546328019691961597} + m_GameObject: {fileID: 4048656607324710000} serializedVersion: 2 - 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_LocalRotation: {x: 0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: 0.0000000018348941, y: 0, z: 0} + m_LocalScale: {x: -100, y: -100, z: -100} m_ConstrainProportionsScale: 0 - m_Children: - - {fileID: 2514485848220458462} - m_Father: {fileID: 1457871284126546349} + m_Children: [] + m_Father: {fileID: 4048656607325141082} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!114 &2866855712475842972 -MonoBehaviour: +--- !u!33 &4048656607323846768 +MeshFilter: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 2546328019691961597} + m_GameObject: {fileID: 4048656607324710000} + m_Mesh: {fileID: 4300012, guid: a725a200199f50f45989f92aa2e2c840, type: 3} +--- !u!23 &4048656607322716848 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4048656607324710000} m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 158639473, guid: e725a070cec140c4caffb81624c8c787, type: 3} - m_Name: - m_EditorClassIdentifier: - WordOffset: 0 - WordCount: 0 - _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 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 0febb0642a50e644ab2b4f075fe32a9b, 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: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + 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!1 &4048656607324710002 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -316,62 +1976,396 @@ GameObject: m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - - component: {fileID: 5887326793658353859} - - component: {fileID: 6993845821128094923} + - component: {fileID: 4048656607325141074} + - component: {fileID: 4048656607323846770} + - component: {fileID: 4048656607322716850} m_Layer: 0 - m_Name: LeftHand + m_Name: l_Transform_Visuals m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!4 &5887326793658353859 +--- !u!4 &4048656607325141074 Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 2568790808486584579} + m_GameObject: {fileID: 4048656607324710002} serializedVersion: 2 - m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalRotation: {x: -6.1500847e-15, y: -0.000000081460335, z: 1, w: 0.0000000754979} + m_LocalPosition: {x: -0, y: 0, z: 0} + m_LocalScale: {x: -100, y: -100, z: -100} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 4048656607325141082} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &4048656607323846770 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4048656607324710002} + m_Mesh: {fileID: 4300000, guid: a725a200199f50f45989f92aa2e2c840, type: 3} +--- !u!23 &4048656607322716850 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4048656607324710002} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: b52077cdc60e6c9458bea3c463972a79, 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: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + 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!1 &4048656607324710004 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4048656607325141076} + - component: {fileID: 4048656607323846772} + - component: {fileID: 4048656607322716852} + m_Layer: 0 + m_Name: l_Controller_Body + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &4048656607325141076 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4048656607324710004} + serializedVersion: 2 + m_LocalRotation: {x: -6.1500847e-15, y: -0.000000081460335, z: 1, w: 0.0000000754979} + m_LocalPosition: {x: -0, y: 0, z: 0} + m_LocalScale: {x: -100, y: -100, z: -100} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 4048656607325141082} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &4048656607323846772 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4048656607324710004} + m_Mesh: {fileID: 4300004, guid: a725a200199f50f45989f92aa2e2c840, type: 3} +--- !u!23 &4048656607322716852 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4048656607324710004} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + 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: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + 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!1 &4048656607324710006 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4048656607325141078} + - component: {fileID: 4048656607323846774} + - component: {fileID: 4048656607322716854} + m_Layer: 0 + m_Name: l_Grip + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &4048656607325141078 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4048656607324710006} + serializedVersion: 2 + m_LocalRotation: {x: -4.141339e-15, y: -0.000000081460335, z: 1, w: 0.0000000754979} + m_LocalPosition: {x: -0.0000000027523415, y: 0.000000014900893, z: 0.00000007824109} + m_LocalScale: {x: -100, y: -100, z: -100} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 4048656607325141082} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &4048656607323846774 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4048656607324710006} + m_Mesh: {fileID: 4300002, guid: a725a200199f50f45989f92aa2e2c840, type: 3} +--- !u!23 &4048656607322716854 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4048656607324710006} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 0febb0642a50e644ab2b4f075fe32a9b, 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: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + 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!1 &4048656607324710008 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4048656607325141080} + - component: {fileID: 4048656607323846782} + - component: {fileID: 4048656607322716862} + m_Layer: 0 + m_Name: l_Y_Slice + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &4048656607325141080 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4048656607324710008} + serializedVersion: 2 + m_LocalRotation: {x: 0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: 0.0000000018348941, y: 0, z: 0} + m_LocalScale: {x: -100, y: -100, z: -100} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 4048656607325141082} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &4048656607323846782 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4048656607324710008} + m_Mesh: {fileID: 4300008, guid: a725a200199f50f45989f92aa2e2c840, type: 3} +--- !u!23 &4048656607322716862 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4048656607324710008} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 0febb0642a50e644ab2b4f075fe32a9b, 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: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + 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!1 &4048656607324710010 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4048656607325141082} + - component: {fileID: 4048656607334225844} + m_Layer: 0 + m_Name: quest_controller_L + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &4048656607325141082 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4048656607324710010} + serializedVersion: 2 + 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_LocalScale: {x: 0.15, y: 0.15, z: 0.15} m_ConstrainProportionsScale: 0 m_Children: - - {fileID: 1310333546402417676} - m_Father: {fileID: 1457871284126546349} + - {fileID: 4048656607325141076} + - {fileID: 4048656607325141078} + - {fileID: 4048656607325141060} + - {fileID: 4048656607325141072} + - {fileID: 4048656607325141074} + - {fileID: 4048656607325141084} + - {fileID: 4048656607325141062} + - {fileID: 4048656607325141070} + - {fileID: 4048656607325141086} + - {fileID: 4048656607325141056} + - {fileID: 4048656607325141064} + - {fileID: 4048656607325141080} + m_Father: {fileID: 1310333546402417676} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!114 &6993845821128094923 -MonoBehaviour: +--- !u!95 &4048656607334225844 +Animator: + serializedVersion: 5 m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 2568790808486584579} + m_GameObject: {fileID: 4048656607324710010} m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 158639473, guid: e725a070cec140c4caffb81624c8c787, type: 3} - m_Name: - m_EditorClassIdentifier: - WordOffset: 0 - WordCount: 0 - _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 + m_Avatar: {fileID: 9000000, guid: a725a200199f50f45989f92aa2e2c840, type: 3} + m_Controller: {fileID: 0} + m_CullingMode: 1 + m_UpdateMode: 0 + m_ApplyRootMotion: 0 + m_LinearVelocityBlending: 0 + m_StabilizeFeet: 0 + m_WarningMessage: + m_HasTransformHierarchy: 1 + m_AllowConstantClipSamplingOptimization: 1 + m_KeepAnimatorStateOnDisable: 0 + m_WriteDefaultValuesOnDisable: 0 +--- !u!1 &4048656607324710012 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -379,29 +2373,166 @@ GameObject: m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - - component: {fileID: 3005319065403690882} + - component: {fileID: 4048656607325141084} + - component: {fileID: 4048656607338310004} m_Layer: 0 - m_Name: DummyArea + m_Name: l_Trigger m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!4 &3005319065403690882 +--- !u!4 &4048656607325141084 Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 3333659737751141766} + m_GameObject: {fileID: 4048656607324710012} serializedVersion: 2 - 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_LocalRotation: {x: -6.1500847e-15, y: -0.000000081460335, z: 1, w: 0.0000000754979} + m_LocalPosition: {x: 0.0000000018348941, y: 0, z: 0} + m_LocalScale: {x: -100, y: -100, z: -100} m_ConstrainProportionsScale: 0 m_Children: [] - m_Father: {fileID: 6539275587195953435} + m_Father: {fileID: 4048656607325141082} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!137 &4048656607338310004 +SkinnedMeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4048656607324710012} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 3 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + 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: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + 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 + serializedVersion: 2 + m_Quality: 0 + m_UpdateWhenOffscreen: 1 + m_SkinnedMotionVectors: 1 + m_Mesh: {fileID: 4300006, guid: a725a200199f50f45989f92aa2e2c840, type: 3} + m_Bones: [] + m_BlendShapeWeights: [] + m_RootBone: {fileID: 0} + m_AABB: + m_Center: {x: 0, y: -0.022013616, z: -0.0046860487} + m_Extent: {x: 0.013755702, y: 0.012507235, z: 0.01091928} + m_DirtyAABB: 0 +--- !u!1 &4048656607324710014 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4048656607325141086} + - component: {fileID: 4048656607323846780} + - component: {fileID: 4048656607322716860} + m_Layer: 0 + m_Name: l_X_Slice + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &4048656607325141086 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4048656607324710014} + serializedVersion: 2 + m_LocalRotation: {x: 0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: 0.0000000018348941, y: 0, z: 0} + m_LocalScale: {x: -100, y: -100, z: -100} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 4048656607325141082} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &4048656607323846780 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4048656607324710014} + m_Mesh: {fileID: 4300010, guid: a725a200199f50f45989f92aa2e2c840, type: 3} +--- !u!23 &4048656607322716860 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4048656607324710014} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 0febb0642a50e644ab2b4f075fe32a9b, 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: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + 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!1 &5681254972916902966 GameObject: m_ObjectHideFlags: 0 @@ -462,7 +2593,8 @@ Transform: m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 - m_Children: [] + m_Children: + - {fileID: 4048656607325141082} m_Father: {fileID: 5887326793658353859} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &6003437268994292288 @@ -493,7 +2625,8 @@ Transform: m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 - m_Children: [] + m_Children: + - {fileID: 2292257892099907573} m_Father: {fileID: 7668165333132264083} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &7803168780329886017 @@ -639,6 +2772,11 @@ PrefabInstance: propertyPath: m_Name value: HMDMesh objectReference: {fileID: 0} + - target: {fileID: 1448111865499013753, guid: 834f4ac71ec35d148acacfca28f04405, + type: 3} + propertyPath: m_IsActive + value: 1 + objectReference: {fileID: 0} - target: {fileID: 1448111865499013754, guid: 834f4ac71ec35d148acacfca28f04405, type: 3} propertyPath: m_RootOrder diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonPlayerRig.cs b/Assets/Scripts/Multiplayer/Photon/PhotonPlayerRig.cs index 6217c7afa2..ae2ec95f81 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonPlayerRig.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonPlayerRig.cs @@ -39,6 +39,9 @@ public class PhotonPlayerRig : NetworkBehaviour, ITransientData PointerScript transientPointer; // The offset transforms. [SerializeField] private Transform headTransform; + [SerializeField] private Transform rightHandTransform; + [SerializeField] private Transform leftHandTransform; + private PlayerRigData transmitData; public int m_PlayerId; @@ -49,6 +52,10 @@ public int PlayerId set { m_PlayerId = value; } } + public GameObject m_DummyLeftController; + public GameObject m_DummyRightController; + + public void TransmitData(PlayerRigData data) { transmitData = data; @@ -98,6 +105,8 @@ public override void Spawned() transientPointer.SetBrush(BrushCatalog.m_Instance.DefaultBrush); transientPointer.SetColor(App.BrushColor.CurrentColor); } + + UpdateControllerVisibility(); } public override void FixedUpdateNetwork() @@ -158,6 +167,36 @@ public override void Render() ); App.Scene.AsScene[headTransform] = remoteTR; + // Remote left hand + var remoteLeftTR = TrTransform.TRS( + m_Left.InterpolationTarget.position, + m_Left.InterpolationTarget.rotation, + 1 / SceneScale + ); + App.Scene.AsScene[leftHandTransform] = remoteLeftTR; + + // Remote right hand + var remoteRightTR = TrTransform.TRS( + m_Right.InterpolationTarget.position, + m_Right.InterpolationTarget.rotation, + 1 / SceneScale + ); + App.Scene.AsScene[rightHandTransform] = remoteRightTR; + + } + } + + public void UpdateControllerVisibility() + { + if (Object.HasStateAuthority) + { + m_DummyLeftController.SetActive(false); + m_DummyRightController.SetActive(false); + } + else + { + m_DummyLeftController.SetActive(true); + m_DummyRightController.SetActive(true); } } From 8beeb04c7e0e11d8dc19aabab61804364e9b1138 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Thu, 14 Nov 2024 10:11:13 +0000 Subject: [PATCH 087/174] Set Undo and Redo unavailable while connected --- Assets/Scripts/SketchControlsScript.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Assets/Scripts/SketchControlsScript.cs b/Assets/Scripts/SketchControlsScript.cs index da6cc58de1..fea7b67968 100644 --- a/Assets/Scripts/SketchControlsScript.cs +++ b/Assets/Scripts/SketchControlsScript.cs @@ -5001,8 +5001,10 @@ public bool IsCommandAvailable(GlobalCommands rEnum, int iParam = -1) // TODO: hide gallery view / publish if there are no saved sketches switch (rEnum) { - case GlobalCommands.Undo: return SketchMemoryScript.m_Instance.CanUndo(); - case GlobalCommands.Redo: return SketchMemoryScript.m_Instance.CanRedo(); + case GlobalCommands.Undo: + return SketchMemoryScript.m_Instance.CanUndo() && !(MultiplayerManager.m_Instance.State == ConnectionState.IN_ROOM); + case GlobalCommands.Redo: + return SketchMemoryScript.m_Instance.CanRedo() && !(MultiplayerManager.m_Instance.State == ConnectionState.IN_ROOM); case GlobalCommands.Save: bool canSave = SaveLoadScript.m_Instance.SceneFile.Valid && From 72936ea33bc54f419cb48d4ff083b038b32062f1 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Thu, 14 Nov 2024 10:39:46 +0000 Subject: [PATCH 088/174] Updating Connect and Disconnect Logic in the UI Removing the Connect and Disconnect buttons and extending the MultiplayerPanel.cs to connect when enabled. If the user is not in the room, it will disconnect when disabled. --- Assets/Prefabs/Panels/MultiplayerPanel.prefab | 4 ++-- Assets/Scripts/GUI/MultiplayerPanel.cs | 19 +++++++++++++++++-- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/Assets/Prefabs/Panels/MultiplayerPanel.prefab b/Assets/Prefabs/Panels/MultiplayerPanel.prefab index b029c16d79..eb77065a6c 100644 --- a/Assets/Prefabs/Panels/MultiplayerPanel.prefab +++ b/Assets/Prefabs/Panels/MultiplayerPanel.prefab @@ -965,7 +965,7 @@ GameObject: m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 - m_IsActive: 1 + m_IsActive: 0 --- !u!4 &682210897750110130 Transform: m_ObjectHideFlags: 0 @@ -3137,7 +3137,7 @@ GameObject: m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 - m_IsActive: 1 + m_IsActive: 0 --- !u!4 &3858004711376697373 Transform: m_ObjectHideFlags: 0 diff --git a/Assets/Scripts/GUI/MultiplayerPanel.cs b/Assets/Scripts/GUI/MultiplayerPanel.cs index 19c7622eb0..94f1d69181 100644 --- a/Assets/Scripts/GUI/MultiplayerPanel.cs +++ b/Assets/Scripts/GUI/MultiplayerPanel.cs @@ -87,7 +87,23 @@ public void Awake() protected override void OnEnablePanel() { base.OnEnablePanel(); - UpdateDisplay(); + + if (MultiplayerManager.m_Instance == null) return; + if (MultiplayerManager.m_Instance.State == ConnectionState.INITIALIZED || MultiplayerManager.m_Instance.State == ConnectionState.DISCONNECTED) + { + MultiplayerManager.m_Instance.Connect(); + } + } + + protected override void OnDisablePanel() + { + base.OnDisablePanel(); + + if (MultiplayerManager.m_Instance == null) return; + if (MultiplayerManager.m_Instance.State != ConnectionState.IN_ROOM) + { + MultiplayerManager.m_Instance.Disconnect(); + } } private static string GenerateUniqueRoomName() @@ -163,7 +179,6 @@ private Tuple CheckAdvancedModeActive() return Tuple.Create(false, ""); } - private Tuple CheckMultiplayerManagerErrors() { From b38b663b142c4a4c82864740f9d9fa073e0bc1bb Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Thu, 14 Nov 2024 11:03:41 +0000 Subject: [PATCH 089/174] Display Room Ownership on the UI Added a RoomOwnershipUpdated action to MultiplayerManager.cs to send updates about room ownership to the multiplayer UI panel. Updated the MultiplayerPanel script and prefab to include a Room Ownership field. --- Assets/Editor/MultiplayerManagerEditor.cs | 4 +- Assets/Prefabs/Panels/MultiplayerPanel.prefab | 191 +++++++++++++++++- Assets/Scripts/GUI/MultiplayerPanel.cs | 15 +- .../Scripts/Multiplayer/MultiplayerManager.cs | 17 +- Assets/Scripts/SketchControlsScript.cs | 4 +- 5 files changed, 216 insertions(+), 15 deletions(-) diff --git a/Assets/Editor/MultiplayerManagerEditor.cs b/Assets/Editor/MultiplayerManagerEditor.cs index 386a0ae623..caa84ece8f 100644 --- a/Assets/Editor/MultiplayerManagerEditor.cs +++ b/Assets/Editor/MultiplayerManagerEditor.cs @@ -82,8 +82,8 @@ public override void OnInspectorGUI() //Room Ownership string ownership = ""; - if (multiplayerManager != null && multiplayerManager.isUserRoomOwner) ownership = "Yes"; - else if (multiplayerManager != null && !multiplayerManager.isUserRoomOwner) ownership = "No"; + if (multiplayerManager != null && multiplayerManager.IsUserRoomOwner()) ownership = "Yes"; + else if (multiplayerManager != null && !multiplayerManager.IsUserRoomOwner()) ownership = "No"; else ownership = "Not Assigned"; EditorGUILayout.BeginHorizontal(); diff --git a/Assets/Prefabs/Panels/MultiplayerPanel.prefab b/Assets/Prefabs/Panels/MultiplayerPanel.prefab index eb77065a6c..13272392a9 100644 --- a/Assets/Prefabs/Panels/MultiplayerPanel.prefab +++ b/Assets/Prefabs/Panels/MultiplayerPanel.prefab @@ -115,6 +115,7 @@ MonoBehaviour: m_RoomNumber: {fileID: 9160963822650286723} m_Nickname: {fileID: 4888819335275065630} m_AlertsErrors: {fileID: 6437857310660163665} + m_RoomOwnership: {fileID: 0} references: version: 2 RefIds: [] @@ -975,7 +976,7 @@ Transform: m_GameObject: {fileID: 1032351299171828485} serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: -0.617, y: -0.323, z: -0.042} + m_LocalPosition: {x: -0.617, y: -0.484, z: -0.042} m_LocalScale: {x: 0.33, y: 0.33, z: 0.33} m_ConstrainProportionsScale: 0 m_Children: [] @@ -1165,7 +1166,7 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: 0.147, y: 0.035999954} + m_AnchoredPosition: {x: 0.147, y: 0.14799947} m_SizeDelta: {x: 1.104868, y: 1.4460607} m_Pivot: {x: 0.5, y: 0.5} --- !u!23 &3100622910448847287 @@ -2810,7 +2811,7 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: 0.466, y: -0.26} + m_AnchoredPosition: {x: 0.466, y: -0.14800048} m_SizeDelta: {x: 1.104868, y: 1.4460607} m_Pivot: {x: 0.5, y: 0.5} --- !u!23 &8677733851088090031 @@ -2981,7 +2982,7 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: 0.466, y: -0.5540001} + m_AnchoredPosition: {x: 0.466, y: -0.44200057} m_SizeDelta: {x: 1.104868, y: 1.4460607} m_Pivot: {x: 0.5, y: 0.5} --- !u!23 &2426349157769062346 @@ -3147,7 +3148,7 @@ Transform: m_GameObject: {fileID: 5899900900417153719} serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: 0.62, y: -0.323, z: -0.042} + m_LocalPosition: {x: 0.62, y: -0.48400003, z: -0.042} m_LocalScale: {x: 0.33, y: 0.33, z: 0.33} m_ConstrainProportionsScale: 0 m_Children: [] @@ -3527,6 +3528,7 @@ Transform: - {fileID: 4609499178869085959} - {fileID: 2677652558882906679} - {fileID: 8994233172856634412} + - {fileID: 3004064693637687833} - {fileID: 5239577752120043582} m_Father: {fileID: 415082} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} @@ -3765,7 +3767,7 @@ Transform: m_GameObject: {fileID: 6530187638887373145} serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: 0.208, y: -0.323, z: -0.042} + m_LocalPosition: {x: 0.208, y: -0.48400003, z: -0.042} m_LocalScale: {x: 0.33, y: 0.33, z: 0.33} m_ConstrainProportionsScale: 0 m_Children: [] @@ -3921,6 +3923,177 @@ MonoBehaviour: references: version: 2 RefIds: [] +--- !u!1 &6603807884407399516 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3004064693637687833} + - component: {fileID: 4811598343171871539} + - component: {fileID: 1415528830258304631} + m_Layer: 16 + m_Name: Room Ownership Status + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &3004064693637687833 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6603807884407399516} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: -0.05} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 8346410928580794432} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0.147, y: -0.73} + m_SizeDelta: {x: 1.104868, y: 1.4460607} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!23 &4811598343171871539 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6603807884407399516} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, 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: 0 + 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!114 &1415528830258304631 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6603807884407399516} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9541d86e2fd84c1d9990edf0852d74ab, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_text: Room Owner + m_isRightToLeft: 0 + m_fontAsset: {fileID: 11400000, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + m_sharedMaterial: {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + m_fontSharedMaterials: [] + m_fontMaterial: {fileID: 0} + m_fontMaterials: [] + m_fontColor32: + serializedVersion: 2 + rgba: 4294967295 + m_fontColor: {r: 1, g: 1, b: 1, a: 1} + m_enableVertexGradient: 0 + m_colorMode: 3 + m_fontColorGradient: + topLeft: {r: 1, g: 1, b: 1, a: 1} + topRight: {r: 1, g: 1, b: 1, a: 1} + bottomLeft: {r: 1, g: 1, b: 1, a: 1} + bottomRight: {r: 1, g: 1, b: 1, a: 1} + m_fontColorGradientPreset: {fileID: 0} + m_spriteAsset: {fileID: 0} + m_tintAllSprites: 0 + m_StyleSheet: {fileID: 0} + m_TextStyleHashCode: -1183493901 + m_overrideHtmlColors: 0 + m_faceColor: + serializedVersion: 2 + rgba: 4294967295 + m_fontSize: 1.5 + m_fontSizeBase: 1.5 + m_fontWeight: 400 + m_enableAutoSizing: 0 + m_fontSizeMin: 18 + m_fontSizeMax: 72 + m_fontStyle: 0 + m_HorizontalAlignment: 1 + m_VerticalAlignment: 512 + m_textAlignment: 65535 + m_characterSpacing: 0 + m_wordSpacing: 0 + m_lineSpacing: -30 + m_lineSpacingMax: 0 + m_paragraphSpacing: 0 + m_charWidthMaxAdj: 0 + m_enableWordWrapping: 1 + m_wordWrappingRatios: 0.4 + m_overflowMode: 0 + m_linkedTextComponent: {fileID: 0} + parentLinkedComponent: {fileID: 0} + m_enableKerning: 1 + m_enableExtraPadding: 0 + checkPaddingRequired: 0 + m_isRichText: 1 + m_parseCtrlCharacters: 1 + m_isOrthographic: 0 + m_isCullingEnabled: 0 + m_horizontalMapping: 0 + m_verticalMapping: 0 + m_uvLineOffset: 0 + m_geometrySortingOrder: 0 + m_IsTextObjectScaleStatic: 0 + m_VertexBufferAutoSizeReduction: 0 + m_useMaxVisibleDescender: 1 + m_pageToDisplay: 1 + m_margin: {x: -0.31141979, y: -0.12023544, z: -0.1872099, w: 1.1764753} + m_isUsingLegacyAnimationComponent: 0 + m_isVolumetricText: 0 + _SortingLayer: 0 + _SortingLayerID: 0 + _SortingOrder: 0 + m_hasFontAssetChanged: 0 + m_renderer: {fileID: 4811598343171871539} + m_maskType: 0 --- !u!1 &7101128147202961155 GameObject: m_ObjectHideFlags: 0 @@ -3950,7 +4123,7 @@ Transform: m_GameObject: {fileID: 7101128147202961155} serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: -0.206, y: -0.323, z: -0.042} + m_LocalPosition: {x: -0.206, y: -0.48400003, z: -0.042} m_LocalScale: {x: 0.33, y: 0.33, z: 0.33} m_ConstrainProportionsScale: 0 m_Children: [] @@ -4135,7 +4308,7 @@ Transform: m_GameObject: {fileID: 7355810358076377629} serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: -0.619, y: 0.09, z: -0.042} + m_LocalPosition: {x: -0.619, y: 0.20199952, z: -0.042} m_LocalScale: {x: 0.15, y: 0.15, z: 0.15} m_ConstrainProportionsScale: 0 m_Children: [] @@ -4506,7 +4679,7 @@ Transform: m_GameObject: {fileID: 7521510091458286424} serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: -0.619, y: 0.384, z: -0.042} + m_LocalPosition: {x: -0.619, y: 0.496, z: -0.042} m_LocalScale: {x: 0.15, y: 0.15, z: 0.15} m_ConstrainProportionsScale: 0 m_Children: [] diff --git a/Assets/Scripts/GUI/MultiplayerPanel.cs b/Assets/Scripts/GUI/MultiplayerPanel.cs index 94f1d69181..2adc09f3c5 100644 --- a/Assets/Scripts/GUI/MultiplayerPanel.cs +++ b/Assets/Scripts/GUI/MultiplayerPanel.cs @@ -27,6 +27,7 @@ public class MultiplayerPanel : BasePanel [SerializeField] private TextMeshPro m_RoomNumber; [SerializeField] private TextMeshPro m_Nickname; [SerializeField] private TextMeshPro m_AlertsErrors; + [SerializeField] private TextMeshPro m_RoomOwnership; public string RoomName { @@ -80,7 +81,11 @@ public void Awake() CheckIfRoomExist, }; - if (MultiplayerManager.m_Instance != null) MultiplayerManager.m_Instance.StateUpdated += OnStateUpdated; + if (MultiplayerManager.m_Instance != null) + { + MultiplayerManager.m_Instance.StateUpdated += OnStateUpdated; + MultiplayerManager.m_Instance.RoomOwnershipUpdated += OnRoomOwnershipUpdated; + } } @@ -165,10 +170,18 @@ private async void Disconnect() private void OnStateUpdated(ConnectionState newState) { + if (!m_State) return; m_State.text = "State: " + newState.ToString(); UpdateDisplay(); } + private void OnRoomOwnershipUpdated(bool isRoomOwner) + { + if (!m_RoomOwnership) return; + m_RoomOwnership.text = isRoomOwner ? "Room Owner" : "Not Room Owner"; + UpdateDisplay(); + } + private Tuple CheckAdvancedModeActive() { if (PanelManager.m_Instance != null) diff --git a/Assets/Scripts/Multiplayer/MultiplayerManager.cs b/Assets/Scripts/Multiplayer/MultiplayerManager.cs index a98d47399b..78d7dc9da6 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerManager.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerManager.cs @@ -54,6 +54,7 @@ public class MultiplayerManager : MonoBehaviour public Action playerLeft; public Action> roomDataRefreshed; public event Action StateUpdated; + public event Action RoomOwnershipUpdated; public event Action UserInfoStateUpdated; private List m_RoomData = new List(); @@ -94,7 +95,16 @@ public ConnectionUserInfo UserInfo [HideInInspector] public RoomCreateData data; - [HideInInspector] public bool isUserRoomOwner = false; //temporary public + private bool _isUserRoomOwner = false; + private bool isUserRoomOwner + { + get => _isUserRoomOwner; + set + { + _isUserRoomOwner = value; + RoomOwnershipUpdated?.Invoke(value); + } + } void Awake() { @@ -540,5 +550,10 @@ public bool CanLeaveRoom() { return State == ConnectionState.IN_ROOM; } + + public bool IsUserRoomOwner() + { + return isUserRoomOwner; + } } } diff --git a/Assets/Scripts/SketchControlsScript.cs b/Assets/Scripts/SketchControlsScript.cs index fea7b67968..3c9d47ab71 100644 --- a/Assets/Scripts/SketchControlsScript.cs +++ b/Assets/Scripts/SketchControlsScript.cs @@ -5001,9 +5001,9 @@ public bool IsCommandAvailable(GlobalCommands rEnum, int iParam = -1) // TODO: hide gallery view / publish if there are no saved sketches switch (rEnum) { - case GlobalCommands.Undo: + case GlobalCommands.Undo: return SketchMemoryScript.m_Instance.CanUndo() && !(MultiplayerManager.m_Instance.State == ConnectionState.IN_ROOM); - case GlobalCommands.Redo: + case GlobalCommands.Redo: return SketchMemoryScript.m_Instance.CanRedo() && !(MultiplayerManager.m_Instance.State == ConnectionState.IN_ROOM); case GlobalCommands.Save: bool canSave = From d1fc5cda5ba0beb2c87abd6705aa25fedc572900 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Thu, 14 Nov 2024 12:29:55 +0000 Subject: [PATCH 090/174] Fix display of room Ownership --- Assets/Prefabs/Panels/MultiplayerPanel.prefab | 2 +- Assets/Scripts/GUI/MultiplayerPanel.cs | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Assets/Prefabs/Panels/MultiplayerPanel.prefab b/Assets/Prefabs/Panels/MultiplayerPanel.prefab index 13272392a9..c265787c58 100644 --- a/Assets/Prefabs/Panels/MultiplayerPanel.prefab +++ b/Assets/Prefabs/Panels/MultiplayerPanel.prefab @@ -115,7 +115,7 @@ MonoBehaviour: m_RoomNumber: {fileID: 9160963822650286723} m_Nickname: {fileID: 4888819335275065630} m_AlertsErrors: {fileID: 6437857310660163665} - m_RoomOwnership: {fileID: 0} + m_RoomOwnership: {fileID: 1415528830258304631} references: version: 2 RefIds: [] diff --git a/Assets/Scripts/GUI/MultiplayerPanel.cs b/Assets/Scripts/GUI/MultiplayerPanel.cs index 2adc09f3c5..a2734ea151 100644 --- a/Assets/Scripts/GUI/MultiplayerPanel.cs +++ b/Assets/Scripts/GUI/MultiplayerPanel.cs @@ -178,8 +178,7 @@ private void OnStateUpdated(ConnectionState newState) private void OnRoomOwnershipUpdated(bool isRoomOwner) { if (!m_RoomOwnership) return; - m_RoomOwnership.text = isRoomOwner ? "Room Owner" : "Not Room Owner"; - UpdateDisplay(); + m_RoomOwnership.text = isRoomOwner ? "You are Room Owner" : "You are Not Room Owner"; } private Tuple CheckAdvancedModeActive() From 3b4ba55519c6bd3116a3fe997841df5bd70bcd9f Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Thu, 14 Nov 2024 12:39:35 +0000 Subject: [PATCH 091/174] Fix null reference issues in PhotonPlayerRig Fix null reference issues in PhotonPlayerRig by adding checks for destroyed objects in RecieveData and other methods. --- .../Multiplayer/Photon/PhotonPlayerRig.cs | 48 ++++++++++++------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonPlayerRig.cs b/Assets/Scripts/Multiplayer/Photon/PhotonPlayerRig.cs index ae2ec95f81..ba11b4a6b3 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonPlayerRig.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonPlayerRig.cs @@ -69,30 +69,40 @@ public void TransmitData(PlayerRigData data) public PlayerRigData RecieveData() { - var data = new PlayerRigData + var data = new PlayerRigData(); + + if (m_PlayerHead?.InterpolationTarget != null) + { + data.HeadPosition = m_PlayerHead.InterpolationTarget.position; + data.HeadRotation = m_PlayerHead.InterpolationTarget.rotation; + } + + if (m_Tool?.InterpolationTarget != null) { - HeadPosition = m_PlayerHead.InterpolationTarget.position, - HeadRotation = m_PlayerHead.InterpolationTarget.rotation, + data.ToolPosition = m_Tool.InterpolationTarget.position; + data.ToolRotation = m_Tool.InterpolationTarget.rotation; + } - ToolPosition = m_Tool.InterpolationTarget.position, - ToolRotation = m_Tool.InterpolationTarget.rotation, + if (m_Left?.InterpolationTarget != null) + { + data.LeftHandPosition = m_Left.InterpolationTarget.position; + data.LeftHandRotation = m_Left.InterpolationTarget.rotation; + } - LeftHandPosition = m_Left.InterpolationTarget.position, - LeftHandRotation = m_Left.InterpolationTarget.rotation, + if (m_Right?.InterpolationTarget != null) + { + data.RightHandPosition = m_Right.InterpolationTarget.position; + data.RightHandRotation = m_Right.InterpolationTarget.rotation; + } - RightHandPosition = m_Right.InterpolationTarget.position, - RightHandRotation = m_Right.InterpolationTarget.rotation, + data.IsRoomOwner = this.IsRoomOwner; + data.ExtraData = new ExtraData { OculusPlayerId = this.oculusPlayerId }; + data.SceneScale = this.SceneScale; - IsRoomOwner = this.IsRoomOwner, - ExtraData = new ExtraData - { - OculusPlayerId = this.oculusPlayerId - }, - SceneScale = this.SceneScale - }; return data; } + public override void Spawned() { base.Spawned(); @@ -205,7 +215,13 @@ void OnDestroy() if (transientPointer != null) { PointerManager.m_Instance.RemoveRemotePointer(transientPointer); + transientPointer = null; } + + m_PlayerHead = null; + m_Tool = null; + m_Left = null; + m_Right = null; } } } From 5454676661ff549bea0aab560e5fab38b1752401 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Thu, 14 Nov 2024 15:41:32 +0000 Subject: [PATCH 092/174] Fix invalid localScale assignment due to extreme SceneScale values Clamped SceneScale to prevent division by zero or extremely small values. --- Assets/Scripts/Multiplayer/Photon/PhotonPlayerRig.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonPlayerRig.cs b/Assets/Scripts/Multiplayer/Photon/PhotonPlayerRig.cs index ba11b4a6b3..9b7c519548 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonPlayerRig.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonPlayerRig.cs @@ -169,11 +169,15 @@ public override void Render() } transientPointer.BrushSize01 = brushSize; + // Calculate the scale based on the scene scale + float clampedSceneScale = Mathf.Clamp(SceneScale, 0.01f, float.MaxValue); + float Scale = 1 / clampedSceneScale; + // Remote head var remoteTR = TrTransform.TRS( m_PlayerHead.InterpolationTarget.position, m_PlayerHead.InterpolationTarget.rotation, - 1/SceneScale + Scale ); App.Scene.AsScene[headTransform] = remoteTR; @@ -181,7 +185,7 @@ public override void Render() var remoteLeftTR = TrTransform.TRS( m_Left.InterpolationTarget.position, m_Left.InterpolationTarget.rotation, - 1 / SceneScale + Scale ); App.Scene.AsScene[leftHandTransform] = remoteLeftTR; @@ -189,7 +193,7 @@ public override void Render() var remoteRightTR = TrTransform.TRS( m_Right.InterpolationTarget.position, m_Right.InterpolationTarget.rotation, - 1 / SceneScale + Scale ); App.Scene.AsScene[rightHandTransform] = remoteRightTR; From 548057e16bfef57ccea68ba0ea27e79b347b8059 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Fri, 15 Nov 2024 12:40:27 +0000 Subject: [PATCH 093/174] Updating UI command descriptions for Multiplayer UI Updating the languages translations for the Multiplayer UI --- .../Localization/Strings/Strings_de.asset | 16 ++++++++++++++++ .../Localization/Strings/Strings_en.asset | 2 +- .../Localization/Strings/Strings_es.asset | 16 ++++++++++++++++ .../Localization/Strings/Strings_fr.asset | 16 ++++++++++++++++ .../Localization/Strings/Strings_ja.asset | 16 ++++++++++++++++ .../Localization/Strings/Strings_ko.asset | 16 ++++++++++++++++ .../Localization/Strings/Strings_zh.asset | 16 ++++++++++++++++ 7 files changed, 97 insertions(+), 1 deletion(-) diff --git a/Assets/Settings/Localization/Strings/Strings_de.asset b/Assets/Settings/Localization/Strings/Strings_de.asset index e0259b2546..9bd616495d 100644 --- a/Assets/Settings/Localization/Strings/Strings_de.asset +++ b/Assets/Settings/Localization/Strings/Strings_de.asset @@ -3576,6 +3576,22 @@ MonoBehaviour: m_Localized: Mehr Informationen zur Verwendung von Open Brush ohne VR m_Metadata: m_Items: [] + - m_Id: 294636769901092864 + m_Localized: Raum beitreten + m_Metadata: + m_Items: [] + - m_Id: 294636908816441344 + m_Localized: Raum verlassen + m_Metadata: + m_Items: [] + - m_Id: 294637073212186624 + m_Localized: Raumnamen bearbeiten + m_Metadata: + m_Items: [] + - m_Id: 294637384320491520 + m_Localized: Spitznamen bearbeiten + m_Metadata: + m_Items: [] references: version: 2 RefIds: [] diff --git a/Assets/Settings/Localization/Strings/Strings_en.asset b/Assets/Settings/Localization/Strings/Strings_en.asset index 028b35db1d..20d77a0ee6 100644 --- a/Assets/Settings/Localization/Strings/Strings_en.asset +++ b/Assets/Settings/Localization/Strings/Strings_en.asset @@ -3557,7 +3557,7 @@ MonoBehaviour: m_Metadata: m_Items: [] - m_Id: 294637073212186624 - m_Localized: Edite Room Name + m_Localized: Edit Room Name m_Metadata: m_Items: [] - m_Id: 294637384320491520 diff --git a/Assets/Settings/Localization/Strings/Strings_es.asset b/Assets/Settings/Localization/Strings/Strings_es.asset index 0aa94db064..16e3da491c 100644 --- a/Assets/Settings/Localization/Strings/Strings_es.asset +++ b/Assets/Settings/Localization/Strings/Strings_es.asset @@ -3548,6 +3548,22 @@ MonoBehaviour: m_Localized: "M\xE1s informaci\xF3n sobre c\xF3mo usar Open Brush sin VR" m_Metadata: m_Items: [] + - m_Id: 294636769901092864 + m_Localized: Unirse a la Sala + m_Metadata: + m_Items: [] + - m_Id: 294636908816441344 + m_Localized: Salir de la Sala + m_Metadata: + m_Items: [] + - m_Id: 294637073212186624 + m_Localized: Editar Nombre de la Sala + m_Metadata: + m_Items: [] + - m_Id: 294637384320491520 + m_Localized: Editar Apodo + m_Metadata: + m_Items: [] references: version: 2 RefIds: [] diff --git a/Assets/Settings/Localization/Strings/Strings_fr.asset b/Assets/Settings/Localization/Strings/Strings_fr.asset index c0c7d234f0..2c69d699a7 100644 --- a/Assets/Settings/Localization/Strings/Strings_fr.asset +++ b/Assets/Settings/Localization/Strings/Strings_fr.asset @@ -3517,6 +3517,22 @@ MonoBehaviour: m_Localized: Plus d'informations sur l'utilisation d'Open Brush sans VR m_Metadata: m_Items: [] + - m_Id: 294636769901092864 + m_Localized: Rejoindre la Salle + m_Metadata: + m_Items: [] + - m_Id: 294636908816441344 + m_Localized: Quitter la Salle + m_Metadata: + m_Items: [] + - m_Id: 294637073212186624 + m_Localized: Modifier le Nom de la Salle + m_Metadata: + m_Items: [] + - m_Id: 294637384320491520 + m_Localized: Modifier le Pseudo + m_Metadata: + m_Items: [] references: version: 2 RefIds: [] diff --git a/Assets/Settings/Localization/Strings/Strings_ja.asset b/Assets/Settings/Localization/Strings/Strings_ja.asset index 4175e9eeb7..1fdc75cd80 100644 --- a/Assets/Settings/Localization/Strings/Strings_ja.asset +++ b/Assets/Settings/Localization/Strings/Strings_ja.asset @@ -3470,6 +3470,22 @@ MonoBehaviour: m_Localized: "VR\u306A\u3057\u3067Open Brush\u3092\u4F7F\u7528\u3059\u308B\u65B9\u6CD5\u306B\u3064\u3044\u3066\u306E\u8A73\u7D30" m_Metadata: m_Items: [] + - m_Id: 294636769901092864 + m_Localized: "\u90E8\u5C4B\u306B\u53C2\u52A0" + m_Metadata: + m_Items: [] + - m_Id: 294636908816441344 + m_Localized: "\u90E8\u5C4B\u3092\u9000\u51FA" + m_Metadata: + m_Items: [] + - m_Id: 294637073212186624 + m_Localized: "\u90E8\u5C4B\u306E\u540D\u524D\u3092\u7DE8\u96C6" + m_Metadata: + m_Items: [] + - m_Id: 294637384320491520 + m_Localized: "\u30CB\u30C3\u30AF\u30CD\u30FC\u30E0\u3092\u7DE8\u96C6" + m_Metadata: + m_Items: [] references: version: 2 RefIds: [] diff --git a/Assets/Settings/Localization/Strings/Strings_ko.asset b/Assets/Settings/Localization/Strings/Strings_ko.asset index 1fdf5c1bfe..d3e172626c 100644 --- a/Assets/Settings/Localization/Strings/Strings_ko.asset +++ b/Assets/Settings/Localization/Strings/Strings_ko.asset @@ -3536,6 +3536,22 @@ MonoBehaviour: \uC815\uBCF4" m_Metadata: m_Items: [] + - m_Id: 294636769901092864 + m_Localized: "\uBC29 \uCC38\uC5EC" + m_Metadata: + m_Items: [] + - m_Id: 294636908816441344 + m_Localized: "\uBC29 \uB098\uAC00\uAE30" + m_Metadata: + m_Items: [] + - m_Id: 294637073212186624 + m_Localized: "\uBC29 \uC774\uB984 \uD3B8\uC9D1" + m_Metadata: + m_Items: [] + - m_Id: 294637384320491520 + m_Localized: "\uB2C9\uB124\uC784 \uD3B8\uC9D1" + m_Metadata: + m_Items: [] references: version: 2 RefIds: [] diff --git a/Assets/Settings/Localization/Strings/Strings_zh.asset b/Assets/Settings/Localization/Strings/Strings_zh.asset index 801374490a..cc3b09d528 100644 --- a/Assets/Settings/Localization/Strings/Strings_zh.asset +++ b/Assets/Settings/Localization/Strings/Strings_zh.asset @@ -3466,6 +3466,22 @@ MonoBehaviour: m_Localized: "\u5173\u4E8E\u4E0D\u4F7F\u7528 VR \u4F7F\u7528 Open Brush \u7684\u66F4\u591A\u4FE1\u606F" m_Metadata: m_Items: [] + - m_Id: 294636769901092864 + m_Localized: "\u52A0\u5165\u623F\u95F4" + m_Metadata: + m_Items: [] + - m_Id: 294636908816441344 + m_Localized: "\u79BB\u5F00\u623F\u95F4" + m_Metadata: + m_Items: [] + - m_Id: 294637073212186624 + m_Localized: "\u7F16\u8F91\u623F\u95F4\u540D\u79F0" + m_Metadata: + m_Items: [] + - m_Id: 294637384320491520 + m_Localized: "\u7F16\u8F91\u6635\u79F0" + m_Metadata: + m_Items: [] references: version: 2 RefIds: [] From 1d51791b1770ce87b5a1534d9cf939312ec754c7 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Fri, 15 Nov 2024 13:45:08 +0000 Subject: [PATCH 094/174] Resolve Controller Flickering Updated the PhotonPlayerRig prefab to properly manage dummy transforms and model transforms for left/right controllers. --- .../Multiplayer/Photon/PhotonPlayerRig.prefab | 84 ++++++++++++++++--- .../Multiplayer/Photon/PhotonPlayerRig.cs | 12 +-- 2 files changed, 80 insertions(+), 16 deletions(-) diff --git a/Assets/Resources/Multiplayer/Photon/PhotonPlayerRig.prefab b/Assets/Resources/Multiplayer/Photon/PhotonPlayerRig.prefab index 3a92c74328..d220c3de20 100644 --- a/Assets/Resources/Multiplayer/Photon/PhotonPlayerRig.prefab +++ b/Assets/Resources/Multiplayer/Photon/PhotonPlayerRig.prefab @@ -32,6 +32,8 @@ Transform: m_ConstrainProportionsScale: 0 m_Children: - {fileID: 5041384770245166357} + - {fileID: 2164438665615948153} + - {fileID: 5267290339349674983} - {fileID: 1457871284126546349} m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} @@ -59,11 +61,11 @@ MonoBehaviour: _IsRoomOwner: 0 _SceneScale: 0 headTransform: {fileID: 5041384770245166357} - rightHandTransform: {fileID: 7668165333132264083} - leftHandTransform: {fileID: 5887326793658353859} + rightHandTransform: {fileID: 5267290339349674983} + leftHandTransform: {fileID: 2164438665615948153} m_PlayerId: 0 - m_DummyLeftController: {fileID: 4048656607324710010} - m_DummyRightController: {fileID: 2292257892099998613} + m_LeftControllerModel: {fileID: 4048656607324710010} + m_RightControllerModel: {fileID: 2292257892099998613} --- !u!114 &2967658244113361874 MonoBehaviour: m_ObjectHideFlags: 0 @@ -292,7 +294,7 @@ Transform: - {fileID: 2292257892099907481} - {fileID: 2292257892099907487} - {fileID: 2292257892099907485} - m_Father: {fileID: 4501806461527917157} + m_Father: {fileID: 5267290339349674983} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!95 &2292257892107399219 Animator: @@ -2325,7 +2327,7 @@ Transform: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 4048656607324710010} serializedVersion: 2 - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 0.15, y: 0.15, z: 0.15} m_ConstrainProportionsScale: 0 @@ -2342,7 +2344,7 @@ Transform: - {fileID: 4048656607325141056} - {fileID: 4048656607325141064} - {fileID: 4048656607325141080} - m_Father: {fileID: 1310333546402417676} + m_Father: {fileID: 2164438665615948153} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!95 &4048656607334225844 Animator: @@ -2593,8 +2595,7 @@ Transform: m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 - m_Children: - - {fileID: 4048656607325141082} + m_Children: [] m_Father: {fileID: 5887326793658353859} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &6003437268994292288 @@ -2625,9 +2626,72 @@ Transform: 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_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &6046791714391239329 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 5267290339349674983} + m_Layer: 0 + m_Name: RightHandTransform + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &5267290339349674983 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6046791714391239329} + serializedVersion: 2 + 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: 2292257892099907573} - m_Father: {fileID: 7668165333132264083} + m_Father: {fileID: 6718544642020818535} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &7683395605648486609 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2164438665615948153} + m_Layer: 0 + m_Name: LeftHandTransform + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &2164438665615948153 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7683395605648486609} + serializedVersion: 2 + 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: 4048656607325141082} + m_Father: {fileID: 6718544642020818535} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &7803168780329886017 GameObject: diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonPlayerRig.cs b/Assets/Scripts/Multiplayer/Photon/PhotonPlayerRig.cs index 9b7c519548..b450ec76fb 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonPlayerRig.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonPlayerRig.cs @@ -52,8 +52,8 @@ public int PlayerId set { m_PlayerId = value; } } - public GameObject m_DummyLeftController; - public GameObject m_DummyRightController; + public GameObject m_LeftControllerModel; + public GameObject m_RightControllerModel; public void TransmitData(PlayerRigData data) @@ -204,13 +204,13 @@ public void UpdateControllerVisibility() { if (Object.HasStateAuthority) { - m_DummyLeftController.SetActive(false); - m_DummyRightController.SetActive(false); + m_LeftControllerModel.SetActive(false); + m_RightControllerModel.SetActive(false); } else { - m_DummyLeftController.SetActive(true); - m_DummyRightController.SetActive(true); + m_LeftControllerModel.SetActive(true); + m_RightControllerModel.SetActive(true); } } From 4f84db619bdb55bf65a6b4f3e854a1799bc635c8 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Fri, 15 Nov 2024 13:45:36 +0000 Subject: [PATCH 095/174] Update MultiplayerManager.cs removing redundant commented code --- Assets/Scripts/Multiplayer/MultiplayerManager.cs | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/Assets/Scripts/Multiplayer/MultiplayerManager.cs b/Assets/Scripts/Multiplayer/MultiplayerManager.cs index 78d7dc9da6..612088deb6 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerManager.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerManager.cs @@ -305,20 +305,6 @@ void Update() var headScale = App.VrSdk.GetVrCamera().transform.localScale; var leftController = InputManager.m_Instance.GetController(InputManager.ControllerName.Brush).transform; var rightController = InputManager.m_Instance.GetController(InputManager.ControllerName.Wand).transform; - - //if (leftController == null || rightController == null) - //{ - // Debug.LogWarning("Left or right controller is null."); - // return; - //} - - //InputDevice headDevice = InputDevices.GetDeviceAtXRNode(XRNode.Head); - //if (headDevice.isValid) - //{ - // deviceModel = headDevice.name; - // Debug.Log("Headset model: " + deviceModel); - //} - var leftHandRelativeToScene = App.Scene.AsScene[leftController]; var rightHandRelativeToScene = App.Scene.AsScene[rightController]; From fd31d00e82bf2a17fb6c6dfbffc9b22506f1c95a Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Fri, 15 Nov 2024 14:12:50 +0000 Subject: [PATCH 096/174] Prevent Access to Networked Properties After PhotonPlayerRig Despawns - Introduced m_IsSpawned flag in PhotonPlayerRig to track spawn state. - Updated ReceiveData() to check m_IsSpawned before accessing IsRoomOwner, avoiding InvalidOperationException. - Set m_IsSpawned to false in OnDestroy() to handle object despawning. - Added warning logs for attempts to access networked properties post-despawn, aiding in debugging. --- .../Multiplayer/MultiplayerInterfaces.cs | 1 + .../Scripts/Multiplayer/MultiplayerManager.cs | 2 ++ .../Multiplayer/Photon/PhotonPlayerRig.cs | 27 ++++++++++++++----- 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs b/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs index f95b1693aa..35568614d1 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs @@ -71,6 +71,7 @@ public enum ConnectionState public interface ITransientData { int PlayerId { get; set; } + bool IsSpawned { get; } void TransmitData(T data); T RecieveData(); } diff --git a/Assets/Scripts/Multiplayer/MultiplayerManager.cs b/Assets/Scripts/Multiplayer/MultiplayerManager.cs index 612088deb6..740c6f26fd 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerManager.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerManager.cs @@ -343,6 +343,8 @@ void Update() bool newUser = false; foreach (var player in m_RemotePlayers) { + if (!player.IsSpawned) continue; + data = player.RecieveData(); #if OCULUS_SUPPORTED // New user, share the anchor with them diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonPlayerRig.cs b/Assets/Scripts/Multiplayer/Photon/PhotonPlayerRig.cs index b450ec76fb..2ca46addba 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonPlayerRig.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonPlayerRig.cs @@ -17,6 +17,7 @@ using UnityEngine; using Fusion; using TiltBrush; +using System; namespace OpenBrush.Multiplayer { @@ -43,6 +44,9 @@ public class PhotonPlayerRig : NetworkBehaviour, ITransientData [SerializeField] private Transform leftHandTransform; private PlayerRigData transmitData; + + private bool m_IsSpawned = false; + public bool IsSpawned => m_IsSpawned; public int m_PlayerId; @@ -55,7 +59,6 @@ public int PlayerId public GameObject m_LeftControllerModel; public GameObject m_RightControllerModel; - public void TransmitData(PlayerRigData data) { transmitData = data; @@ -68,7 +71,9 @@ public void TransmitData(PlayerRigData data) } public PlayerRigData RecieveData() - { + { + if (!m_IsSpawned) return default; + var data = new PlayerRigData(); if (m_PlayerHead?.InterpolationTarget != null) @@ -95,14 +100,20 @@ public PlayerRigData RecieveData() data.RightHandRotation = m_Right.InterpolationTarget.rotation; } - data.IsRoomOwner = this.IsRoomOwner; - data.ExtraData = new ExtraData { OculusPlayerId = this.oculusPlayerId }; - data.SceneScale = this.SceneScale; + try + { + data.IsRoomOwner = this.IsRoomOwner; + data.ExtraData = new ExtraData { OculusPlayerId = this.oculusPlayerId }; + data.SceneScale = this.SceneScale; + } + catch (InvalidOperationException ex) + { + return default; + } return data; } - public override void Spawned() { base.Spawned(); @@ -117,6 +128,8 @@ public override void Spawned() } UpdateControllerVisibility(); + + m_IsSpawned = true; } public override void FixedUpdateNetwork() @@ -226,6 +239,8 @@ void OnDestroy() m_Tool = null; m_Left = null; m_Right = null; + + m_IsSpawned = false; } } } From f670ae15e692611a7d80093f8c3d1cab694a6daf Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Fri, 15 Nov 2024 14:32:18 +0000 Subject: [PATCH 097/174] RecieveData to ReceiveData --- Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs | 2 +- Assets/Scripts/Multiplayer/MultiplayerManager.cs | 2 +- Assets/Scripts/Multiplayer/Photon/PhotonPlayerRig.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs b/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs index 35568614d1..089c6316a8 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs @@ -73,6 +73,6 @@ public interface ITransientData int PlayerId { get; set; } bool IsSpawned { get; } void TransmitData(T data); - T RecieveData(); + T ReceiveData(); } } diff --git a/Assets/Scripts/Multiplayer/MultiplayerManager.cs b/Assets/Scripts/Multiplayer/MultiplayerManager.cs index 740c6f26fd..203459dc19 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerManager.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerManager.cs @@ -345,7 +345,7 @@ void Update() { if (!player.IsSpawned) continue; - data = player.RecieveData(); + data = player.ReceiveData(); #if OCULUS_SUPPORTED // New user, share the anchor with them if (data.ExtraData.OculusPlayerId != 0 && !oculusPlayerIds.Contains(data.ExtraData.OculusPlayerId)) diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonPlayerRig.cs b/Assets/Scripts/Multiplayer/Photon/PhotonPlayerRig.cs index 2ca46addba..414a5ddfb5 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonPlayerRig.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonPlayerRig.cs @@ -70,7 +70,7 @@ public void TransmitData(PlayerRigData data) SceneScale = data.SceneScale; } - public PlayerRigData RecieveData() + public PlayerRigData ReceiveData() { if (!m_IsSpawned) return default; From 01f78c5f4acc7f7e849498e54b56de1c273f72e0 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Fri, 15 Nov 2024 14:51:22 +0000 Subject: [PATCH 098/174] Disable Undo/Redo in Multiplayer specifically Controller buttons Extended CanRedo() and CanUndo() methods in SketchControlScript to include a condition checking if the connection state is IN_ROOM, preventing undo/redo functionality when in multiplayer mode. --- Assets/Scripts/SketchControlsScript.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Assets/Scripts/SketchControlsScript.cs b/Assets/Scripts/SketchControlsScript.cs index 3c9d47ab71..8440900373 100644 --- a/Assets/Scripts/SketchControlsScript.cs +++ b/Assets/Scripts/SketchControlsScript.cs @@ -1610,7 +1610,8 @@ bool CanUndo() m_PanelManager.GazePanelsAreVisible() && !m_GrabWand.grabbingWorld && !InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Activate) && - !SelectionManager.m_Instance.IsAnimatingTossFromGrabbingGroup; + !SelectionManager.m_Instance.IsAnimatingTossFromGrabbingGroup && + !(MultiplayerManager.m_Instance.State == ConnectionState.IN_ROOM); } bool CanRedo() @@ -1620,7 +1621,8 @@ bool CanRedo() m_PanelManager.GazePanelsAreVisible() && !m_GrabBrush.grabbingWorld && !InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Activate) && - !SelectionManager.m_Instance.IsAnimatingTossFromGrabbingGroup; + !SelectionManager.m_Instance.IsAnimatingTossFromGrabbingGroup && + !(MultiplayerManager.m_Instance.State == ConnectionState.IN_ROOM); } bool ShouldRepeatUndo() From 72c68cae643c2f158b558ed91fbab4a433598163 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Fri, 15 Nov 2024 15:30:06 +0000 Subject: [PATCH 099/174] Disable loading sketches when in multiplayer mode --- Assets/Prefabs/Panels/AdminPanel_Mobile.prefab | 9 +++++++++ Assets/Scripts/GUI/SketchbookButton.cs | 8 ++++++++ Assets/Scripts/SketchControlsScript.cs | 4 ++++ 3 files changed, 21 insertions(+) diff --git a/Assets/Prefabs/Panels/AdminPanel_Mobile.prefab b/Assets/Prefabs/Panels/AdminPanel_Mobile.prefab index b37d1cd893..4421b82bc6 100644 --- a/Assets/Prefabs/Panels/AdminPanel_Mobile.prefab +++ b/Assets/Prefabs/Panels/AdminPanel_Mobile.prefab @@ -5,6 +5,7 @@ PrefabInstance: m_ObjectHideFlags: 0 serializedVersion: 2 m_Modification: + serializedVersion: 3 m_TransformParent: {fileID: 0} m_Modifications: - target: {fileID: 1000816559363234, guid: f5b291cbc819737469486452ea6748b8, type: 3} @@ -55,6 +56,11 @@ PrefabInstance: propertyPath: m_LocalEulerAnglesHint.z value: 0 objectReference: {fileID: 0} + - target: {fileID: 114426215517413324, guid: f5b291cbc819737469486452ea6748b8, + type: 3} + propertyPath: m_AllowUnavailable + value: 1 + objectReference: {fileID: 0} - target: {fileID: 114828844590133850, guid: f5b291cbc819737469486452ea6748b8, type: 3} propertyPath: m_PanelType @@ -79,4 +85,7 @@ PrefabInstance: objectReference: {fileID: 563727746153209697, guid: 60ddeefaa28cf76478a6671a1193e2c9, type: 3} m_RemovedComponents: [] + m_RemovedGameObjects: [] + m_AddedGameObjects: [] + m_AddedComponents: [] m_SourcePrefab: {fileID: 100100000, guid: f5b291cbc819737469486452ea6748b8, type: 3} diff --git a/Assets/Scripts/GUI/SketchbookButton.cs b/Assets/Scripts/GUI/SketchbookButton.cs index a8334858fd..4be0054324 100644 --- a/Assets/Scripts/GUI/SketchbookButton.cs +++ b/Assets/Scripts/GUI/SketchbookButton.cs @@ -21,6 +21,14 @@ public class SketchbookButton : OptionButton [SerializeField] private float m_AdjustDistanceAmount; [SerializeField] private Renderer m_IconRenderer; + override public void UpdateVisuals() + { + // this is ugly but for some reason even if this is set to true on the option button it gets turned to false + m_AllowUnavailable = true; + base.UpdateVisuals(); + + } + protected override void AdjustButtonPositionAndScale( float posAmount, float scaleAmount, float boxColliderGrowAmount) { diff --git a/Assets/Scripts/SketchControlsScript.cs b/Assets/Scripts/SketchControlsScript.cs index 8440900373..eb7340b984 100644 --- a/Assets/Scripts/SketchControlsScript.cs +++ b/Assets/Scripts/SketchControlsScript.cs @@ -5088,6 +5088,10 @@ public bool IsCommandAvailable(GlobalCommands rEnum, int iParam = -1) return !PanelManager.m_Instance.AdvancedModeActive() && MultiplayerManager.m_Instance.CanJoinRoom(); case GlobalCommands.MultiplayerLeaveRoom: return MultiplayerManager.m_Instance.CanLeaveRoom(); + case GlobalCommands.Sketchbook: + return !(MultiplayerManager.m_Instance.State == ConnectionState.IN_ROOM); + case GlobalCommands.SketchbookMenu: + return !(MultiplayerManager.m_Instance.State == ConnectionState.IN_ROOM); } return true; } From 91fec4fc676aed00d91a17192c40efa8224dba09 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Fri, 15 Nov 2024 16:01:50 +0000 Subject: [PATCH 100/174] hiding extra button on prefab --- Assets/Prefabs/Panels/AdminPanel.prefab | 22 ++++++++----- .../Prefabs/Panels/AdminPanel_Mobile.prefab | 32 ------------------- 2 files changed, 14 insertions(+), 40 deletions(-) diff --git a/Assets/Prefabs/Panels/AdminPanel.prefab b/Assets/Prefabs/Panels/AdminPanel.prefab index 34683efa54..cd6539df84 100644 --- a/Assets/Prefabs/Panels/AdminPanel.prefab +++ b/Assets/Prefabs/Panels/AdminPanel.prefab @@ -48,7 +48,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 8aef76c4a1603ae4a9b2be90c69653da, type: 3} m_Name: m_EditorClassIdentifier: - m_PanelType: 26 + m_PanelType: 4 m_Collider: {fileID: 65962833710381506} m_Mesh: {fileID: 1577576848578658} m_Border: {fileID: 23798923668222696} @@ -61,12 +61,13 @@ MonoBehaviour: - m_PopUpPrefab: {fileID: 1000012632495268, guid: ce8b545b77426514d8145079c55ac5e7, type: 3} m_Command: 47 - - m_PopUpPrefab: {fileID: 197348, guid: d6d515da978a4db419b15f960d57d718, type: 3} + - m_PopUpPrefab: {fileID: 1269800539083404, guid: 8c11503e6d0a24d409b1d8c992652d3b, + type: 3} m_Command: 31 - - m_PopUpPrefab: {fileID: 1000012632495268, guid: 4d2503fe0d5c9ae48a510658ac9df4a9, + - m_PopUpPrefab: {fileID: 1153581385733574731, guid: 545e16288627ef8439942b5ec3dac49e, type: 3} m_Command: 96 - - m_PopUpPrefab: {fileID: 8644332587479430734, guid: 4abd2477ca9b56d4ebbc36af78d00b5d, + - m_PopUpPrefab: {fileID: 563727746153209697, guid: 60ddeefaa28cf76478a6671a1193e2c9, type: 3} m_Command: 30 m_PanelDescription: @@ -168,6 +169,10 @@ MonoBehaviour: renderer: {fileID: 0} baseLocalPos: {x: 0, y: 0, z: 0} baseScale: {x: 0, y: 0, z: 0} + - button: {fileID: 3913670656884359384} + renderer: {fileID: 0} + baseLocalPos: {x: 0, y: 0, z: 0} + baseScale: {x: 0, y: 0, z: 0} m_SaveNewButton: {fileID: 1922662397484504} m_SaveOptionsButton: {fileID: 1151082384707834} m_SettingsButton: {fileID: 1936268928807740} @@ -182,6 +187,7 @@ MonoBehaviour: m_MemoryWarningButton: {fileID: 1712905289553642} m_MemoryWarningColor: {r: 0.78676474, g: 0.09256055, b: 0.09256055, a: 1} m_MultiplayerButton: {fileID: 5787249127137797294} + m_EmptyButton: {fileID: 1283872556223845477} m_ButtonRotationAngle: 45 m_ShareButtonLoggedOutExtraText: m_TableReference: @@ -1123,7 +1129,7 @@ MonoBehaviour: m_WaitForCompletion: 0 m_LocalVariables: [] m_ToggleOnTexture: {fileID: 0} - m_AllowUnavailable: 0 + m_AllowUnavailable: 1 m_LinkedUIObject: {fileID: 0} m_AdjustDistanceAmount: 1 m_IconRenderer: {fileID: 23861549990399966} @@ -3224,7 +3230,7 @@ GameObject: - component: {fileID: 6605348980898369502} - component: {fileID: 8695917633644188848} m_Layer: 16 - m_Name: Button_News + m_Name: Button_Empty m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 @@ -3328,7 +3334,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 6c6859eec74651247968d56b594ac313, type: 3} m_Name: m_EditorClassIdentifier: - m_DescriptionType: -1 + m_DescriptionType: 0 m_DescriptionYOffset: 0 m_DescriptionText: ADMIN_PANEL_SAVE_SKETCH_BUTTON_DESCRIPTION m_LocalizedDescription: @@ -3365,7 +3371,7 @@ MonoBehaviour: m_Command: 0 m_CommandParam: -1 m_CommandParam2: -1 - m_RequiresPopup: 1 + m_RequiresPopup: 0 m_CenterPopupOnButton: 0 m_PopupOffset: {x: 0, y: 0, z: 0} m_PopupText: diff --git a/Assets/Prefabs/Panels/AdminPanel_Mobile.prefab b/Assets/Prefabs/Panels/AdminPanel_Mobile.prefab index 4421b82bc6..1c422e5154 100644 --- a/Assets/Prefabs/Panels/AdminPanel_Mobile.prefab +++ b/Assets/Prefabs/Panels/AdminPanel_Mobile.prefab @@ -12,10 +12,6 @@ PrefabInstance: propertyPath: m_Name value: AdminPanel_Mobile objectReference: {fileID: 0} - - target: {fileID: 4482757564845618, guid: f5b291cbc819737469486452ea6748b8, type: 3} - propertyPath: m_RootOrder - value: 0 - objectReference: {fileID: 0} - target: {fileID: 4482757564845618, guid: f5b291cbc819737469486452ea6748b8, type: 3} propertyPath: m_LocalPosition.x value: 6.277 @@ -56,34 +52,6 @@ PrefabInstance: propertyPath: m_LocalEulerAnglesHint.z value: 0 objectReference: {fileID: 0} - - target: {fileID: 114426215517413324, guid: f5b291cbc819737469486452ea6748b8, - type: 3} - propertyPath: m_AllowUnavailable - value: 1 - objectReference: {fileID: 0} - - target: {fileID: 114828844590133850, guid: f5b291cbc819737469486452ea6748b8, - type: 3} - propertyPath: m_PanelType - value: 4 - objectReference: {fileID: 0} - - target: {fileID: 114828844590133850, guid: f5b291cbc819737469486452ea6748b8, - type: 3} - propertyPath: m_PanelPopUpMap.Array.data[2].m_PopUpPrefab - value: - objectReference: {fileID: 1269800539083404, guid: 8c11503e6d0a24d409b1d8c992652d3b, - type: 3} - - target: {fileID: 114828844590133850, guid: f5b291cbc819737469486452ea6748b8, - type: 3} - propertyPath: m_PanelPopUpMap.Array.data[3].m_PopUpPrefab - value: - objectReference: {fileID: 1153581385733574731, guid: 545e16288627ef8439942b5ec3dac49e, - type: 3} - - target: {fileID: 114828844590133850, guid: f5b291cbc819737469486452ea6748b8, - type: 3} - propertyPath: m_PanelPopUpMap.Array.data[4].m_PopUpPrefab - value: - objectReference: {fileID: 563727746153209697, guid: 60ddeefaa28cf76478a6671a1193e2c9, - type: 3} m_RemovedComponents: [] m_RemovedGameObjects: [] m_AddedGameObjects: [] From 9c96f86d25c2410ac88adc996d1e7d2edcbc0dbf Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Tue, 19 Nov 2024 18:37:21 +0000 Subject: [PATCH 101/174] [CI BUILD] From fae21b96ad001d1135d5ddf4522833732d06611d Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Wed, 27 Nov 2024 13:46:57 +0000 Subject: [PATCH 102/174] Removing m_GuidCache Removing unused m_GuidCache --- Assets/Scripts/SketchMemoryScript.cs | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/Assets/Scripts/SketchMemoryScript.cs b/Assets/Scripts/SketchMemoryScript.cs index c1f108daa4..7fcc2a50df 100644 --- a/Assets/Scripts/SketchMemoryScript.cs +++ b/Assets/Scripts/SketchMemoryScript.cs @@ -16,8 +16,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Security.Cryptography; -using System.Text; namespace TiltBrush { @@ -80,7 +78,6 @@ public static void SetFlag(ref SketchMemoryScript.StrokeFlags flags, // stack of sketch operations this session\\\ private Stack m_OperationStack; - private HashSet m_GuidCache; // Cache of GUIDs for network transmission // stack of undone operations available for redo private Stack m_RedoStack; @@ -290,20 +287,6 @@ public bool IsMemoryDirty() return false; } - public IReadOnlyCollection GetCommandGuids() - { - return m_GuidCache; - } - - private string CalculateGuidCacheHash() - { - using (var md5 = MD5.Create()) - { - var combined = string.Join("", m_GuidCache.OrderBy(g => g).Select(g => g.ToString())); - byte[] hashBytes = md5.ComputeHash(Encoding.UTF8.GetBytes(combined)); - return BitConverter.ToString(hashBytes).Replace("-", "").ToLowerInvariant(); - } - } public bool CanUndo() { return m_OperationStack.Count > 0; } public bool CanRedo() { return m_RedoStack.Count > 0; } @@ -313,7 +296,6 @@ void Awake() m_OperationStack = new Stack(); m_LastOperationStackCount = 0; m_RedoStack = new Stack(); - m_GuidCache = new HashSet(); m_HasVisibleObjects = false; m_MemoryExceeded = false; m_MemoryWarningAccepted = false; @@ -407,11 +389,9 @@ public void PerformAndRecordCommand(BaseCommand command, bool discardIfNotMerged while (m_OperationStack.Any()) { BaseCommand top = m_OperationStack.Pop(); - m_GuidCache.Remove(top.Guid); if (!top.Merge(command)) { m_OperationStack.Push(top); - m_GuidCache.Add(top.Guid); break; } discardCommand = false; @@ -424,7 +404,6 @@ public void PerformAndRecordCommand(BaseCommand command, bool discardIfNotMerged } delta.Redo(); m_OperationStack.Push(command); - m_GuidCache.Add(command.Guid); OperationStackChanged?.Invoke(); if (invoke) @@ -441,17 +420,14 @@ public void RecordCommand(BaseCommand command) while (m_OperationStack.Any()) { BaseCommand top = m_OperationStack.Pop(); - m_GuidCache.Remove(top.Guid); if (!top.Merge(command)) { m_OperationStack.Push(top); - m_GuidCache.Add(top.Guid); break; } command = top; } m_OperationStack.Push(command); - m_GuidCache.Add(command.Guid); OperationStackChanged?.Invoke(); CommandPerformed?.Invoke(command); } @@ -830,7 +806,6 @@ public void ClearMemory() } } m_OperationStack.Clear(); - m_GuidCache.Clear(); OperationStackChanged?.Invoke(); m_LastOperationStackCount = 0; m_MemoryList.Clear(); @@ -950,7 +925,6 @@ public IEnumerator RepaintCoroutine() public void StepBack(bool invoke = true) { var comm = m_OperationStack.Pop(); - m_GuidCache.Remove(comm.Guid); comm.Undo(); m_RedoStack.Push(comm); OperationStackChanged?.Invoke(); From e831cca679584e476d17e53117b9453330c23280 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Wed, 27 Nov 2024 14:58:57 +0000 Subject: [PATCH 103/174] Separation of Command Stacks Divided local (m_OperationStack) and network (m_NetworkStack) command stacks to handle independent undo/redo flows for local and networked operations. Added IsCommandInNetworkStack to check for repeated commands in the network stack, ensuring no duplicate processing. Facilitates clearer distinction between local and global operations in multiplayer scenarios. --- .../Scripts/Multiplayer/Photon/PhotonRPC.cs | 3 +- Assets/Scripts/SketchMemoryScript.cs | 35 +++++++++++++++++-- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs b/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs index f5a3f51f35..a985df7117 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs @@ -105,7 +105,8 @@ private void TryProcessCommands() InvokePreCommands(command); - SketchMemoryScript.m_Instance.PerformAndRecordCommand(command.Command, invoke: false); + //SketchMemoryScript.m_Instance.PerformAndRecordCommand(command.Command, invoke: false); + SketchMemoryScript.m_Instance.PerformAndRecordNetworkCommand(command.Command); TryProcessCommands(); } diff --git a/Assets/Scripts/SketchMemoryScript.cs b/Assets/Scripts/SketchMemoryScript.cs index 7fcc2a50df..4199cbab69 100644 --- a/Assets/Scripts/SketchMemoryScript.cs +++ b/Assets/Scripts/SketchMemoryScript.cs @@ -36,6 +36,7 @@ public class SketchMemoryScript : MonoBehaviour public static SketchMemoryScript m_Instance; public event Action OperationStackChanged; + public event Action NetworkOperationStackChanged; public Action CommandPerformed; public Action CommandUndo; public Action CommandRedo; @@ -76,10 +77,12 @@ public static void SetFlag(ref SketchMemoryScript.StrokeFlags flags, } } - // stack of sketch operations this session\\\ + // stack of sketch operations this session private Stack m_OperationStack; // stack of undone operations available for redo private Stack m_RedoStack; + // stack of network sketch operations + private Stack m_NetworkStack = new Stack(); // Memory list by timestamp of initial control point. The nodes of this list are // embedded in MemoryObject. Notable properties: @@ -412,6 +415,17 @@ public void PerformAndRecordCommand(BaseCommand command, bool discardIfNotMerged } } + /// Executes and records a network-synchronized command. + /// Note: This method does not include merge logic or parent-child relationship checks, + /// as these are already handled by the PhotonRPC system. + public void PerformAndRecordNetworkCommand(BaseCommand command) + { + BaseCommand delta = command; + delta.Redo(); + m_NetworkStack.Push(command); + NetworkOperationStackChanged?.Invoke(); + } + // TODO: deprecate in favor of PerformAndRecordCommand // Used by BrushStrokeCommand and ModifyLightCommmand while in Disco mode public void RecordCommand(BaseCommand command) @@ -784,6 +798,16 @@ public void ClearRedo() m_RedoStack.Clear(); } + public void ClearNetworkStack() + { + foreach (var command in m_NetworkStack) + { + command.Dispose(); + } + m_NetworkStack.Clear(); + NetworkOperationStackChanged?.Invoke(); + } + public void ClearMemory() { if (m_ScenePlayback != null) @@ -1383,7 +1407,9 @@ public static List GetStrokesBetween(int start, int end) public bool IsCommandInStack(Guid commandGuid) { - return IsCommandInOperationStack(commandGuid) || IsCommandInRedoStack(commandGuid); + return IsCommandInOperationStack(commandGuid) || + IsCommandInRedoStack(commandGuid) || + IsCommandInNetworkStack(commandGuid); } public bool IsCommandInOperationStack(Guid commandGuid) @@ -1395,5 +1421,10 @@ public bool IsCommandInRedoStack(Guid commandGuid) { return m_RedoStack.Any(command => command.Guid == commandGuid); } + + public bool IsCommandInNetworkStack(Guid commandGuid) + { + return m_NetworkStack.Any(command => command.Guid == commandGuid); + } } } // namespace TiltBrush From e51d1b24fd881271e42b3cffd486becea6005a8d Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Wed, 27 Nov 2024 15:38:29 +0000 Subject: [PATCH 104/174] Update to SendCommandHistory() to include the network command stack --- Assets/Scripts/Multiplayer/MultiplayerManager.cs | 2 +- Assets/Scripts/SketchMemoryScript.cs | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Assets/Scripts/Multiplayer/MultiplayerManager.cs b/Assets/Scripts/Multiplayer/MultiplayerManager.cs index 203459dc19..f5abfc1612 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerManager.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerManager.cs @@ -491,7 +491,7 @@ private void OnConnectionHandlerDisconnected() private IEnumerator SendCommandHistory() { - IEnumerable commands = SketchMemoryScript.m_Instance.GetOperationStack().Reverse(); + IEnumerable commands = SketchMemoryScript.m_Instance.GetAllOperations().Reverse(); int counter = 0; diff --git a/Assets/Scripts/SketchMemoryScript.cs b/Assets/Scripts/SketchMemoryScript.cs index 4199cbab69..07660ff114 100644 --- a/Assets/Scripts/SketchMemoryScript.cs +++ b/Assets/Scripts/SketchMemoryScript.cs @@ -194,11 +194,14 @@ public LinkedList GetMemoryList get { return m_MemoryList; } } - public IEnumerable GetOperationStack() + public IEnumerable GetAllOperations() { - return m_OperationStack; + var allCommands = m_OperationStack.Concat(m_NetworkStack); + //return allCommands.OrderBy(command => command.Timestamp); + return allCommands; } + public Stroke GetStrokeAtIndex(int index) { return m_Instance.m_MemoryList.ElementAt(index); From e59470a7cc03abd3c23c077129c99bfbd0f10c76 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Wed, 27 Nov 2024 20:46:19 +0000 Subject: [PATCH 105/174] Introduce networked timestamps for commands This ensures that command histories across different stacks can be transmitted and synchronized in order between peers. --- Assets/Scripts/Commands/BaseCommand.cs | 20 +++++++++-- Assets/Scripts/Commands/BrushStrokeCommand.cs | 4 +-- .../Scripts/Commands/DeleteStrokeCommand.cs | 9 +++++ .../Commands/SwitchEnvironmentCommand.cs | 18 ++++++++++ .../Multiplayer/MultiplayerInterfaces.cs | 1 + .../Scripts/Multiplayer/MultiplayerManager.cs | 33 ++++++++++++++++++- .../Multiplayer/Photon/PhotonManager.cs | 16 ++++++--- .../Scripts/Multiplayer/Photon/PhotonRPC.cs | 21 ++++++------ Assets/Scripts/SketchMemoryScript.cs | 23 +++++++++++-- 9 files changed, 123 insertions(+), 22 deletions(-) diff --git a/Assets/Scripts/Commands/BaseCommand.cs b/Assets/Scripts/Commands/BaseCommand.cs index 623b24c756..ad32f7b62c 100644 --- a/Assets/Scripts/Commands/BaseCommand.cs +++ b/Assets/Scripts/Commands/BaseCommand.cs @@ -16,6 +16,8 @@ using System.Linq; using System.Collections.Generic; using UnityEngine; +using Fusion; +using OpenBrush.Multiplayer; namespace TiltBrush { @@ -29,7 +31,8 @@ public class BaseCommand : IDisposable private Guid m_Guid; private BaseCommand m_Parent; protected List m_Children; - private int m_Timestamp; // Updated to use an integer timestamp from Photon server + private int m_Timestamp; + private int? m_NetworkTimestamp; public void SetParent(BaseCommand parent) { @@ -41,6 +44,12 @@ public int Timestamp set { m_Timestamp = value; } } + public int? NetworkTimestamp + { + get { return m_NetworkTimestamp; } + set { m_NetworkTimestamp = value; } + } + public int ChildrenCount { get { return m_Children.Count; } @@ -88,10 +97,13 @@ public BaseCommand(BaseCommand parent = null) parent.m_Children.Add(this); m_Parent = parent; } + + m_Timestamp = (int)(App.Instance.CurrentSketchTime * 1000); // convert to milliseconds + m_NetworkTimestamp = MultiplayerManager.m_Instance?.GetNetworkedTimestampMilliseconds(); } - // constructor that takes an existing Guid used in multiplayer to mantain consistences of commands across peers - public BaseCommand(Guid existingGuid, BaseCommand parent = null) + // constructor that takes an existing Guid and Timestamp used in multiplayer to mantain consistences of commands across peers + public BaseCommand(Guid existingGuid, int timestamp, BaseCommand parent = null) { m_Guid = existingGuid; m_Children = new List(); @@ -100,6 +112,8 @@ public BaseCommand(Guid existingGuid, BaseCommand parent = null) parent.m_Children.Add(this); m_Parent = parent; } + m_Timestamp = timestamp; + m_NetworkTimestamp = timestamp; } /// True if this command changes the sketch in a saveable diff --git a/Assets/Scripts/Commands/BrushStrokeCommand.cs b/Assets/Scripts/Commands/BrushStrokeCommand.cs index 0c92ae8c58..1c52ac4750 100644 --- a/Assets/Scripts/Commands/BrushStrokeCommand.cs +++ b/Assets/Scripts/Commands/BrushStrokeCommand.cs @@ -37,9 +37,9 @@ public BrushStrokeCommand(Stroke stroke, StencilWidget widget = null, } // New constructor that accepts an existing Guid - public BrushStrokeCommand(Stroke stroke, Guid existingGuid, StencilWidget widget = null, + public BrushStrokeCommand(Stroke stroke, Guid existingGuid, int timestamp, StencilWidget widget = null, float lineLength = -1, BaseCommand parent = null) - : base(existingGuid, parent) + : base(existingGuid, timestamp, parent) { m_Stroke = stroke; m_Widget = widget; diff --git a/Assets/Scripts/Commands/DeleteStrokeCommand.cs b/Assets/Scripts/Commands/DeleteStrokeCommand.cs index 18f86c1d30..c8d5abe85c 100644 --- a/Assets/Scripts/Commands/DeleteStrokeCommand.cs +++ b/Assets/Scripts/Commands/DeleteStrokeCommand.cs @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +using System; +using UnityEditor; using UnityEngine; namespace TiltBrush @@ -33,6 +35,13 @@ public DeleteStrokeCommand(Stroke stroke, BaseCommand parent = null) m_SilenceFirstAudio = true; } + public DeleteStrokeCommand(Stroke stroke, Guid existingGuid, int timestamp, BaseCommand parent = null) + : base(existingGuid, timestamp, parent) + { + m_TargetStroke = stroke; + m_SilenceFirstAudio = true; + } + public override bool NeedsSave { get { return true; } } protected override void OnRedo() diff --git a/Assets/Scripts/Commands/SwitchEnvironmentCommand.cs b/Assets/Scripts/Commands/SwitchEnvironmentCommand.cs index 5ff82667e5..dab3d40b58 100644 --- a/Assets/Scripts/Commands/SwitchEnvironmentCommand.cs +++ b/Assets/Scripts/Commands/SwitchEnvironmentCommand.cs @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +using System; + namespace TiltBrush { public class SwitchEnvironmentCommand : BaseCommand @@ -36,6 +38,22 @@ public SwitchEnvironmentCommand(Environment nextEnv, BaseCommand parent = null) } } + public SwitchEnvironmentCommand(Environment nextEnv, Guid existingGuid, int timestamp, BaseCommand parent = null) + : base(existingGuid, timestamp, parent) + { + m_NextEnvironment = nextEnv; + m_PrevBackdrop = SceneSettings.m_Instance.CustomEnvironment; + if (SceneSettings.m_Instance.IsTransitioning) + { + m_PrevEnvironment = SceneSettings.m_Instance.GetDesiredPreset(); + } + else + { + m_PrevLights = LightsControlScript.m_Instance.CustomLights; + m_PrevEnvironment = SceneSettings.m_Instance.CurrentEnvironment; + } + } + public override bool NeedsSave { get diff --git a/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs b/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs index 089c6316a8..80a84213a1 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs @@ -34,6 +34,7 @@ public interface IDataConnectionHandler : IConnectionHandler void Update(); int GetPlayerCount(); + int GetNetworkedTimestampMilliseconds(); bool GetPlayerRoomOwnershipStatus(int playerId); Task PerformCommand(BaseCommand command); Task UndoCommand(BaseCommand command); diff --git a/Assets/Scripts/Multiplayer/MultiplayerManager.cs b/Assets/Scripts/Multiplayer/MultiplayerManager.cs index f5abfc1612..a6e6284af2 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerManager.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerManager.cs @@ -57,6 +57,7 @@ public class MultiplayerManager : MonoBehaviour public event Action RoomOwnershipUpdated; public event Action UserInfoStateUpdated; private List m_RoomData = new List(); + private double? m_NetworkOffsetTimestamp = null; ulong myOculusUserId; @@ -155,6 +156,7 @@ void Start() localPlayerJoined += OnLocalPlayerJoined; remotePlayerJoined += OnRemotePlayerJoined; playerLeft += OnPlayerLeft; + StateUpdated += UpdateSketchMemoryScriptTimeOffset; SketchMemoryScript.m_Instance.CommandPerformed += OnCommandPerformed; SketchMemoryScript.m_Instance.CommandUndo += OnCommandUndo; SketchMemoryScript.m_Instance.CommandRedo += OnCommandRedo; @@ -166,6 +168,7 @@ void OnDestroy() localPlayerJoined -= OnLocalPlayerJoined; remotePlayerJoined -= OnRemotePlayerJoined; playerLeft -= OnPlayerLeft; + StateUpdated -= UpdateSketchMemoryScriptTimeOffset; SketchMemoryScript.m_Instance.CommandPerformed -= OnCommandPerformed; SketchMemoryScript.m_Instance.CommandUndo -= OnCommandUndo; SketchMemoryScript.m_Instance.CommandRedo -= OnCommandRedo; @@ -491,7 +494,7 @@ private void OnConnectionHandlerDisconnected() private IEnumerator SendCommandHistory() { - IEnumerable commands = SketchMemoryScript.m_Instance.GetAllOperations().Reverse(); + IEnumerable commands = SketchMemoryScript.m_Instance.GetAllOperations(); int counter = 0; @@ -543,5 +546,33 @@ public bool IsUserRoomOwner() { return isUserRoomOwner; } + + public int? GetNetworkedTimestampMilliseconds() + { + if (State == ConnectionState.IN_ROOM) + { + if (m_Manager != null) return m_Manager.GetNetworkedTimestampMilliseconds(); + } + + return null; + } + + // this only needs to be done once when the room is created + private void UpdateSketchMemoryScriptTimeOffset(ConnectionState state) + { + // Ensure the offset is set only once upon connecting as room owner + if (state == ConnectionState.IN_ROOM + && isUserRoomOwner + && m_NetworkOffsetTimestamp == null) + { + // Capture the current sketch time as the base offset for network synchronization + m_NetworkOffsetTimestamp = (int)(App.Instance.CurrentSketchTime * 1000); + SketchMemoryScript.m_Instance.SetTimeOffsetToAllStacks((int)m_NetworkOffsetTimestamp); + Debug.Log($"Network offset timestamp set: {m_NetworkOffsetTimestamp}s"); + + } + + } } } + diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs index 69c6a9c9e1..90e5283c65 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs @@ -236,6 +236,13 @@ public int GetPlayerCount() return 0; } + public int GetNetworkedTimestampMilliseconds() + { + int tickRate = m_Runner.Simulation.Config.TickRate; + int networkTimeMilliseconds = (int)((m_Runner.Simulation.Tick * 1000) / (double)tickRate); + return networkTimeMilliseconds; + } + public bool GetPlayerRoomOwnershipStatus(int playerId) { var remotePlayer = m_PlayersSpawning @@ -355,12 +362,12 @@ private bool CommandBrushStroke(BrushStrokeCommand command) } // End - PhotonRPC.RPC_BrushStrokeComplete(m_Runner, strokeGuid, command.Guid, command.ParentGuid, command.ChildrenCount); + PhotonRPC.RPC_BrushStrokeComplete(m_Runner, strokeGuid, command.Guid, (int)command.NetworkTimestamp, 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); + PhotonRPC.RPC_BrushStrokeFull(m_Runner, new NetworkedStroke().Init(command.m_Stroke), command.Guid, (int)command.NetworkTimestamp, command.ParentGuid, command.ChildrenCount); } return true; } @@ -373,14 +380,14 @@ private bool CommandBase(BaseCommand command) private bool CommandDeleteStroke(DeleteStrokeCommand command) { - PhotonRPC.RPC_DeleteStroke(m_Runner, command.m_TargetStroke.m_Seed, command.Guid, command.ParentGuid, command.ChildrenCount); + PhotonRPC.RPC_DeleteStroke(m_Runner, command.m_TargetStroke.m_Seed, command.Guid, (int)command.NetworkTimestamp, command.ParentGuid, command.ChildrenCount); return true; } private bool CommandSwitchEnvironment(SwitchEnvironmentCommand command) { Guid environmentGuid = command.m_NextEnvironment.m_Guid; - PhotonRPC.RPC_SwitchEnvironment(m_Runner, environmentGuid, command.Guid, command.ParentGuid, command.ChildrenCount); + PhotonRPC.RPC_SwitchEnvironment(m_Runner, environmentGuid, command.Guid, (int)command.NetworkTimestamp, command.ParentGuid, command.ChildrenCount); return true; } #endregion @@ -460,6 +467,7 @@ public void OnHostMigration(NetworkRunner runner, HostMigrationToken hostMigrati public void OnReliableDataReceived(NetworkRunner runner, PlayerRef player, ArraySegment data) { } public void OnSceneLoadDone(NetworkRunner runner) { } public void OnSceneLoadStart(NetworkRunner runner) { } + #endregion } } diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs b/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs index a985df7117..10fa994fa6 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs @@ -145,8 +145,9 @@ private static BaseCommand FindParentCommand(Guid parentGuid) return null; } - public static void CreateBrushStroke(Stroke stroke, Guid commandGuid, Guid parentGuid = default, int childCount = 0) + public static void CreateBrushStroke(Stroke stroke, Guid commandGuid, int timestamp, Guid parentGuid = default, int childCount = 0) { + Action preAction = () => { stroke.m_Type = Stroke.Type.NotCreated; @@ -157,7 +158,7 @@ public static void CreateBrushStroke(Stroke stroke, Guid commandGuid, Guid paren var parentCommand = FindParentCommand(parentGuid); - var command = new BrushStrokeCommand( stroke, commandGuid, parent: parentCommand); + var command = new BrushStrokeCommand( stroke, commandGuid, timestamp, parent: parentCommand); AddPendingCommand(preAction, commandGuid, parentGuid, command, childCount); } @@ -228,14 +229,14 @@ public static void RPC_BaseCommand(NetworkRunner runner, Guid commandGuid, Guid } [Rpc(InvokeLocal = false)] - public static void RPC_BrushStrokeFull(NetworkRunner runner, NetworkedStroke strokeData, Guid commandGuid, Guid parentGuid = default, int childCount = 0) + public static void RPC_BrushStrokeFull(NetworkRunner runner, NetworkedStroke strokeData, Guid commandGuid, int timestamp, Guid parentGuid = default, int childCount = 0) { if (CheckifCommandGuidIsInStack(commandGuid)) return; var decode = NetworkedStroke.ToStroke(strokeData); - CreateBrushStroke(decode, commandGuid, parentGuid, childCount); + CreateBrushStroke(decode, commandGuid, timestamp , parentGuid, childCount); } [Rpc(InvokeLocal = false)] @@ -277,7 +278,7 @@ public static void RPC_BrushStrokeContinue(NetworkRunner runner, Guid id, int of } [Rpc(InvokeLocal = false)] - public static void RPC_BrushStrokeComplete(NetworkRunner runner, Guid id, Guid commandGuid, Guid parentGuid = default, int childCount = 0) + public static void RPC_BrushStrokeComplete(NetworkRunner runner, Guid id, Guid commandGuid, int timestamp, Guid parentGuid = default, int childCount = 0) { if (CheckifCommandGuidIsInStack(commandGuid)) return; @@ -290,13 +291,13 @@ public static void RPC_BrushStrokeComplete(NetworkRunner runner, Guid id, Guid c var stroke = m_inProgressStrokes[id]; - CreateBrushStroke(stroke, commandGuid, parentGuid, childCount); + CreateBrushStroke(stroke, commandGuid, timestamp, 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) + public static void RPC_DeleteStroke(NetworkRunner runner, int seed, Guid commandGuid, int timestamp, Guid parentGuid = default, int childCount = 0) { if (CheckifCommandGuidIsInStack(commandGuid)) return; @@ -305,7 +306,7 @@ public static void RPC_DeleteStroke(NetworkRunner runner, int seed, Guid command if (foundStroke != null) { var parentCommand = FindParentCommand(parentGuid); - var command = new DeleteStrokeCommand(foundStroke, parent: parentCommand); + var command = new DeleteStrokeCommand(foundStroke, commandGuid, timestamp, parent: parentCommand); AddPendingCommand(() => {}, commandGuid, parentGuid, command, childCount); } @@ -316,7 +317,7 @@ public static void RPC_DeleteStroke(NetworkRunner runner, int seed, Guid command } [Rpc(InvokeLocal = false)] - public static void RPC_SwitchEnvironment(NetworkRunner runner, Guid environmentGuid, Guid commandGuid, Guid parentGuid = default, int childCount = 0) + public static void RPC_SwitchEnvironment(NetworkRunner runner, Guid environmentGuid, Guid commandGuid, int timestamp, Guid parentGuid = default, int childCount = 0) { if (CheckifCommandGuidIsInStack(commandGuid)) return; @@ -326,7 +327,7 @@ public static void RPC_SwitchEnvironment(NetworkRunner runner, Guid environmentG { var parentCommand = FindParentCommand(parentGuid); - var command = new SwitchEnvironmentCommand(environment, parent: parentCommand); + var command = new SwitchEnvironmentCommand(environment, commandGuid, timestamp, parent: parentCommand); AddPendingCommand(() => { }, commandGuid, parentGuid, command, childCount); } diff --git a/Assets/Scripts/SketchMemoryScript.cs b/Assets/Scripts/SketchMemoryScript.cs index 07660ff114..26d9918d4b 100644 --- a/Assets/Scripts/SketchMemoryScript.cs +++ b/Assets/Scripts/SketchMemoryScript.cs @@ -16,6 +16,9 @@ using System; using System.Collections.Generic; using System.Linq; +using TiltBrush; +using System.Collections; +using System.Threading.Tasks; namespace TiltBrush { @@ -197,8 +200,8 @@ public LinkedList GetMemoryList public IEnumerable GetAllOperations() { var allCommands = m_OperationStack.Concat(m_NetworkStack); - //return allCommands.OrderBy(command => command.Timestamp); - return allCommands; + + return allCommands.OrderBy(command => command.NetworkTimestamp); } @@ -1429,5 +1432,21 @@ public bool IsCommandInNetworkStack(Guid commandGuid) { return m_NetworkStack.Any(command => command.Guid == commandGuid); } + + public void SetTimeOffsetToAllStacks(int m_NetworkOffsetTimestamp) + { + SetTimeOffset(m_RedoStack, m_NetworkOffsetTimestamp); + SetTimeOffset(m_OperationStack, m_NetworkOffsetTimestamp); + SetTimeOffset(m_NetworkStack, m_NetworkOffsetTimestamp); + } + + public void SetTimeOffset(Stack stack, int m_NetworkOffsetTimestamp) + { + foreach (BaseCommand c in stack) + { + if (c.NetworkTimestamp == null) + c.NetworkTimestamp = c.Timestamp - m_NetworkOffsetTimestamp; + } + } } } // namespace TiltBrush From aa8d3d442bf8b1418074c0d198550d14f60118df Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Thu, 28 Nov 2024 09:47:25 +0000 Subject: [PATCH 106/174] Clear NetworkStack in ClearMemory() to prevent command reception issues after multiplayer sessions When calling ClearMemory(), the NetworkStack must be cleared alongside other stacks. Without this, IsCommandInStack may find existing GUIDs in NetworkStack, causing new commands to be ignored after entering and exiting multiplayer rooms. Clearing NetworkStack ensures that old command GUIDs don't interfere with new commands, allowing them to be received and processed correctly. --- Assets/Scripts/SketchMemoryScript.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Assets/Scripts/SketchMemoryScript.cs b/Assets/Scripts/SketchMemoryScript.cs index 26d9918d4b..ff30cfbe3a 100644 --- a/Assets/Scripts/SketchMemoryScript.cs +++ b/Assets/Scripts/SketchMemoryScript.cs @@ -837,6 +837,8 @@ public void ClearMemory() } m_OperationStack.Clear(); OperationStackChanged?.Invoke(); + m_NetworkStack.Clear(); + NetworkOperationStackChanged?.Invoke(); m_LastOperationStackCount = 0; m_MemoryList.Clear(); App.GroupManager.ResetGroups(); From c5498cbe8a02e722f68644b1b4a0ef1ec44a389e Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Thu, 28 Nov 2024 18:11:15 +0000 Subject: [PATCH 107/174] Update BaseCommand.cs wrongly referencing Fusion --- Assets/Scripts/Commands/BaseCommand.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Assets/Scripts/Commands/BaseCommand.cs b/Assets/Scripts/Commands/BaseCommand.cs index ad32f7b62c..5ba656cac7 100644 --- a/Assets/Scripts/Commands/BaseCommand.cs +++ b/Assets/Scripts/Commands/BaseCommand.cs @@ -16,7 +16,6 @@ using System.Linq; using System.Collections.Generic; using UnityEngine; -using Fusion; using OpenBrush.Multiplayer; namespace TiltBrush From 62222a71de7385b8c87b0ec55d35f8ea61546fdc Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Thu, 28 Nov 2024 19:00:00 +0000 Subject: [PATCH 108/174] Updating to Room Owner Logic --- Assets/Editor/MultiplayerManagerEditor.cs | 35 +++++++++++++------ .../Scripts/Multiplayer/MultiplayerManager.cs | 25 +++++++++++-- 2 files changed, 46 insertions(+), 14 deletions(-) diff --git a/Assets/Editor/MultiplayerManagerEditor.cs b/Assets/Editor/MultiplayerManagerEditor.cs index caa84ece8f..782a53f61d 100644 --- a/Assets/Editor/MultiplayerManagerEditor.cs +++ b/Assets/Editor/MultiplayerManagerEditor.cs @@ -40,33 +40,38 @@ public override void OnInspectorGUI() EditorGUILayout.EndHorizontal(); GUILayout.Space(5); - if (GUILayout.Button("Connect") ) + if (GUILayout.Button("Connect")) { ConnectToLobby(); - EditorUtility.SetDirty(target); + EditorUtility.SetDirty(target); } if (GUILayout.Button("Join Room")) { ConnectToRoom(); - EditorUtility.SetDirty(target); + EditorUtility.SetDirty(target); } - - if (GUILayout.Button("Exit Room") ) + + if (GUILayout.Button("Exit Room")) { DisconnectFromRoom(); - EditorUtility.SetDirty(target); + EditorUtility.SetDirty(target); } if (GUILayout.Button("Disconnect")) { Disconnect(); - EditorUtility.SetDirty(target); + EditorUtility.SetDirty(target); } + if (GUILayout.Button("Refresh Room List")) + { + CheckIfRoomExists(); + EditorUtility.SetDirty(target); + } //Local Player Id string localPlayerId = ""; @@ -99,7 +104,7 @@ public override void OnInspectorGUI() foreach (var remotePlayer in multiplayerManager.m_RemotePlayers) { remoteUsersRegistered += remotePlayer.PlayerId.ToString() + ","; - } + } remoteUsersRegistered += "]"; } else remoteUsersRegistered = "Not Assigned"; @@ -111,7 +116,7 @@ public override void OnInspectorGUI() EditorGUILayout.EndHorizontal(); Repaint(); - + } private async void ConnectToLobby() @@ -135,7 +140,7 @@ private async void ConnectToRoom() }; bool success = await multiplayerManager.JoinRoom(roomData); - + } } @@ -144,7 +149,7 @@ private async void DisconnectFromRoom() if (multiplayerManager != null) { bool success = await multiplayerManager.LeaveRoom(); - + } } @@ -156,5 +161,13 @@ private async void Disconnect() } } + + private void CheckIfRoomExists() + { + if (multiplayerManager != null) + { + bool roomExists = multiplayerManager.DoesRoomNameExist(roomName); + } + } } #endif diff --git a/Assets/Scripts/Multiplayer/MultiplayerManager.cs b/Assets/Scripts/Multiplayer/MultiplayerManager.cs index a6e6284af2..5d7bebf7b9 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerManager.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerManager.cs @@ -204,6 +204,8 @@ public async Task JoinRoom(RoomCreateData RoomData) { State = ConnectionState.JOINING_ROOM; + // check if room exist to determine if user is room owner + DoesRoomNameExist(RoomData.roomName); if (!isUserRoomOwner) SketchMemoryScript.m_Instance.ClearMemory(); bool successData = false; @@ -280,12 +282,26 @@ public async Task Disconnect() } public bool DoesRoomNameExist(string roomName) - { + { + bool roomExist = m_RoomData.Any(room => room.roomName == roomName); - if (roomExist) { isUserRoomOwner = false; } + // Room does not exist + if (!roomExist) + { + isUserRoomOwner = true; + return false; + } - return roomExist; + // Find the room with the given name + RoomData? room = m_RoomData.FirstOrDefault(r => r.roomName == roomName); + + // Room exists + RoomData r = (RoomData)room; + if (r.numPlayers == 0) isUserRoomOwner = true;// and is empty user becomes room owner + else isUserRoomOwner = false; // not empty user is not the room owner + + return true; } void OnRoomDataRefreshed(List rooms) @@ -371,6 +387,9 @@ void OnLocalPlayerJoined(int id, ITransientData playerData) { // the user is the room owner if is the firt to get in isUserRoomOwner = m_Manager.GetPlayerCount() == 1 ? true : false; + // if not room owner clear scene + if (!isUserRoomOwner) SketchMemoryScript.m_Instance.ClearMemory(); + m_LocalPlayer = playerData; m_LocalPlayer.PlayerId = id; From 0ad6d4f6e8563e133791371ff57a3a85ed8295df Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Thu, 28 Nov 2024 20:23:23 +0000 Subject: [PATCH 109/174] Implementing Reference StrokeData to BrushStrokeCommands Implementing WeakReference between Strokes and BrushStrokeComma. This allows the garbage collector to collect the BrushStrokeCommand if it's no longer in use elsewhere. --- Assets/Scripts/Commands/BrushStrokeCommand.cs | 2 ++ Assets/Scripts/StrokeData.cs | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/Assets/Scripts/Commands/BrushStrokeCommand.cs b/Assets/Scripts/Commands/BrushStrokeCommand.cs index 1c52ac4750..586f5dca07 100644 --- a/Assets/Scripts/Commands/BrushStrokeCommand.cs +++ b/Assets/Scripts/Commands/BrushStrokeCommand.cs @@ -32,6 +32,7 @@ public BrushStrokeCommand(Stroke stroke, StencilWidget widget = null, float lineLength = -1, BaseCommand parent = null) : base(parent) { m_Stroke = stroke; + m_Stroke.Command = this; m_Widget = widget; m_LineLength_CS = lineLength; } @@ -42,6 +43,7 @@ public BrushStrokeCommand(Stroke stroke, Guid existingGuid, int timestamp, Stenc : base(existingGuid, timestamp, parent) { m_Stroke = stroke; + m_Stroke.Command = this; m_Widget = widget; m_LineLength_CS = lineLength; } diff --git a/Assets/Scripts/StrokeData.cs b/Assets/Scripts/StrokeData.cs index 26ec4f41a3..d827dffabc 100644 --- a/Assets/Scripts/StrokeData.cs +++ b/Assets/Scripts/StrokeData.cs @@ -35,6 +35,22 @@ public class StrokeData public int m_Seed; public SketchGroupTag m_Group = SketchGroupTag.None; + // Reference the BrushStrokeCommand that created this stroke with a WeakReference. + // This allows the garbage collector to collect the BrushStrokeCommand if it's no + // longer in use elsewhere. + [NonSerialized] private WeakReference m_Command; + public BrushStrokeCommand Command + { + get + { + if (m_Command != null && m_Command.TryGetTarget(out var command)) + return command; + return null; + } + set { m_Command = new WeakReference(value); } + } + + /// This creates a copy of the given stroke. public StrokeData(StrokeData existing = null) { From 993812fc6ed36ca70afad097ea852b10a04e3ac1 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Thu, 28 Nov 2024 20:27:27 +0000 Subject: [PATCH 110/174] Identify Command-less Strokes We have implemented the GetStrokesWithoutCommand() method in SketchMemoryScript.cs. This enhancement enables us to distinguish between strokes in the memory list that were loaded from deserialization (i.e., from a file) and those that were interactively generated during the current session. By identifying strokes without referenced brush stroke command, we can better understand the origin of each stroke and manage them accordingly. --- Assets/Scripts/SketchMemoryScript.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Assets/Scripts/SketchMemoryScript.cs b/Assets/Scripts/SketchMemoryScript.cs index ff30cfbe3a..ff4d95c97b 100644 --- a/Assets/Scripts/SketchMemoryScript.cs +++ b/Assets/Scripts/SketchMemoryScript.cs @@ -990,6 +990,14 @@ public static int AllStrokesCount() return m_Instance.m_MemoryList.Count(); } + public List GetStrokesWithoutCommand() + { + return m_MemoryList + .Where(stroke => stroke.Command == null) + .OrderBy(s => s.HeadTimestampMs) + .ToList(); + } + public static void InitUndoObject(BaseBrushScript rBrushScript) { rBrushScript.CloneAsUndoObject(); From 7907807d016a7364c010b8d04bf159813660e808 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Thu, 28 Nov 2024 20:33:08 +0000 Subject: [PATCH 111/174] Implementing command-less Strokes Synchronization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The newly implemented GetStrokesWithoutCommand() function in SketchMemoryScript.cs significantly improves stroke synchronization. This function not only retrieves all commands from the various stacks but also identifies strokes that lack associated commands—specifically, those that have been deserialized or loaded from files. By distinguishing these command-less strokes, we can effectively share them with peers, avoiding redundances. --- .../Scripts/Multiplayer/MultiplayerManager.cs | 67 ++++++++++++++++++- .../Scripts/Multiplayer/Photon/PhotonRPC.cs | 2 + 2 files changed, 66 insertions(+), 3 deletions(-) diff --git a/Assets/Scripts/Multiplayer/MultiplayerManager.cs b/Assets/Scripts/Multiplayer/MultiplayerManager.cs index 5d7bebf7b9..d3ccba1a2a 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerManager.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerManager.cs @@ -401,7 +401,7 @@ void OnRemotePlayerJoined(int id, ITransientData playerData) m_RemotePlayers.Add(playerData); //if i am the room owner I should send the command history - if (isUserRoomOwner) StartCoroutine(SendCommandHistory()); + if (isUserRoomOwner) StartCoroutine(SendStrokesAndCommandHistory()); } @@ -511,13 +511,33 @@ private void OnConnectionHandlerDisconnected() Disconnected?.Invoke();// Invoke the Disconnected event } - private IEnumerator SendCommandHistory() + private IEnumerator SendStrokesAndCommandHistory() { + + // Retrieve Strokes that do not have a command associated with them + List strokesWithoutCommand = SketchMemoryScript.m_Instance.GetStrokesWithoutCommand(); + // Retrieve all commands from staks IEnumerable commands = SketchMemoryScript.m_Instance.GetAllOperations(); int counter = 0; - foreach (BaseCommand command in commands) + // Determine the first command timestamp + int firstCommandTimestamp = int.MaxValue; // Default to MaxValue if no commands are present + if (commands.Any()) + { + var firstCommand = commands.First(); + if (firstCommand.NetworkTimestamp.HasValue) firstCommandTimestamp = firstCommand.NetworkTimestamp.Value; + } + + // Generate a list of brush stroke commands from the list of strokes without commands + List brushCommands = CreateBrushStrokeCommands(strokesWithoutCommand, firstCommandTimestamp); + + // Merge + IEnumerable allCommands = brushCommands.Concat(commands); + + // Send commands in the existing command staks + counter = 0; + foreach (BaseCommand command in allCommands) { OnCommandPerformed(command); counter++; @@ -530,6 +550,47 @@ private IEnumerator SendCommandHistory() } } + private List CreateBrushStrokeCommands(List strokes, int LastTimestamp) + { + List commands = new List(); + + // Handle empty list + if (strokes == null || strokes.Count == 0) + { + return commands; + } + + // Ensure strokes are ordered by their timestamps + strokes = strokes.OrderBy(s => s.HeadTimestampMs).ToList(); + + uint earliestStrokeTimestampMs = strokes.First().HeadTimestampMs; + uint latestStrokeTimestampMs = strokes.Last().TailTimestampMs; + uint totalStrokeTimeMs = latestStrokeTimestampMs - earliestStrokeTimestampMs; + + if (totalStrokeTimeMs == 0) totalStrokeTimeMs = 1; + + foreach (var stroke in strokes) + { + // Calculate timestamp + uint strokeTimeMs = stroke.HeadTimestampMs - earliestStrokeTimestampMs; + + // Use long to prevent integer overflow + long numerator = (long)strokeTimeMs * (LastTimestamp - 1); + int timestamp = (int)(numerator / totalStrokeTimeMs); + + // Ensure timestamp is less than firstCommandTimestamp + if (timestamp >= LastTimestamp) + { + timestamp = LastTimestamp - 1; + } + + BrushStrokeCommand command = new BrushStrokeCommand(stroke, Guid.NewGuid(), timestamp); + commands.Add(command); + } + + return commands; + } + public void StartSpeaking() { m_VoiceManager?.StartSpeaking(); diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs b/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs index 10fa994fa6..ab144fd0db 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs @@ -301,6 +301,8 @@ public static void RPC_DeleteStroke(NetworkRunner runner, int seed, Guid command { if (CheckifCommandGuidIsInStack(commandGuid)) return; + // TODO : implment GUID for strokesdata. + // The range of int is large (-2,147,483,648 to 2,147,483,647), but collisions are still possible. var foundStroke = SketchMemoryScript.m_Instance.GetMemoryList.Where(x => x.m_Seed == seed).First(); if (foundStroke != null) From d8304b1fdcb6326693feafae4ef11f2518d4d521 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Fri, 29 Nov 2024 12:06:13 +0000 Subject: [PATCH 112/174] Implement Estimation of Command-to-Network-Message Conversion Some commands must be split into multiple network messages due to payload size limitations per message. In this changeset, we implemented a function to estimate the number of network messages required to transmit each command. This enhancement improves the efficiency of the coroutine responsible for synchronizing the scene with new users. --- Assets/Scenes/Main.unity | 25 +++++-------- .../Scripts/Multiplayer/MultiplayerManager.cs | 36 +++++++++++++++---- Assets/Scripts/StrokeData.cs | 2 +- 3 files changed, 39 insertions(+), 24 deletions(-) diff --git a/Assets/Scenes/Main.unity b/Assets/Scenes/Main.unity index b0fdb24eee..448cef02e6 100644 --- a/Assets/Scenes/Main.unity +++ b/Assets/Scenes/Main.unity @@ -3654,7 +3654,7 @@ MonoBehaviour: m_SdkMode: 0 m_AutoProfile: 0 m_AutoProfileWaitTime: 10 - Secrets: {fileID: 11400000, guid: 3196e9bd8aa430e42af6fe061aa7323b, type: 2} + Secrets: {fileID: 11400000, guid: 2958d93356c83c5409c5fc930c42e6fc, type: 2} m_SketchFiles: [] DisableAccountLogins: 0 m_OdsNumFrames: 0 @@ -15212,8 +15212,16 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: b989d2f19099ac044b955d3a6a62dea9, type: 3} m_Name: m_EditorClassIdentifier: + batchSize: 60 + delayBetweenBatches: 0.05 m_MultiplayerType: 2 CurrentRoomName: + data: + roomName: + roomPassword: + private: 0 + maxPlayers: 0 + voiceDisabled: 0 --- !u!114 &1052269835 MonoBehaviour: m_ObjectHideFlags: 0 @@ -15296,21 +15304,6 @@ MonoBehaviour: EnableLobbyStatistics: 0 NetworkLogging: 1 ShowSettings: 0 - AppSettings: - AppIdFusion: - AppIdChat: - AppIdVoice: - AppVersion: - UseNameServer: 1 - FixedRegion: - Server: - Port: 0 - ProxyServer: - Protocol: 0 - EnableProtocolFallback: 1 - AuthMode: 0 - EnableLobbyStatistics: 0 - NetworkLogging: 1 UseVoiceAppSettings: 0 --- !u!1 &1057179852 GameObject: diff --git a/Assets/Scripts/Multiplayer/MultiplayerManager.cs b/Assets/Scripts/Multiplayer/MultiplayerManager.cs index d3ccba1a2a..266ab6e784 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerManager.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerManager.cs @@ -36,8 +36,8 @@ public enum MultiplayerType public class MultiplayerManager : MonoBehaviour { - public int batchSize = 1; - public float delayBetweenBatches = 0.25f; + public int batchSize = 60; + public float delayBetweenBatches = 0.05f; public static MultiplayerManager m_Instance; public MultiplayerType m_MultiplayerType; @@ -282,7 +282,7 @@ public async Task Disconnect() } public bool DoesRoomNameExist(string roomName) - { + { bool roomExist = m_RoomData.Any(room => room.roomName == roomName); @@ -511,6 +511,8 @@ private void OnConnectionHandlerDisconnected() Disconnected?.Invoke();// Invoke the Disconnected event } + private const int MAX_MESSAGES_PER_TICK = 1; // Adjust based on network constraints + private IEnumerator SendStrokesAndCommandHistory() { @@ -535,18 +537,20 @@ private IEnumerator SendStrokesAndCommandHistory() // Merge IEnumerable allCommands = brushCommands.Concat(commands); - // Send commands in the existing command staks + // Send commands counter = 0; foreach (BaseCommand command in allCommands) { - OnCommandPerformed(command); - counter++; + int estimatedMessages = EstimateMessagesForCommand(command); - if (counter % batchSize == 0) // Using batch size for pacing; consider calculating payload size as an improvement + if (counter + estimatedMessages > batchSize) // Using batch size for pacing; consider calculating payload size as an improvement { yield return null; yield return new WaitForSeconds(delayBetweenBatches); } + + OnCommandPerformed(command); + counter += estimatedMessages; } } @@ -591,6 +595,24 @@ private List CreateBrushStrokeCommands(List strokes, return commands; } + private int EstimateMessagesForCommand(BaseCommand command) + { + switch (command) + { + case BrushStrokeCommand strokeCommand: + int totalControlPoints = strokeCommand.m_Stroke.m_ControlPoints.Length; + if (totalControlPoints <= NetworkingConstants.MaxControlPointsPerChunk) return 1; + int splits = (int)Math.Ceiling((double)totalControlPoints / NetworkingConstants.MaxControlPointsPerChunk); + return 2 + (splits - 1); + case DeleteStrokeCommand: + case SwitchEnvironmentCommand: + case BaseCommand: + return 1; + default: + return 0; + } + } + public void StartSpeaking() { m_VoiceManager?.StartSpeaking(); diff --git a/Assets/Scripts/StrokeData.cs b/Assets/Scripts/StrokeData.cs index d827dffabc..f5dc088eba 100644 --- a/Assets/Scripts/StrokeData.cs +++ b/Assets/Scripts/StrokeData.cs @@ -47,7 +47,7 @@ public BrushStrokeCommand Command return command; return null; } - set { m_Command = new WeakReference(value); } + set { m_Command = new WeakReference(value); } } From 1094b9896f18a0bb88df919f0b5fd8cf9500c8dc Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Mon, 2 Dec 2024 16:05:48 +0000 Subject: [PATCH 113/174] Reducing log level for pun fusion and voice --- Assets/Scripts/Multiplayer/Photon/PhotonManager.cs | 2 ++ Assets/Scripts/Multiplayer/Photon/PhotonVoiceManager.cs | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs index 90e5283c65..1485f41eaa 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs @@ -72,6 +72,8 @@ public async Task Init() m_Runner.ProvideInput = true; m_Runner.AddCallbacks(this); + Log.LogLevel = Fusion.LogType.Error; + } catch (Exception ex) { diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonVoiceManager.cs b/Assets/Scripts/Multiplayer/Photon/PhotonVoiceManager.cs index 27ddb492fb..a9a1e5a7a2 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonVoiceManager.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonVoiceManager.cs @@ -14,8 +14,8 @@ #if PHOTON_UNITY_NETWORKING && PHOTON_VOICE_DEFINED -using Fusion; using OpenBrush.Multiplayer; +using Photon.Pun; using Photon.Realtime; using Photon.Voice.Unity; using System; @@ -50,6 +50,8 @@ public async Task Init() State = ConnectionState.INITIALISING; m_VoiceConnection = GameObject.FindFirstObjectByType(); if (m_VoiceConnection == null) throw new Exception("[PhotonVoiceManager] VoiceConnection component not found in scene"); + PhotonNetwork.LogLevel = PunLogLevel.ErrorsOnly; + m_VoiceConnection.VoiceLogger.LogLevel = Photon.Voice.LogLevel.Error; m_VoiceConnection.Settings = new AppSettings { From d7eb82097cb5a173dec89b5fdb5b5d620d12f91e Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Mon, 2 Dec 2024 17:09:26 +0000 Subject: [PATCH 114/174] Limiting sequential execution of the SendStrokesAndCommandHistory coroutine to one queued instance Overlapping coroutines could result in network packet loss and cause users to receive an incomplete scene. This commit prevents overlapping execution of SendStrokesAndCommandHistory by allowing only one active coroutine and one queued instance. This ensures that users joining during an ongoing or queued execution receive a complete history of commands. --- .../Scripts/Multiplayer/MultiplayerManager.cs | 88 +++++++++++-------- Assets/Scripts/SketchMemoryScript.cs | 4 + 2 files changed, 55 insertions(+), 37 deletions(-) diff --git a/Assets/Scripts/Multiplayer/MultiplayerManager.cs b/Assets/Scripts/Multiplayer/MultiplayerManager.cs index 266ab6e784..0534c63054 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerManager.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerManager.cs @@ -58,6 +58,8 @@ public class MultiplayerManager : MonoBehaviour public event Action UserInfoStateUpdated; private List m_RoomData = new List(); private double? m_NetworkOffsetTimestamp = null; + private bool _isWaiting = false; + private bool _isSendingCommandHistory = false; ulong myOculusUserId; @@ -515,54 +517,70 @@ private void OnConnectionHandlerDisconnected() private IEnumerator SendStrokesAndCommandHistory() { + // Allow only one active coroutine and one queued: + // If already waiting, ignore this call. + if (_isWaiting) yield break; - // Retrieve Strokes that do not have a command associated with them - List strokesWithoutCommand = SketchMemoryScript.m_Instance.GetStrokesWithoutCommand(); - // Retrieve all commands from staks - IEnumerable commands = SketchMemoryScript.m_Instance.GetAllOperations(); - - int counter = 0; - - // Determine the first command timestamp - int firstCommandTimestamp = int.MaxValue; // Default to MaxValue if no commands are present - if (commands.Any()) + if (_isSendingCommandHistory) { - var firstCommand = commands.First(); - if (firstCommand.NetworkTimestamp.HasValue) firstCommandTimestamp = firstCommand.NetworkTimestamp.Value; + _isWaiting = true; + while (_isSendingCommandHistory) + { + yield return null; + + } + _isWaiting = false; } - // Generate a list of brush stroke commands from the list of strokes without commands - List brushCommands = CreateBrushStrokeCommands(strokesWithoutCommand, firstCommandTimestamp); + _isSendingCommandHistory = true; - // Merge - IEnumerable allCommands = brushCommands.Concat(commands); - // Send commands - counter = 0; - foreach (BaseCommand command in allCommands) + try { - int estimatedMessages = EstimateMessagesForCommand(command); + // Retrieve Strokes that do not have a command associated with them + List strokesWithoutCommand = SketchMemoryScript.m_Instance.GetStrokesWithoutCommand(); + // Retrieve all commands from staks + IEnumerable commands = SketchMemoryScript.m_Instance.GetAllOperations(); + + int counter = 0; - if (counter + estimatedMessages > batchSize) // Using batch size for pacing; consider calculating payload size as an improvement + // Determine the first command timestamp + int firstCommandTimestamp = 0; + if (commands.Any()) { - yield return null; - yield return new WaitForSeconds(delayBetweenBatches); + var firstCommand = commands.First(); + if (firstCommand.NetworkTimestamp.HasValue) firstCommandTimestamp = firstCommand.NetworkTimestamp.Value; } - OnCommandPerformed(command); - counter += estimatedMessages; + // Generate a list of brush stroke commands from the list of strokes without commands + CreateBrushStrokeCommands(strokesWithoutCommand, firstCommandTimestamp); + + // Send commands + counter = 0; + foreach (BaseCommand command in commands) + { + int estimatedMessages = EstimateMessagesForCommand(command); + + if (counter + estimatedMessages > batchSize) + { + yield return null; + yield return new WaitForSeconds(delayBetweenBatches); + } + + OnCommandPerformed(command); + counter += estimatedMessages; + } + } + finally + { + _isSendingCommandHistory = false; } } - private List CreateBrushStrokeCommands(List strokes, int LastTimestamp) + private void CreateBrushStrokeCommands(List strokes, int LastTimestamp) { - List commands = new List(); - // Handle empty list - if (strokes == null || strokes.Count == 0) - { - return commands; - } + if (strokes == null || strokes.Count == 0) return; // Ensure strokes are ordered by their timestamps strokes = strokes.OrderBy(s => s.HeadTimestampMs).ToList(); @@ -589,10 +607,8 @@ private List CreateBrushStrokeCommands(List strokes, } BrushStrokeCommand command = new BrushStrokeCommand(stroke, Guid.NewGuid(), timestamp); - commands.Add(command); + SketchMemoryScript.m_Instance.AddCommandToNetworkStack(command); } - - return commands; } private int EstimateMessagesForCommand(BaseCommand command) @@ -670,8 +686,6 @@ private void UpdateSketchMemoryScriptTimeOffset(ConnectionState state) // Capture the current sketch time as the base offset for network synchronization m_NetworkOffsetTimestamp = (int)(App.Instance.CurrentSketchTime * 1000); SketchMemoryScript.m_Instance.SetTimeOffsetToAllStacks((int)m_NetworkOffsetTimestamp); - Debug.Log($"Network offset timestamp set: {m_NetworkOffsetTimestamp}s"); - } } diff --git a/Assets/Scripts/SketchMemoryScript.cs b/Assets/Scripts/SketchMemoryScript.cs index ff4d95c97b..d83016f933 100644 --- a/Assets/Scripts/SketchMemoryScript.cs +++ b/Assets/Scripts/SketchMemoryScript.cs @@ -204,6 +204,10 @@ public IEnumerable GetAllOperations() return allCommands.OrderBy(command => command.NetworkTimestamp); } + public void AddCommandToNetworkStack(BaseCommand command) + { + m_NetworkStack.Push(command); + } public Stroke GetStrokeAtIndex(int index) { From 9b25897bb90fb7ac270d1650a886f57aac358b17 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Mon, 2 Dec 2024 19:07:59 +0000 Subject: [PATCH 115/174] Test reliable for brush commands --- .../Scripts/Multiplayer/Photon/PhotonRPC.cs | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs b/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs index ab144fd0db..a49bedd401 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs @@ -164,7 +164,7 @@ public static void CreateBrushStroke(Stroke stroke, Guid commandGuid, int times } #region RPCS - [Rpc(InvokeLocal = false)] + [Rpc(InvokeLocal = false, Channel = RpcChannel.Reliable)] public static void RPC_SyncToSharedAnchor(NetworkRunner runner, string uuid) { #if OCULUS_SUPPORTED @@ -172,7 +172,7 @@ public static void RPC_SyncToSharedAnchor(NetworkRunner runner, string uuid) #endif // OCULUS_SUPPORTED } - [Rpc(InvokeLocal = false)] + [Rpc(InvokeLocal = false, Channel = RpcChannel.Reliable)] public static void RPC_PerformCommand(NetworkRunner runner, string commandName, string guid, string[] data) { Debug.Log($"Command recieved: {commandName}"); @@ -198,7 +198,7 @@ public static void RPC_PerformCommand(NetworkRunner runner, string commandName, } } - [Rpc(InvokeLocal = false)] + [Rpc(InvokeLocal = false, Channel = RpcChannel.Reliable)] public static void RPC_Undo(NetworkRunner runner, string commandName) { if (SketchMemoryScript.m_Instance.CanUndo()) @@ -207,7 +207,7 @@ public static void RPC_Undo(NetworkRunner runner, string commandName) } } - [Rpc(InvokeLocal = false)] + [Rpc(InvokeLocal = false, Channel = RpcChannel.Reliable)] public static void RPC_Redo(NetworkRunner runner, string commandName) { if (SketchMemoryScript.m_Instance.CanRedo()) @@ -216,7 +216,7 @@ public static void RPC_Redo(NetworkRunner runner, string commandName) } } - [Rpc(InvokeLocal = false)] + [Rpc(InvokeLocal = false, Channel = RpcChannel.Reliable)] public static void RPC_BaseCommand(NetworkRunner runner, Guid commandGuid, Guid parentGuid = default, int childCount = 0) { if (CheckifCommandGuidIsInStack(commandGuid)) return; @@ -228,7 +228,7 @@ public static void RPC_BaseCommand(NetworkRunner runner, Guid commandGuid, Guid AddPendingCommand(() => {}, commandGuid, parentGuid, command, childCount); } - [Rpc(InvokeLocal = false)] + [Rpc(InvokeLocal = false, Channel = RpcChannel.Reliable)] public static void RPC_BrushStrokeFull(NetworkRunner runner, NetworkedStroke strokeData, Guid commandGuid, int timestamp, Guid parentGuid = default, int childCount = 0) { @@ -239,7 +239,7 @@ public static void RPC_BrushStrokeFull(NetworkRunner runner, NetworkedStroke str CreateBrushStroke(decode, commandGuid, timestamp , parentGuid, childCount); } - [Rpc(InvokeLocal = false)] + [Rpc(InvokeLocal = false, Channel = RpcChannel.Reliable)] public static void RPC_BrushStrokeBegin(NetworkRunner runner, Guid id, NetworkedStroke strokeData, int finalLength) { var decode = NetworkedStroke.ToStroke(strokeData); @@ -259,7 +259,7 @@ public static void RPC_BrushStrokeBegin(NetworkRunner runner, Guid id, Networked m_inProgressStrokes[id] = decode; } - [Rpc(InvokeLocal = false)] + [Rpc(InvokeLocal = false, Channel = RpcChannel.Reliable)] public static void RPC_BrushStrokeContinue(NetworkRunner runner, Guid id, int offset, NetworkedControlPoint[] controlPoints, bool[] dropPoints) { if(!m_inProgressStrokes.ContainsKey(id)) @@ -277,7 +277,7 @@ public static void RPC_BrushStrokeContinue(NetworkRunner runner, Guid id, int of } } - [Rpc(InvokeLocal = false)] + [Rpc(InvokeLocal = false, Channel = RpcChannel.Reliable)] public static void RPC_BrushStrokeComplete(NetworkRunner runner, Guid id, Guid commandGuid, int timestamp, Guid parentGuid = default, int childCount = 0) { @@ -296,7 +296,7 @@ public static void RPC_BrushStrokeComplete(NetworkRunner runner, Guid id, Guid c m_inProgressStrokes.Remove(id); } - [Rpc(InvokeLocal = false)] + [Rpc(InvokeLocal = false, Channel = RpcChannel.Reliable)] public static void RPC_DeleteStroke(NetworkRunner runner, int seed, Guid commandGuid, int timestamp, Guid parentGuid = default, int childCount = 0) { if (CheckifCommandGuidIsInStack(commandGuid)) return; @@ -318,7 +318,7 @@ public static void RPC_DeleteStroke(NetworkRunner runner, int seed, Guid command } } - [Rpc(InvokeLocal = false)] + [Rpc(InvokeLocal = false, Channel = RpcChannel.Reliable)] public static void RPC_SwitchEnvironment(NetworkRunner runner, Guid environmentGuid, Guid commandGuid, int timestamp, Guid parentGuid = default, int childCount = 0) { if (CheckifCommandGuidIsInStack(commandGuid)) return; From 356fd2282e720e3c88dfa4e13b7ffc384dcc6f03 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Tue, 3 Dec 2024 09:39:04 +0000 Subject: [PATCH 116/174] Disable edit room and nickname buttons when in room Disable edit room and nickname buttons when in room --- Assets/Prefabs/Panels/MultiplayerPanel.prefab | 4 ++-- Assets/Scripts/SketchControlsScript.cs | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Assets/Prefabs/Panels/MultiplayerPanel.prefab b/Assets/Prefabs/Panels/MultiplayerPanel.prefab index c265787c58..bf248b3074 100644 --- a/Assets/Prefabs/Panels/MultiplayerPanel.prefab +++ b/Assets/Prefabs/Panels/MultiplayerPanel.prefab @@ -4458,7 +4458,7 @@ MonoBehaviour: m_WaitForCompletion: 0 m_LocalVariables: [] m_ToggleOnTexture: {fileID: 0} - m_AllowUnavailable: 0 + m_AllowUnavailable: 1 m_LinkedUIObject: {fileID: 0} m_CommandIgnored: 0 references: @@ -4829,7 +4829,7 @@ MonoBehaviour: m_WaitForCompletion: 0 m_LocalVariables: [] m_ToggleOnTexture: {fileID: 0} - m_AllowUnavailable: 0 + m_AllowUnavailable: 1 m_LinkedUIObject: {fileID: 0} m_CommandIgnored: 0 references: diff --git a/Assets/Scripts/SketchControlsScript.cs b/Assets/Scripts/SketchControlsScript.cs index eb7340b984..4c2e00005c 100644 --- a/Assets/Scripts/SketchControlsScript.cs +++ b/Assets/Scripts/SketchControlsScript.cs @@ -5089,8 +5089,9 @@ public bool IsCommandAvailable(GlobalCommands rEnum, int iParam = -1) case GlobalCommands.MultiplayerLeaveRoom: return MultiplayerManager.m_Instance.CanLeaveRoom(); case GlobalCommands.Sketchbook: - return !(MultiplayerManager.m_Instance.State == ConnectionState.IN_ROOM); case GlobalCommands.SketchbookMenu: + case GlobalCommands.EditMultiplayerNickName: + case GlobalCommands.EditMultiplayerRoomName: return !(MultiplayerManager.m_Instance.State == ConnectionState.IN_ROOM); } return true; From 38722e9b9b222b788ed6106e123210ba77bb7b6c Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Tue, 3 Dec 2024 10:02:54 +0000 Subject: [PATCH 117/174] Add a button to erase all text Add a button to the keyboard pop up to erase all text --- .../PopUps/PopUpWindow_Keyboard.prefab | 236 +++++++++++++++++- .../Strings/Strings Shared Data.asset | 4 + .../Localization/Strings/Strings_de.asset | 4 + .../Localization/Strings/Strings_en.asset | 4 + .../Localization/Strings/Strings_es.asset | 4 + .../Localization/Strings/Strings_fr.asset | 4 + .../Localization/Strings/Strings_ja.asset | 4 + .../Localization/Strings/Strings_ko.asset | 4 + .../Localization/Strings/Strings_zh.asset | 4 + 9 files changed, 259 insertions(+), 9 deletions(-) diff --git a/Assets/Prefabs/PopUps/PopUpWindow_Keyboard.prefab b/Assets/Prefabs/PopUps/PopUpWindow_Keyboard.prefab index 64a0e8461e..03e12c60cd 100644 --- a/Assets/Prefabs/PopUps/PopUpWindow_Keyboard.prefab +++ b/Assets/Prefabs/PopUps/PopUpWindow_Keyboard.prefab @@ -27,6 +27,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 293902843205586148} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 1.251, y: -0.752, z: -0.018} m_LocalScale: {x: 0.29999998, y: 0.29999998, z: 0.29999998} @@ -35,7 +36,6 @@ Transform: - {fileID: 1534559809702098178} - {fileID: 1332166645596930261} m_Father: {fileID: 8645625735778197046} - m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &6720148685990472896 MeshFilter: @@ -95,9 +95,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 293902843205586148} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1.0000001, y: 1, z: 0.01} m_Center: {x: 0, y: 0, z: -0.01} --- !u!114 &5466685659915710089 @@ -178,13 +186,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 2617002625324559686} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 0.65, y: 0.65, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 334991469541287722} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &1778020965354640619 MeshFilter: @@ -236,6 +244,176 @@ MeshRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_AdditionalVertexStreams: {fileID: 0} +--- !u!1 &4765895928551080905 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1914744345865055709} + - component: {fileID: 2805378110410830168} + - component: {fileID: 3270546546881808181} + - component: {fileID: 5754635738034065764} + - component: {fileID: 8804208351212219551} + m_Layer: 16 + m_Name: Button_ClearText + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1914744345865055709 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4765895928551080905} + serializedVersion: 2 + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 1.023, y: 0.565, z: -0.018} + m_LocalScale: {x: 0.2, y: 0.2, z: 0.2} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 8645625735778197046} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &2805378110410830168 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4765895928551080905} + m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &3270546546881808181 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4765895928551080905} + 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: 3c8ca511828182747a0b79564892ec57, 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: 0 + 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!65 &5754635738034065764 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4765895928551080905} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1.0000001, y: 1, z: 0.01} + m_Center: {x: 0, y: 0, z: -0.01} +--- !u!114 &8804208351212219551 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4765895928551080905} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 07f172f1096366841bb9362060bb0095, type: 3} + m_Name: + m_EditorClassIdentifier: + m_DescriptionType: 0 + m_DescriptionYOffset: 0 + m_DescriptionText: + m_LocalizedDescription: + m_TableReference: + m_TableCollectionName: GUID:c84355079ab3f3e4f8f3812258805f86 + m_TableEntryReference: + m_KeyId: 307216698459103232 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_DescriptionTextExtra: + m_LocalizedDescriptionExtra: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_DescriptionActivateSpeed: 12 + m_DescriptionZScale: 1 + m_ButtonTexture: {fileID: 2800000, guid: 567e825788c120d499fb56ab4ce674f1, type: 3} + m_AtlasTexture: 1 + m_ToggleButton: 0 + m_LongPressReleaseButton: 0 + m_ButtonHasPressedAudio: 1 + m_ZAdjustHover: -0.02 + m_ZAdjustClick: 0.05 + m_HoverScale: 1.1 + m_HoverBoxColliderGrow: 0.2 + m_AddOverlay: 0 + m_Action: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 6543094775333332137} + m_TargetAssemblyTypeName: TiltBrush.KeyboardUI, Assembly-CSharp + m_MethodName: Clear + m_Mode: 1 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 0 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 2 + references: + version: 2 + RefIds: [] --- !u!1 &6530336952169636648 GameObject: m_ObjectHideFlags: 0 @@ -261,13 +439,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 6530336952169636648} + serializedVersion: 2 m_LocalRotation: {x: 0.7071068, y: -0, z: -0, w: 0.7071068} m_LocalPosition: {x: 0, y: 0, z: 0.025} m_LocalScale: {x: 0.79999995, y: 0.02, z: 0.8} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 334991469541287722} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 90, y: 0, z: 0} --- !u!33 &6179665678031837250 MeshFilter: @@ -344,13 +522,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8644332587409793350} + serializedVersion: 2 m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} m_LocalPosition: {x: -0, y: 0, z: 0} m_LocalScale: {x: 100, y: 100, z: 100} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 8645625735778197046} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &8610995081492312010 MeshFilter: @@ -428,6 +606,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8644332587479430734} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 3, z: 0} m_LocalScale: {x: 1.5, y: 1.5, z: 1.5} @@ -435,7 +614,6 @@ Transform: m_Children: - {fileID: 8645625735778197046} m_Father: {fileID: 0} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!114 &3095268576859284452 MonoBehaviour: @@ -479,9 +657,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8644332587479430734} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 2.5, y: 1.5, z: 0.01} m_Center: {x: 0, y: 0, z: -0.01} --- !u!114 &8533537593755566908 @@ -519,6 +705,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8644332587709734578} + serializedVersion: 2 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} @@ -528,8 +715,8 @@ Transform: - {fileID: 8645625740661137770} - {fileID: 6360790635351249531} - {fileID: 334991469541287722} + - {fileID: 1914744345865055709} m_Father: {fileID: 8645625734765961102} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &8644332588524666284 GameObject: @@ -556,13 +743,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8644332588524666284} + serializedVersion: 2 m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} m_LocalPosition: {x: -0, y: 0, z: 0} m_LocalScale: {x: 100, y: 100, z: 100} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 8645625735778197046} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &8610995081988753086 MeshFilter: @@ -619,12 +806,23 @@ PrefabInstance: m_ObjectHideFlags: 0 serializedVersion: 2 m_Modification: + serializedVersion: 3 m_TransformParent: {fileID: 8645625735778197046} m_Modifications: - target: {fileID: 1934243545717122, guid: ef7eb6dda2db50c4bbf6c682e4a21736, type: 3} propertyPath: m_Name value: Keyboard objectReference: {fileID: 0} + - target: {fileID: 199519161696781973, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_SizeDelta.x + value: 2200 + objectReference: {fileID: 0} + - target: {fileID: 199519161696781973, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_AnchoredPosition.x + value: -192 + objectReference: {fileID: 0} - target: {fileID: 224560323429904302, guid: ef7eb6dda2db50c4bbf6c682e4a21736, type: 3} propertyPath: m_Pivot.x @@ -730,7 +928,15 @@ PrefabInstance: propertyPath: m_LocalEulerAnglesHint.z value: 0 objectReference: {fileID: 0} + - target: {fileID: 8210763275035622327, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_FontData.m_HorizontalOverflow + value: 0 + objectReference: {fileID: 0} m_RemovedComponents: [] + m_RemovedGameObjects: [] + m_AddedGameObjects: [] + m_AddedComponents: [] m_SourcePrefab: {fileID: 100100000, guid: ef7eb6dda2db50c4bbf6c682e4a21736, type: 3} --- !u!224 &6360790635351249531 stripped RectTransform: @@ -738,3 +944,15 @@ RectTransform: type: 3} m_PrefabInstance: {fileID: 6583098329881755093} m_PrefabAsset: {fileID: 0} +--- !u!114 &6543094775333332137 stripped +MonoBehaviour: + m_CorrespondingSourceObject: {fileID: 114387715371604348, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + m_PrefabInstance: {fileID: 6583098329881755093} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 85e65ed8ff0832245869f1b6941e9eb1, type: 3} + m_Name: + m_EditorClassIdentifier: diff --git a/Assets/Settings/Localization/Strings/Strings Shared Data.asset b/Assets/Settings/Localization/Strings/Strings Shared Data.asset index b2940246d9..d002e87a90 100644 --- a/Assets/Settings/Localization/Strings/Strings Shared Data.asset +++ b/Assets/Settings/Localization/Strings/Strings Shared Data.asset @@ -3367,6 +3367,10 @@ MonoBehaviour: m_Key: MP_EDIT_NICK_NAME m_Metadata: m_Items: [] + - m_Id: 307216698459103232 + m_Key: erase text + m_Metadata: + m_Items: [] m_Metadata: m_Items: [] m_KeyGenerator: diff --git a/Assets/Settings/Localization/Strings/Strings_de.asset b/Assets/Settings/Localization/Strings/Strings_de.asset index 9bd616495d..4e21e371a5 100644 --- a/Assets/Settings/Localization/Strings/Strings_de.asset +++ b/Assets/Settings/Localization/Strings/Strings_de.asset @@ -3592,6 +3592,10 @@ MonoBehaviour: m_Localized: Spitznamen bearbeiten m_Metadata: m_Items: [] + - m_Id: 307216698459103232 + m_Localized: "Text l\xF6schen" + m_Metadata: + m_Items: [] references: version: 2 RefIds: [] diff --git a/Assets/Settings/Localization/Strings/Strings_en.asset b/Assets/Settings/Localization/Strings/Strings_en.asset index 20d77a0ee6..f46dfceb03 100644 --- a/Assets/Settings/Localization/Strings/Strings_en.asset +++ b/Assets/Settings/Localization/Strings/Strings_en.asset @@ -3564,6 +3564,10 @@ MonoBehaviour: m_Localized: Edit Nickname m_Metadata: m_Items: [] + - m_Id: 307216698459103232 + m_Localized: Erase text + m_Metadata: + m_Items: [] references: version: 2 RefIds: [] diff --git a/Assets/Settings/Localization/Strings/Strings_es.asset b/Assets/Settings/Localization/Strings/Strings_es.asset index 16e3da491c..af9f4122b1 100644 --- a/Assets/Settings/Localization/Strings/Strings_es.asset +++ b/Assets/Settings/Localization/Strings/Strings_es.asset @@ -3564,6 +3564,10 @@ MonoBehaviour: m_Localized: Editar Apodo m_Metadata: m_Items: [] + - m_Id: 307216698459103232 + m_Localized: Borrar texto + m_Metadata: + m_Items: [] references: version: 2 RefIds: [] diff --git a/Assets/Settings/Localization/Strings/Strings_fr.asset b/Assets/Settings/Localization/Strings/Strings_fr.asset index 2c69d699a7..18d8bc5e93 100644 --- a/Assets/Settings/Localization/Strings/Strings_fr.asset +++ b/Assets/Settings/Localization/Strings/Strings_fr.asset @@ -3533,6 +3533,10 @@ MonoBehaviour: m_Localized: Modifier le Pseudo m_Metadata: m_Items: [] + - m_Id: 307216698459103232 + m_Localized: Effacer le texte + m_Metadata: + m_Items: [] references: version: 2 RefIds: [] diff --git a/Assets/Settings/Localization/Strings/Strings_ja.asset b/Assets/Settings/Localization/Strings/Strings_ja.asset index 1fdc75cd80..dede3791ab 100644 --- a/Assets/Settings/Localization/Strings/Strings_ja.asset +++ b/Assets/Settings/Localization/Strings/Strings_ja.asset @@ -3486,6 +3486,10 @@ MonoBehaviour: m_Localized: "\u30CB\u30C3\u30AF\u30CD\u30FC\u30E0\u3092\u7DE8\u96C6" m_Metadata: m_Items: [] + - m_Id: 307216698459103232 + m_Localized: "\u30C6\u30AD\u30B9\u30C8\u3092\u6D88\u53BB" + m_Metadata: + m_Items: [] references: version: 2 RefIds: [] diff --git a/Assets/Settings/Localization/Strings/Strings_ko.asset b/Assets/Settings/Localization/Strings/Strings_ko.asset index d3e172626c..394d3438a4 100644 --- a/Assets/Settings/Localization/Strings/Strings_ko.asset +++ b/Assets/Settings/Localization/Strings/Strings_ko.asset @@ -3552,6 +3552,10 @@ MonoBehaviour: m_Localized: "\uB2C9\uB124\uC784 \uD3B8\uC9D1" m_Metadata: m_Items: [] + - m_Id: 307216698459103232 + m_Localized: "\uD14D\uC2A4\uD2B8 \uC9C0\uC6B0\uAE30" + m_Metadata: + m_Items: [] references: version: 2 RefIds: [] diff --git a/Assets/Settings/Localization/Strings/Strings_zh.asset b/Assets/Settings/Localization/Strings/Strings_zh.asset index cc3b09d528..ed52155add 100644 --- a/Assets/Settings/Localization/Strings/Strings_zh.asset +++ b/Assets/Settings/Localization/Strings/Strings_zh.asset @@ -3482,6 +3482,10 @@ MonoBehaviour: m_Localized: "\u7F16\u8F91\u6635\u79F0" m_Metadata: m_Items: [] + - m_Id: 307216698459103232 + m_Localized: "\u6E05\u9664\u6587\u672C" + m_Metadata: + m_Items: [] references: version: 2 RefIds: [] From 93790d0f72e8390c4a77d108a56a79e391e44d4a Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Tue, 3 Dec 2024 17:38:20 +0000 Subject: [PATCH 118/174] Implementing Sync Information for Users Joining Multiplayers Room Added synchronization info display when a user(non owners) joins the room. Updates synchronization percentage dynamically. Clears sync info upon completion. --- Assets/Scripts/InfoCardAnimation.cs | 13 +- .../Multiplayer/MultiplayerInterfaces.cs | 3 + .../Scripts/Multiplayer/MultiplayerManager.cs | 128 +++++++++++++++--- .../Multiplayer/Photon/PhotonManager.cs | 28 ++++ .../Scripts/Multiplayer/Photon/PhotonRPC.cs | 24 +++- Assets/Scripts/SketchControlsScript.cs | 12 ++ 6 files changed, 189 insertions(+), 19 deletions(-) diff --git a/Assets/Scripts/InfoCardAnimation.cs b/Assets/Scripts/InfoCardAnimation.cs index 66af47e99b..72185e26ce 100644 --- a/Assets/Scripts/InfoCardAnimation.cs +++ b/Assets/Scripts/InfoCardAnimation.cs @@ -284,5 +284,16 @@ void UpdateScale(float fScale) { transform.localScale = m_BaseScale * fScale; } + + public void UpdateHoldingDuration(float newDuration) + { + if (m_CurrentState == State.Holding) + { + m_HoldingStateDuration = newDuration; + m_StateTimer = 0.0f; + } + } + } -} // namespace TiltBrush + +}// namespace TiltBrush diff --git a/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs b/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs index 80a84213a1..c184a35ce9 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs @@ -40,6 +40,9 @@ public interface IDataConnectionHandler : IConnectionHandler Task UndoCommand(BaseCommand command); Task RedoCommand(BaseCommand command); Task RpcSyncToSharedAnchor(string uuid); + Task RpcStartSyncHistory(int id); + Task RpcSyncHistoryPercentage(int id, int exp, int snt); + Task RpcHistorySyncComplete(int id); event Action Disconnected; diff --git a/Assets/Scripts/Multiplayer/MultiplayerManager.cs b/Assets/Scripts/Multiplayer/MultiplayerManager.cs index 0534c63054..50b027bfbe 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerManager.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerManager.cs @@ -18,6 +18,12 @@ using System.Threading.Tasks; using UnityEngine; using System.Collections; +using Fusion; +using System.Security.Cryptography; +using TMPro; + + + #if OCULUS_SUPPORTED @@ -60,6 +66,9 @@ public class MultiplayerManager : MonoBehaviour private double? m_NetworkOffsetTimestamp = null; private bool _isWaiting = false; private bool _isSendingCommandHistory = false; + [HideInInspector] public int numberOfCommandsExpected = 0; + [HideInInspector] public int numberOfCommandsSent = 0; + private InfoCardAnimation infoCard; ulong myOculusUserId; @@ -403,7 +412,11 @@ void OnRemotePlayerJoined(int id, ITransientData playerData) m_RemotePlayers.Add(playerData); //if i am the room owner I should send the command history - if (isUserRoomOwner) StartCoroutine(SendStrokesAndCommandHistory()); + if (isUserRoomOwner) + { + StartSynchHistory(id); + StartCoroutine(SendStrokesAndCommandHistory(id)); + } } @@ -459,16 +472,41 @@ private async void OnCommandPerformed(BaseCommand command) await m_Manager.PerformCommand(command); } + } + + private async void StartSynchHistory(int id) + { + if (State == ConnectionState.IN_ROOM) + { + await m_Manager.RpcStartSyncHistory(id); + } + } + + private async void SynchHistoryPercentage(int id, int expected, int sent) + { + if (State == ConnectionState.IN_ROOM) + { + await m_Manager.RpcSyncHistoryPercentage(id, expected, sent); + } + } - // 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. + private async void SynchHistoryComplete(int id) + { + if (State == ConnectionState.IN_ROOM) + { + await m_Manager.RpcHistorySyncComplete(id); + } + } - // if (!success) - // { - // OutputWindowScript.m_Instance.CreateInfoCardAtController(InputManager.ControllerName.Brush, "Don't know how to network this action yet."); - // SketchMemoryScript.m_Instance.StepBack(false); - // } + private async void SynchHistoryCompleteForAll() + { + if (State == ConnectionState.IN_ROOM) + { + foreach (var player in m_RemotePlayers) + { + await m_Manager.RpcHistorySyncComplete(player.PlayerId); + } + } } private void OnCommandUndo(BaseCommand command) @@ -513,9 +551,7 @@ private void OnConnectionHandlerDisconnected() Disconnected?.Invoke();// Invoke the Disconnected event } - private const int MAX_MESSAGES_PER_TICK = 1; // Adjust based on network constraints - - private IEnumerator SendStrokesAndCommandHistory() + private IEnumerator SendStrokesAndCommandHistory(int id) { // Allow only one active coroutine and one queued: // If already waiting, ignore this call. @@ -542,10 +578,9 @@ private IEnumerator SendStrokesAndCommandHistory() // Retrieve all commands from staks IEnumerable commands = SketchMemoryScript.m_Instance.GetAllOperations(); - int counter = 0; // Determine the first command timestamp - int firstCommandTimestamp = 0; + int firstCommandTimestamp = int.MaxValue; if (commands.Any()) { var firstCommand = commands.First(); @@ -556,24 +591,30 @@ private IEnumerator SendStrokesAndCommandHistory() CreateBrushStrokeCommands(strokesWithoutCommand, firstCommandTimestamp); // Send commands - counter = 0; + int packetCounter = 0; + int counter = 0; foreach (BaseCommand command in commands) { int estimatedMessages = EstimateMessagesForCommand(command); - if (counter + estimatedMessages > batchSize) + if (packetCounter + estimatedMessages > batchSize) { yield return null; yield return new WaitForSeconds(delayBetweenBatches); } OnCommandPerformed(command); - counter += estimatedMessages; + packetCounter += estimatedMessages; + counter++; + SynchHistoryPercentage(id, commands.Count(), counter); } } finally { _isSendingCommandHistory = false; + + if (_isWaiting) SynchHistoryComplete(id); + else SynchHistoryCompleteForAll(); } } @@ -689,6 +730,59 @@ private void UpdateSketchMemoryScriptTimeOffset(ConnectionState state) } } + + public void DisplaySynchInfo() + { + OutputWindowScript.m_Instance.CreateInfoCardAtController( + InputManager.ControllerName.Brush, + "Synch Started!", + fPopScalar: 1.0f + ); + + RetrieveInfoCard(); + } + + public InfoCardAnimation RetrieveInfoCard() + { + // Find all active InfoCards in the scene + InfoCardAnimation[] allInfoCards = FindObjectsOfType(); + + foreach (var card in allInfoCards) + { + TextMeshPro textComponent = card.GetComponentInChildren(); + if (textComponent != null && textComponent.text.Contains("Synch")) + { + infoCard = card; + return card; + } + } + + return null; + } + + public void SynchInfoPercentageUpdate() + { + int percentage = (int)((float)SketchMemoryScript.AllStrokesCount() / MultiplayerManager.m_Instance.numberOfCommandsExpected * 100); + string text = $"Synch {percentage}%"; + + if (infoCard == null) infoCard = RetrieveInfoCard(); + + if (infoCard == null) DisplaySynchInfo(); + + infoCard.GetComponentInChildren().text = text; + infoCard.UpdateHoldingDuration(5f); + } + + + public void HideSynchInfo() + { + if (infoCard == null) infoCard = RetrieveInfoCard(); + + if (infoCard == null) DisplaySynchInfo(); + + infoCard.GetComponentInChildren().text = "Synch Ended!"; + infoCard.UpdateHoldingDuration(3.0f); + } } } diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs index 1485f41eaa..5cd4c9fff4 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs @@ -23,6 +23,7 @@ using Fusion.Photon.Realtime; using Fusion.Sockets; using TiltBrush; +using UnityEditor; @@ -114,6 +115,7 @@ public void CheckExistingUsers() } + #region IConnectionHandler Methods public async Task Connect() @@ -282,6 +284,32 @@ public async Task RpcSyncToSharedAnchor(string uuid) await Task.Yield(); return true; } + + public async Task RpcStartSyncHistory(int id) + { + PlayerRef playerRef = id; + PhotonRPC.RPC_StartHistorySync(m_Runner, id); + await Task.Yield(); + return true; + } + + public async Task RpcHistorySyncComplete(int id) + { + PlayerRef playerRef = id; + PhotonRPC.RPC_HistorySyncCompleted(m_Runner, id); + await Task.Yield(); + return true; + } + + + public async Task RpcSyncHistoryPercentage(int id, int exp, int snt) + { + PlayerRef playerRef = id; + PhotonRPC.RPC_HistoryPercentageUpdate(m_Runner, id, exp, snt); + await Task.Yield(); + return true; + } + #endregion #region Command Methods diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs b/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs index a49bedd401..8d0494b75e 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs @@ -21,6 +21,7 @@ using UnityEngine; using Fusion; using TiltBrush; +using static TiltBrush.SketchControlsScript; namespace OpenBrush.Multiplayer { @@ -128,7 +129,7 @@ private static void AddPendingCommand(Action preAction, Guid commandGuid, Guid p private static bool CheckifCommandGuidIsInStack(Guid commandGuid) { if (SketchMemoryScript.m_Instance.IsCommandInStack(commandGuid)) { - Debug.Log($"Command with Guid {commandGuid} already in stack."); + //Debug.Log($"Command with Guid {commandGuid} already in stack."); return true; } return false; @@ -339,6 +340,27 @@ public static void RPC_SwitchEnvironment(NetworkRunner runner, Guid environmentG } } + [Rpc(InvokeLocal = false)] + public static void RPC_StartHistorySync(NetworkRunner runner, [RpcTarget] PlayerRef targetPlayer ) + { + m_Instance.IssueGlobalCommand(GlobalCommands.DisplaySynchInfo); + } + + [Rpc(InvokeLocal = false)] + public static void RPC_HistoryPercentageUpdate(NetworkRunner runner, [RpcTarget] PlayerRef targetPlayer, int expected, int sent) + { + MultiplayerManager.m_Instance.numberOfCommandsExpected = expected; + MultiplayerManager.m_Instance.numberOfCommandsSent = sent; + m_Instance.IssueGlobalCommand(GlobalCommands.SynchInfoPercentageUpdate); + } + + [Rpc(InvokeLocal = false)] + public static void RPC_HistorySyncCompleted(NetworkRunner runner, [RpcTarget] PlayerRef targetPlayer) + { + m_Instance.IssueGlobalCommand(GlobalCommands.HideSynchInfo); + } + + #endregion } } diff --git a/Assets/Scripts/SketchControlsScript.cs b/Assets/Scripts/SketchControlsScript.cs index 4c2e00005c..c10d279ba6 100644 --- a/Assets/Scripts/SketchControlsScript.cs +++ b/Assets/Scripts/SketchControlsScript.cs @@ -155,6 +155,9 @@ public enum GlobalCommands MultiplayerConnect = 1007, MultiplayerDisconnect = 1008, EditMultiplayerNickName = 1009, + DisplaySynchInfo = 1010, + SynchInfoPercentageUpdate = 1011, + HideSynchInfo = 1012, RenameSketch = 5200, OpenLayerOptionsPopup = 5201, @@ -4857,6 +4860,15 @@ public void IssueGlobalCommand(GlobalCommands rEnum, int iParam1 = -1, PointerManager.m_Instance.EatLineEnabledInput(); SketchSurfacePanel.m_Instance.EatToolsInput(); break; + case GlobalCommands.DisplaySynchInfo: + MultiplayerManager.m_Instance.DisplaySynchInfo(); + break; + case GlobalCommands.SynchInfoPercentageUpdate: + MultiplayerManager.m_Instance.SynchInfoPercentageUpdate(); + break; + case GlobalCommands.HideSynchInfo: + MultiplayerManager.m_Instance.HideSynchInfo(); + break; case GlobalCommands.RepaintOptions: break; // Intentionally blank. case GlobalCommands.Null: break; // Intentionally blank. case GlobalCommands.MultiplayerPanelOptions: break; // Intentionally blank. From 987764b6bea7398c4fd72ccd3e6f797412c862e7 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Tue, 3 Dec 2024 18:23:45 +0000 Subject: [PATCH 119/174] Implement Environment Synchronization for Sketch-Loaded Environments SendStrokesAndCommandHistory lack environment updates loaded from files. This change ensures environments loaded from sketches are synchronized, improving scene consistency. --- Assets/Scripts/Multiplayer/MultiplayerManager.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Assets/Scripts/Multiplayer/MultiplayerManager.cs b/Assets/Scripts/Multiplayer/MultiplayerManager.cs index 50b027bfbe..2070aa995a 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerManager.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerManager.cs @@ -415,6 +415,7 @@ void OnRemotePlayerJoined(int id, ITransientData playerData) if (isUserRoomOwner) { StartSynchHistory(id); + SendCurrentTargetEnvironmentCommand(); StartCoroutine(SendStrokesAndCommandHistory(id)); } @@ -551,6 +552,17 @@ private void OnConnectionHandlerDisconnected() Disconnected?.Invoke();// Invoke the Disconnected event } + public void SendCurrentTargetEnvironmentCommand() + { + TiltBrush.Environment targetEnvironment = SceneSettings.m_Instance.GetDesiredPreset(); + + if (targetEnvironment != null) + { + SwitchEnvironmentCommand command = new SwitchEnvironmentCommand(targetEnvironment); + OnCommandPerformed(command); + } + } + private IEnumerator SendStrokesAndCommandHistory(int id) { // Allow only one active coroutine and one queued: From 2cdd501c41459c31e95d1b1af838b30e1cdbf172 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Wed, 4 Dec 2024 10:28:48 +0000 Subject: [PATCH 120/174] Move History Synchronization Logic to his own class --- Assets/Scenes/Main.unity | 19 +- .../HistorySynchronizationManager.cs | 231 ++++++++++++++++++ .../HistorySynchronizationManager.cs.meta | 11 + .../Scripts/Multiplayer/MultiplayerManager.cs | 227 ++--------------- .../Scripts/Multiplayer/Photon/PhotonRPC.cs | 4 +- Assets/Scripts/SketchControlsScript.cs | 6 +- 6 files changed, 281 insertions(+), 217 deletions(-) create mode 100644 Assets/Scripts/Multiplayer/HistorySynchronizationManager.cs create mode 100644 Assets/Scripts/Multiplayer/HistorySynchronizationManager.cs.meta diff --git a/Assets/Scenes/Main.unity b/Assets/Scenes/Main.unity index 448cef02e6..bd145b959c 100644 --- a/Assets/Scenes/Main.unity +++ b/Assets/Scenes/Main.unity @@ -15176,6 +15176,7 @@ GameObject: m_Component: - component: {fileID: 1052269831} - component: {fileID: 1052269832} + - component: {fileID: 1052269837} - component: {fileID: 1052269836} - component: {fileID: 1052269835} m_Layer: 0 @@ -15212,8 +15213,6 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: b989d2f19099ac044b955d3a6a62dea9, type: 3} m_Name: m_EditorClassIdentifier: - batchSize: 60 - delayBetweenBatches: 0.05 m_MultiplayerType: 2 CurrentRoomName: data: @@ -15305,6 +15304,22 @@ MonoBehaviour: NetworkLogging: 1 ShowSettings: 0 UseVoiceAppSettings: 0 +--- !u!114 &1052269837 +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: a751437a9d2b2724584655aaead0ddfe, type: 3} + m_Name: + m_EditorClassIdentifier: + batchSize: 60 + delayBetweenBatches: 0.05 + numberOfCommandsExpected: 0 + numberOfCommandsSent: 0 --- !u!1 &1057179852 GameObject: m_ObjectHideFlags: 0 diff --git a/Assets/Scripts/Multiplayer/HistorySynchronizationManager.cs b/Assets/Scripts/Multiplayer/HistorySynchronizationManager.cs new file mode 100644 index 0000000000..1523d89f06 --- /dev/null +++ b/Assets/Scripts/Multiplayer/HistorySynchronizationManager.cs @@ -0,0 +1,231 @@ +// 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.Linq; +using TiltBrush; +using TMPro; +using UnityEngine; + +namespace OpenBrush.Multiplayer +{ + public class HistorySynchronizationManager : MonoBehaviour + { + public static HistorySynchronizationManager m_Instance; + public int batchSize = 60; + public float delayBetweenBatches = 0.05f; + [HideInInspector] public int numberOfCommandsExpected = 0; + [HideInInspector] public int numberOfCommandsSent = 0; + + private bool _isWaiting = false; + private bool _isSendingCommandHistory = false; + private InfoCardAnimation infoCard; + + + void Awake() + { + m_Instance = this; + } + + + public void StartSyncronizationForUser(int id) + { + + StartSynchHistory(id); + SendCurrentTargetEnvironmentCommand(); + StartCoroutine(SendStrokesAndCommandHistory(id)); + } + + #region Syncronization Logic + public void SendCurrentTargetEnvironmentCommand() + { + TiltBrush.Environment targetEnvironment = SceneSettings.m_Instance.GetDesiredPreset(); + + if (targetEnvironment != null) + { + SwitchEnvironmentCommand command = new SwitchEnvironmentCommand(targetEnvironment); + MultiplayerManager.m_Instance.OnCommandPerformed(command); + } + } + + public IEnumerator SendStrokesAndCommandHistory(int id) + { + if (_isWaiting) yield break; + + if (_isSendingCommandHistory) + { + _isWaiting = true; + while (_isSendingCommandHistory) + { + yield return null; + } + _isWaiting = false; + } + + _isSendingCommandHistory = true; + + + List strokesWithoutCommand = SketchMemoryScript.m_Instance.GetStrokesWithoutCommand(); + IEnumerable commands = SketchMemoryScript.m_Instance.GetAllOperations(); + + int firstCommandTimestamp = commands.Any() ? commands.First().NetworkTimestamp ?? int.MaxValue : int.MaxValue; + + CreateBrushStrokeCommands(strokesWithoutCommand, firstCommandTimestamp); + + int packetCounter = 0; + int counter = 0; + foreach (BaseCommand command in commands) + { + int estimatedMessages = EstimateMessagesForCommand(command); + + if (packetCounter + estimatedMessages > batchSize) + { + yield return null; + yield return new WaitForSeconds(delayBetweenBatches); + } + + MultiplayerManager.m_Instance.OnCommandPerformed(command); + packetCounter += estimatedMessages; + counter++; + SynchHistoryPercentage(id, commands.Count(), counter); + } + + _isSendingCommandHistory = false; + + if (_isWaiting) SynchHistoryComplete(id); + else SynchHistoryCompleteForAll(); + + } + + private void CreateBrushStrokeCommands(List strokes, int LastTimestamp) + { + if (strokes == null || strokes.Count == 0) return; + + strokes = strokes.OrderBy(s => s.HeadTimestampMs).ToList(); + + uint earliestStrokeTimestampMs = strokes.First().HeadTimestampMs; + uint latestStrokeTimestampMs = strokes.Last().TailTimestampMs; + uint totalStrokeTimeMs = latestStrokeTimestampMs - earliestStrokeTimestampMs; + + if (totalStrokeTimeMs == 0) totalStrokeTimeMs = 1; + + foreach (var stroke in strokes) + { + uint strokeTimeMs = stroke.HeadTimestampMs - earliestStrokeTimestampMs; + long numerator = (long)strokeTimeMs * (LastTimestamp - 1); + int timestamp = (int)(numerator / totalStrokeTimeMs); + + if (timestamp >= LastTimestamp) + { + timestamp = LastTimestamp - 1; + } + + BrushStrokeCommand command = new BrushStrokeCommand(stroke, Guid.NewGuid(), timestamp); + SketchMemoryScript.m_Instance.AddCommandToNetworkStack(command); + } + } + + private int EstimateMessagesForCommand(BaseCommand command) + { + switch (command) + { + case BrushStrokeCommand strokeCommand: + int totalControlPoints = strokeCommand.m_Stroke.m_ControlPoints.Length; + return totalControlPoints <= NetworkingConstants.MaxControlPointsPerChunk + ? 1 + : 2 + ((int)Math.Ceiling((double)totalControlPoints / NetworkingConstants.MaxControlPointsPerChunk) - 1); + default: + return 1; + } + } + #endregion + + #region Remote infoCard commands + private void StartSynchHistory(int id) + { + + MultiplayerManager.m_Instance.StartSynchHistory(id); + } + + private void SynchHistoryPercentage(int id, int expected, int sent) + { + MultiplayerManager.m_Instance.SynchHistoryPercentage(id, expected, sent); + } + + private void SynchHistoryComplete(int id) + { + MultiplayerManager.m_Instance.SynchHistoryComplete(id); + } + + private void SynchHistoryCompleteForAll() + { + MultiplayerManager.m_Instance.SynchHistoryCompleteForAll(); + } + #endregion + + #region Local infoCard commands + public void DisplaySynchInfo() + { + OutputWindowScript.m_Instance.CreateInfoCardAtController( + InputManager.ControllerName.Brush, + "Synch Started!", + fPopScalar: 1.0f + ); + RetrieveInfoCard(); + } + + public InfoCardAnimation RetrieveInfoCard() + { + InfoCardAnimation[] allInfoCards = UnityEngine.Object.FindObjectsOfType(); + + foreach (var card in allInfoCards) + { + TextMeshPro textComponent = card.GetComponentInChildren(); + if (textComponent != null && textComponent.text.Contains("Synch")) + { + infoCard = card; + return card; + } + } + + return null; + } + + public void SynchInfoPercentageUpdate() + { + int percentage = (int)((float)SketchMemoryScript.AllStrokesCount() / numberOfCommandsExpected * 100); + string text = $"Synch {percentage}%"; + + if (infoCard == null) infoCard = RetrieveInfoCard(); + if (infoCard == null) DisplaySynchInfo(); + + infoCard.GetComponentInChildren().text = text; + infoCard.UpdateHoldingDuration(5f); + } + + public void HideSynchInfo() + { + if (infoCard == null) infoCard = RetrieveInfoCard(); + if (infoCard == null) DisplaySynchInfo(); + + infoCard.GetComponentInChildren().text = "Synch Ended!"; + infoCard.UpdateHoldingDuration(3.0f); + } + + #endregion + + } +} diff --git a/Assets/Scripts/Multiplayer/HistorySynchronizationManager.cs.meta b/Assets/Scripts/Multiplayer/HistorySynchronizationManager.cs.meta new file mode 100644 index 0000000000..b3b7a5b0c1 --- /dev/null +++ b/Assets/Scripts/Multiplayer/HistorySynchronizationManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a751437a9d2b2724584655aaead0ddfe +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 index 2070aa995a..579e3ce4dd 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerManager.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerManager.cs @@ -42,8 +42,6 @@ public enum MultiplayerType public class MultiplayerManager : MonoBehaviour { - public int batchSize = 60; - public float delayBetweenBatches = 0.05f; public static MultiplayerManager m_Instance; public MultiplayerType m_MultiplayerType; @@ -64,11 +62,6 @@ public class MultiplayerManager : MonoBehaviour public event Action UserInfoStateUpdated; private List m_RoomData = new List(); private double? m_NetworkOffsetTimestamp = null; - private bool _isWaiting = false; - private bool _isSendingCommandHistory = false; - [HideInInspector] public int numberOfCommandsExpected = 0; - [HideInInspector] public int numberOfCommandsSent = 0; - private InfoCardAnimation infoCard; ulong myOculusUserId; @@ -411,14 +404,10 @@ void OnRemotePlayerJoined(int id, ITransientData playerData) playerData.PlayerId = id; m_RemotePlayers.Add(playerData); - //if i am the room owner I should send the command history if (isUserRoomOwner) { - StartSynchHistory(id); - SendCurrentTargetEnvironmentCommand(); - StartCoroutine(SendStrokesAndCommandHistory(id)); + HistorySynchronizationManager.m_Instance.StartSyncronizationForUser(id); } - } void OnPlayerLeft(int id) @@ -466,7 +455,7 @@ void OnPlayerLeft(int id) } - private async void OnCommandPerformed(BaseCommand command) + public async void OnCommandPerformed(BaseCommand command) { if (State == ConnectionState.IN_ROOM) { @@ -475,54 +464,54 @@ private async void OnCommandPerformed(BaseCommand command) } - private async void StartSynchHistory(int id) + public void OnCommandUndo(BaseCommand command) { if (State == ConnectionState.IN_ROOM) { - await m_Manager.RpcStartSyncHistory(id); + m_Manager.UndoCommand(command); } } - private async void SynchHistoryPercentage(int id, int expected, int sent) + public void OnCommandRedo(BaseCommand command) { if (State == ConnectionState.IN_ROOM) { - await m_Manager.RpcSyncHistoryPercentage(id, expected, sent); + m_Manager.RedoCommand(command); } } - private async void SynchHistoryComplete(int id) + public async void StartSynchHistory(int id) { if (State == ConnectionState.IN_ROOM) { - await m_Manager.RpcHistorySyncComplete(id); + await m_Manager.RpcStartSyncHistory(id); } } - private async void SynchHistoryCompleteForAll() + public async void SynchHistoryPercentage(int id, int expected, int sent) { if (State == ConnectionState.IN_ROOM) { - foreach (var player in m_RemotePlayers) - { - await m_Manager.RpcHistorySyncComplete(player.PlayerId); - } + await m_Manager.RpcSyncHistoryPercentage(id, expected, sent); } } - private void OnCommandUndo(BaseCommand command) + public async void SynchHistoryComplete(int id) { if (State == ConnectionState.IN_ROOM) { - m_Manager.UndoCommand(command); + await m_Manager.RpcHistorySyncComplete(id); } } - private void OnCommandRedo(BaseCommand command) + public async void SynchHistoryCompleteForAll() { if (State == ConnectionState.IN_ROOM) { - m_Manager.RedoCommand(command); + foreach (var player in m_RemotePlayers) + { + await m_Manager.RpcHistorySyncComplete(player.PlayerId); + } } } @@ -552,136 +541,6 @@ private void OnConnectionHandlerDisconnected() Disconnected?.Invoke();// Invoke the Disconnected event } - public void SendCurrentTargetEnvironmentCommand() - { - TiltBrush.Environment targetEnvironment = SceneSettings.m_Instance.GetDesiredPreset(); - - if (targetEnvironment != null) - { - SwitchEnvironmentCommand command = new SwitchEnvironmentCommand(targetEnvironment); - OnCommandPerformed(command); - } - } - - private IEnumerator SendStrokesAndCommandHistory(int id) - { - // Allow only one active coroutine and one queued: - // If already waiting, ignore this call. - if (_isWaiting) yield break; - - if (_isSendingCommandHistory) - { - _isWaiting = true; - while (_isSendingCommandHistory) - { - yield return null; - - } - _isWaiting = false; - } - - _isSendingCommandHistory = true; - - - try - { - // Retrieve Strokes that do not have a command associated with them - List strokesWithoutCommand = SketchMemoryScript.m_Instance.GetStrokesWithoutCommand(); - // Retrieve all commands from staks - IEnumerable commands = SketchMemoryScript.m_Instance.GetAllOperations(); - - - // Determine the first command timestamp - int firstCommandTimestamp = int.MaxValue; - if (commands.Any()) - { - var firstCommand = commands.First(); - if (firstCommand.NetworkTimestamp.HasValue) firstCommandTimestamp = firstCommand.NetworkTimestamp.Value; - } - - // Generate a list of brush stroke commands from the list of strokes without commands - CreateBrushStrokeCommands(strokesWithoutCommand, firstCommandTimestamp); - - // Send commands - int packetCounter = 0; - int counter = 0; - foreach (BaseCommand command in commands) - { - int estimatedMessages = EstimateMessagesForCommand(command); - - if (packetCounter + estimatedMessages > batchSize) - { - yield return null; - yield return new WaitForSeconds(delayBetweenBatches); - } - - OnCommandPerformed(command); - packetCounter += estimatedMessages; - counter++; - SynchHistoryPercentage(id, commands.Count(), counter); - } - } - finally - { - _isSendingCommandHistory = false; - - if (_isWaiting) SynchHistoryComplete(id); - else SynchHistoryCompleteForAll(); - } - } - - private void CreateBrushStrokeCommands(List strokes, int LastTimestamp) - { - - if (strokes == null || strokes.Count == 0) return; - - // Ensure strokes are ordered by their timestamps - strokes = strokes.OrderBy(s => s.HeadTimestampMs).ToList(); - - uint earliestStrokeTimestampMs = strokes.First().HeadTimestampMs; - uint latestStrokeTimestampMs = strokes.Last().TailTimestampMs; - uint totalStrokeTimeMs = latestStrokeTimestampMs - earliestStrokeTimestampMs; - - if (totalStrokeTimeMs == 0) totalStrokeTimeMs = 1; - - foreach (var stroke in strokes) - { - // Calculate timestamp - uint strokeTimeMs = stroke.HeadTimestampMs - earliestStrokeTimestampMs; - - // Use long to prevent integer overflow - long numerator = (long)strokeTimeMs * (LastTimestamp - 1); - int timestamp = (int)(numerator / totalStrokeTimeMs); - - // Ensure timestamp is less than firstCommandTimestamp - if (timestamp >= LastTimestamp) - { - timestamp = LastTimestamp - 1; - } - - BrushStrokeCommand command = new BrushStrokeCommand(stroke, Guid.NewGuid(), timestamp); - SketchMemoryScript.m_Instance.AddCommandToNetworkStack(command); - } - } - - private int EstimateMessagesForCommand(BaseCommand command) - { - switch (command) - { - case BrushStrokeCommand strokeCommand: - int totalControlPoints = strokeCommand.m_Stroke.m_ControlPoints.Length; - if (totalControlPoints <= NetworkingConstants.MaxControlPointsPerChunk) return 1; - int splits = (int)Math.Ceiling((double)totalControlPoints / NetworkingConstants.MaxControlPointsPerChunk); - return 2 + (splits - 1); - case DeleteStrokeCommand: - case SwitchEnvironmentCommand: - case BaseCommand: - return 1; - default: - return 0; - } - } - public void StartSpeaking() { m_VoiceManager?.StartSpeaking(); @@ -743,58 +602,6 @@ private void UpdateSketchMemoryScriptTimeOffset(ConnectionState state) } - public void DisplaySynchInfo() - { - OutputWindowScript.m_Instance.CreateInfoCardAtController( - InputManager.ControllerName.Brush, - "Synch Started!", - fPopScalar: 1.0f - ); - - RetrieveInfoCard(); - } - - public InfoCardAnimation RetrieveInfoCard() - { - // Find all active InfoCards in the scene - InfoCardAnimation[] allInfoCards = FindObjectsOfType(); - - foreach (var card in allInfoCards) - { - TextMeshPro textComponent = card.GetComponentInChildren(); - if (textComponent != null && textComponent.text.Contains("Synch")) - { - infoCard = card; - return card; - } - } - - return null; - } - - public void SynchInfoPercentageUpdate() - { - int percentage = (int)((float)SketchMemoryScript.AllStrokesCount() / MultiplayerManager.m_Instance.numberOfCommandsExpected * 100); - string text = $"Synch {percentage}%"; - - if (infoCard == null) infoCard = RetrieveInfoCard(); - - if (infoCard == null) DisplaySynchInfo(); - - infoCard.GetComponentInChildren().text = text; - infoCard.UpdateHoldingDuration(5f); - } - - - public void HideSynchInfo() - { - if (infoCard == null) infoCard = RetrieveInfoCard(); - - if (infoCard == null) DisplaySynchInfo(); - - infoCard.GetComponentInChildren().text = "Synch Ended!"; - infoCard.UpdateHoldingDuration(3.0f); - } } } diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs b/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs index 8d0494b75e..100763c9b1 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs @@ -349,8 +349,8 @@ public static void RPC_StartHistorySync(NetworkRunner runner, [RpcTarget] Player [Rpc(InvokeLocal = false)] public static void RPC_HistoryPercentageUpdate(NetworkRunner runner, [RpcTarget] PlayerRef targetPlayer, int expected, int sent) { - MultiplayerManager.m_Instance.numberOfCommandsExpected = expected; - MultiplayerManager.m_Instance.numberOfCommandsSent = sent; + HistorySynchronizationManager.m_Instance.numberOfCommandsExpected = expected; + HistorySynchronizationManager.m_Instance.numberOfCommandsSent = sent; m_Instance.IssueGlobalCommand(GlobalCommands.SynchInfoPercentageUpdate); } diff --git a/Assets/Scripts/SketchControlsScript.cs b/Assets/Scripts/SketchControlsScript.cs index c10d279ba6..64969ee25e 100644 --- a/Assets/Scripts/SketchControlsScript.cs +++ b/Assets/Scripts/SketchControlsScript.cs @@ -4861,13 +4861,13 @@ public void IssueGlobalCommand(GlobalCommands rEnum, int iParam1 = -1, SketchSurfacePanel.m_Instance.EatToolsInput(); break; case GlobalCommands.DisplaySynchInfo: - MultiplayerManager.m_Instance.DisplaySynchInfo(); + HistorySynchronizationManager.m_Instance.DisplaySynchInfo(); break; case GlobalCommands.SynchInfoPercentageUpdate: - MultiplayerManager.m_Instance.SynchInfoPercentageUpdate(); + HistorySynchronizationManager.m_Instance.SynchInfoPercentageUpdate(); break; case GlobalCommands.HideSynchInfo: - MultiplayerManager.m_Instance.HideSynchInfo(); + HistorySynchronizationManager.m_Instance.HideSynchInfo(); break; case GlobalCommands.RepaintOptions: break; // Intentionally blank. case GlobalCommands.Null: break; // Intentionally blank. From 146fd6f291934cbadad1b804362c8c5c6f31fffc Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Wed, 4 Dec 2024 10:49:23 +0000 Subject: [PATCH 121/174] Update HistorySynchronizationManager.cs Add a list of users being update to manage the display updates for each --- .../HistorySynchronizationManager.cs | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/Assets/Scripts/Multiplayer/HistorySynchronizationManager.cs b/Assets/Scripts/Multiplayer/HistorySynchronizationManager.cs index 1523d89f06..fd7a83a1ce 100644 --- a/Assets/Scripts/Multiplayer/HistorySynchronizationManager.cs +++ b/Assets/Scripts/Multiplayer/HistorySynchronizationManager.cs @@ -33,17 +33,16 @@ public class HistorySynchronizationManager : MonoBehaviour private bool _isWaiting = false; private bool _isSendingCommandHistory = false; private InfoCardAnimation infoCard; - + private List PlayerIdBeingSynched = new List(); void Awake() { m_Instance = this; } - public void StartSyncronizationForUser(int id) { - + PlayerIdBeingSynched.Add(id); StartSynchHistory(id); SendCurrentTargetEnvironmentCommand(); StartCoroutine(SendStrokesAndCommandHistory(id)); @@ -100,13 +99,22 @@ public IEnumerator SendStrokesAndCommandHistory(int id) MultiplayerManager.m_Instance.OnCommandPerformed(command); packetCounter += estimatedMessages; counter++; - SynchHistoryPercentage(id, commands.Count(), counter); + + foreach (int PlayerId in PlayerIdBeingSynched) SynchHistoryPercentage(PlayerId, commands.Count(), counter); } _isSendingCommandHistory = false; - if (_isWaiting) SynchHistoryComplete(id); - else SynchHistoryCompleteForAll(); + if (_isWaiting) + { + SynchHistoryComplete(id); + PlayerIdBeingSynched.Remove(id); + } + else + { + PlayerIdBeingSynched.Clear(); + SynchHistoryCompleteForAll(); + } } From 389c7044c540a0bf25e377792cffb2baa6fbc0c8 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Wed, 4 Dec 2024 10:51:07 +0000 Subject: [PATCH 122/174] Update PhotonRPC.cs Add an optional target player to RPC methods to enable one-to-one history synchronization rather than one-to-many --- .../Scripts/Multiplayer/Photon/PhotonRPC.cs | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs b/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs index 100763c9b1..881ac29d1d 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs @@ -166,7 +166,7 @@ public static void CreateBrushStroke(Stroke stroke, Guid commandGuid, int times #region RPCS [Rpc(InvokeLocal = false, Channel = RpcChannel.Reliable)] - public static void RPC_SyncToSharedAnchor(NetworkRunner runner, string uuid) + public static void RPC_SyncToSharedAnchor(NetworkRunner runner, string uuid, [RpcTarget] PlayerRef targetPlayer = default) { #if OCULUS_SUPPORTED OculusMRController.m_Instance.RemoteSyncToAnchor(uuid); @@ -174,7 +174,7 @@ public static void RPC_SyncToSharedAnchor(NetworkRunner runner, string uuid) } [Rpc(InvokeLocal = false, Channel = RpcChannel.Reliable)] - public static void RPC_PerformCommand(NetworkRunner runner, string commandName, string guid, string[] data) + public static void RPC_PerformCommand(NetworkRunner runner, string commandName, string guid, string[] data, [RpcTarget] PlayerRef targetPlayer = default) { Debug.Log($"Command recieved: {commandName}"); @@ -200,7 +200,7 @@ public static void RPC_PerformCommand(NetworkRunner runner, string commandName, } [Rpc(InvokeLocal = false, Channel = RpcChannel.Reliable)] - public static void RPC_Undo(NetworkRunner runner, string commandName) + public static void RPC_Undo(NetworkRunner runner, string commandName, [RpcTarget] PlayerRef targetPlayer = default) { if (SketchMemoryScript.m_Instance.CanUndo()) { @@ -209,7 +209,7 @@ public static void RPC_Undo(NetworkRunner runner, string commandName) } [Rpc(InvokeLocal = false, Channel = RpcChannel.Reliable)] - public static void RPC_Redo(NetworkRunner runner, string commandName) + public static void RPC_Redo(NetworkRunner runner, string commandName, [RpcTarget] PlayerRef targetPlayer = default) { if (SketchMemoryScript.m_Instance.CanRedo()) { @@ -218,7 +218,7 @@ public static void RPC_Redo(NetworkRunner runner, string commandName) } [Rpc(InvokeLocal = false, Channel = RpcChannel.Reliable)] - public static void RPC_BaseCommand(NetworkRunner runner, Guid commandGuid, Guid parentGuid = default, int childCount = 0) + public static void RPC_BaseCommand(NetworkRunner runner, Guid commandGuid, Guid parentGuid = default, int childCount = 0, [RpcTarget] PlayerRef targetPlayer = default) { if (CheckifCommandGuidIsInStack(commandGuid)) return; @@ -230,7 +230,7 @@ public static void RPC_BaseCommand(NetworkRunner runner, Guid commandGuid, Guid } [Rpc(InvokeLocal = false, Channel = RpcChannel.Reliable)] - public static void RPC_BrushStrokeFull(NetworkRunner runner, NetworkedStroke strokeData, Guid commandGuid, int timestamp, Guid parentGuid = default, int childCount = 0) + public static void RPC_BrushStrokeFull(NetworkRunner runner, NetworkedStroke strokeData, Guid commandGuid, int timestamp, Guid parentGuid = default, int childCount = 0, [RpcTarget] PlayerRef targetPlayer = default) { if (CheckifCommandGuidIsInStack(commandGuid)) return; @@ -241,7 +241,7 @@ public static void RPC_BrushStrokeFull(NetworkRunner runner, NetworkedStroke str } [Rpc(InvokeLocal = false, Channel = RpcChannel.Reliable)] - public static void RPC_BrushStrokeBegin(NetworkRunner runner, Guid id, NetworkedStroke strokeData, int finalLength) + public static void RPC_BrushStrokeBegin(NetworkRunner runner, Guid id, NetworkedStroke strokeData, int finalLength, [RpcTarget] PlayerRef targetPlayer = default) { var decode = NetworkedStroke.ToStroke(strokeData); @@ -261,7 +261,7 @@ public static void RPC_BrushStrokeBegin(NetworkRunner runner, Guid id, Networked } [Rpc(InvokeLocal = false, Channel = RpcChannel.Reliable)] - public static void RPC_BrushStrokeContinue(NetworkRunner runner, Guid id, int offset, NetworkedControlPoint[] controlPoints, bool[] dropPoints) + public static void RPC_BrushStrokeContinue(NetworkRunner runner, Guid id, int offset, NetworkedControlPoint[] controlPoints, bool[] dropPoints, [RpcTarget] PlayerRef targetPlayer = default) { if(!m_inProgressStrokes.ContainsKey(id)) { @@ -279,7 +279,7 @@ public static void RPC_BrushStrokeContinue(NetworkRunner runner, Guid id, int of } [Rpc(InvokeLocal = false, Channel = RpcChannel.Reliable)] - public static void RPC_BrushStrokeComplete(NetworkRunner runner, Guid id, Guid commandGuid, int timestamp, Guid parentGuid = default, int childCount = 0) + public static void RPC_BrushStrokeComplete(NetworkRunner runner, Guid id, Guid commandGuid, int timestamp, Guid parentGuid = default, int childCount = 0, [RpcTarget] PlayerRef targetPlayer = default) { if (CheckifCommandGuidIsInStack(commandGuid)) return; @@ -298,7 +298,7 @@ public static void RPC_BrushStrokeComplete(NetworkRunner runner, Guid id, Guid c } [Rpc(InvokeLocal = false, Channel = RpcChannel.Reliable)] - public static void RPC_DeleteStroke(NetworkRunner runner, int seed, Guid commandGuid, int timestamp, Guid parentGuid = default, int childCount = 0) + public static void RPC_DeleteStroke(NetworkRunner runner, int seed, Guid commandGuid, int timestamp, Guid parentGuid = default, int childCount = 0, [RpcTarget] PlayerRef targetPlayer = default) { if (CheckifCommandGuidIsInStack(commandGuid)) return; @@ -320,7 +320,7 @@ public static void RPC_DeleteStroke(NetworkRunner runner, int seed, Guid command } [Rpc(InvokeLocal = false, Channel = RpcChannel.Reliable)] - public static void RPC_SwitchEnvironment(NetworkRunner runner, Guid environmentGuid, Guid commandGuid, int timestamp, Guid parentGuid = default, int childCount = 0) + public static void RPC_SwitchEnvironment(NetworkRunner runner, Guid environmentGuid, Guid commandGuid, int timestamp, Guid parentGuid = default, int childCount = 0, [RpcTarget] PlayerRef targetPlayer = default) { if (CheckifCommandGuidIsInStack(commandGuid)) return; From 2ea415d805446060838913cc7bd441828c83a99f Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Wed, 4 Dec 2024 13:12:32 +0000 Subject: [PATCH 123/174] Implement Acknowledgment-Based History Synchronization to Ensure Reliability [skip ci] Data packets continue to be lost during history synchronization, even with reliable transmission and controlled packet rates. To address this, we are transitioning to a system where every synchronization command is explicitly acknowledged by the receiving user, ensuring robust and reliable synchronization. --- .../HistorySynchronizationManager.cs | 186 ++++++++++++------ .../Multiplayer/MultiplayerInterfaces.cs | 2 + .../Scripts/Multiplayer/MultiplayerManager.cs | 34 ++-- .../Multiplayer/Photon/PhotonManager.cs | 42 ++-- .../Scripts/Multiplayer/Photon/PhotonRPC.cs | 63 ++++-- 5 files changed, 230 insertions(+), 97 deletions(-) diff --git a/Assets/Scripts/Multiplayer/HistorySynchronizationManager.cs b/Assets/Scripts/Multiplayer/HistorySynchronizationManager.cs index fd7a83a1ce..75b98ebbd8 100644 --- a/Assets/Scripts/Multiplayer/HistorySynchronizationManager.cs +++ b/Assets/Scripts/Multiplayer/HistorySynchronizationManager.cs @@ -13,9 +13,10 @@ // limitations under the License. using System; -using System.Collections; using System.Collections.Generic; using System.Linq; +using System.Threading; +using System.Threading.Tasks; using TiltBrush; using TMPro; using UnityEngine; @@ -33,19 +34,25 @@ public class HistorySynchronizationManager : MonoBehaviour private bool _isWaiting = false; private bool _isSendingCommandHistory = false; private InfoCardAnimation infoCard; - private List PlayerIdBeingSynched = new List(); + private HashSet currentlyProcessingUsers = new HashSet(); + private Dictionary> userCommandQueues = new Dictionary>(); + private readonly SemaphoreSlim _networkLock = new SemaphoreSlim(1, 1); void Awake() { m_Instance = this; } - public void StartSyncronizationForUser(int id) + public async void StartSyncronizationForUser(int playerId) { - PlayerIdBeingSynched.Add(id); - StartSynchHistory(id); - SendCurrentTargetEnvironmentCommand(); - StartCoroutine(SendStrokesAndCommandHistory(id)); + + if (!userCommandQueues.ContainsKey(playerId)) + userCommandQueues[playerId] = new Queue(); + + PrepareHistory(playerId); + + if (!currentlyProcessingUsers.Contains(playerId)) + await ProcessQueueForUser(playerId); } #region Syncronization Logic @@ -60,22 +67,8 @@ public void SendCurrentTargetEnvironmentCommand() } } - public IEnumerator SendStrokesAndCommandHistory(int id) + public void PrepareHistory(int playerId) { - if (_isWaiting) yield break; - - if (_isSendingCommandHistory) - { - _isWaiting = true; - while (_isSendingCommandHistory) - { - yield return null; - } - _isWaiting = false; - } - - _isSendingCommandHistory = true; - List strokesWithoutCommand = SketchMemoryScript.m_Instance.GetStrokesWithoutCommand(); IEnumerable commands = SketchMemoryScript.m_Instance.GetAllOperations(); @@ -84,38 +77,79 @@ public IEnumerator SendStrokesAndCommandHistory(int id) CreateBrushStrokeCommands(strokesWithoutCommand, firstCommandTimestamp); - int packetCounter = 0; - int counter = 0; foreach (BaseCommand command in commands) { - int estimatedMessages = EstimateMessagesForCommand(command); - - if (packetCounter + estimatedMessages > batchSize) - { - yield return null; - yield return new WaitForSeconds(delayBetweenBatches); - } + HistorySynchronizationCommand c = new HistorySynchronizationCommand(command, playerId); + userCommandQueues[playerId].Enqueue(c); + } - MultiplayerManager.m_Instance.OnCommandPerformed(command); - packetCounter += estimatedMessages; - counter++; + } - foreach (int PlayerId in PlayerIdBeingSynched) SynchHistoryPercentage(PlayerId, commands.Count(), counter); - } + private async Task ProcessQueueForUser(int playerId) + { + currentlyProcessingUsers.Add(playerId); - _isSendingCommandHistory = false; + StartSynchHistory(playerId); - if (_isWaiting) + if (!userCommandQueues.TryGetValue(playerId, out var commandQueue)) { - SynchHistoryComplete(id); - PlayerIdBeingSynched.Remove(id); + Debug.LogWarning($"No queue found for user {playerId}. Exiting."); + CleanupUserQueue(playerId); + return; } - else + + int totalCommands = commandQueue.Count; + int processedCommands = 0; + + while (commandQueue.Count > 0) { - PlayerIdBeingSynched.Clear(); - SynchHistoryCompleteForAll(); + var command = commandQueue.Peek(); + Debug.Log($"Processing command for User {playerId}."); + + if (!MultiplayerManager.m_Instance.IsRemotePlayerStillConnected(playerId)) + { + Debug.LogWarning($"User {playerId} is no longer connected. Clearing their queue."); + CleanupUserQueue(playerId); + break; + } + + await _networkLock.WaitAsync(); + try + { + bool success = await command.Process(); + + if (success) + { + Debug.Log($"Command successfully acknowledged for User {playerId}."); + commandQueue.Dequeue(); + processedCommands++; + SynchHistoryPercentage(playerId, totalCommands, processedCommands); + } + else + { + Debug.LogError($"Command failed after retries {command.Attempts} for User {playerId}."); + CleanupUserQueue(playerId); + break; + } + } + finally + { + _networkLock.Release(); + } + } + Debug.Log($"Finished processing the queue for User {playerId}."); + SynchHistoryComplete(playerId); + currentlyProcessingUsers.Remove(playerId); + userCommandQueues.Remove(playerId); + } + + private void CleanupUserQueue(int playerId) + { + currentlyProcessingUsers.Remove(playerId); + userCommandQueues.Remove(playerId); + Debug.Log($"Cleaned up resources for User {playerId}."); } private void CreateBrushStrokeCommands(List strokes, int LastTimestamp) @@ -146,19 +180,6 @@ private void CreateBrushStrokeCommands(List strokes, int LastTimestamp) } } - private int EstimateMessagesForCommand(BaseCommand command) - { - switch (command) - { - case BrushStrokeCommand strokeCommand: - int totalControlPoints = strokeCommand.m_Stroke.m_ControlPoints.Length; - return totalControlPoints <= NetworkingConstants.MaxControlPointsPerChunk - ? 1 - : 2 + ((int)Math.Ceiling((double)totalControlPoints / NetworkingConstants.MaxControlPointsPerChunk) - 1); - default: - return 1; - } - } #endregion #region Remote infoCard commands @@ -178,10 +199,6 @@ private void SynchHistoryComplete(int id) MultiplayerManager.m_Instance.SynchHistoryComplete(id); } - private void SynchHistoryCompleteForAll() - { - MultiplayerManager.m_Instance.SynchHistoryCompleteForAll(); - } #endregion #region Local infoCard commands @@ -236,4 +253,53 @@ public void HideSynchInfo() #endregion } + + public class HistorySynchronizationCommand + { + public BaseCommand Command { get; private set; } + public int TargetPlayerId { get; private set; } + public bool IsAcknowledged { get; private set; } + public float LastSentTime { get; private set; } + public int Attempts { get; private set; } + + private const int MaxRetries = 5; + private float RetryDelay = 0.1f; + + public HistorySynchronizationCommand(BaseCommand command, int targetPlayerId) + { + Command = command; + IsAcknowledged = false; + LastSentTime = Time.time; + Attempts = 0; + TargetPlayerId = targetPlayerId; + } + + public async Task Process() + { + + while (Attempts < MaxRetries) + { + Attempts++; + LastSentTime = Time.time; + + MultiplayerManager.m_Instance.SendCommandToPlayer(Command, TargetPlayerId); + + bool isAcknowledged = await MultiplayerManager.m_Instance.CheckCommandReception(Command, TargetPlayerId); + + if (isAcknowledged) + { + IsAcknowledged = true; + return true; + } + + Debug.Log($"Attempt {Attempts} failed for command. Retrying in {RetryDelay} seconds..."); + await Task.Delay(TimeSpan.FromSeconds(RetryDelay)); + RetryDelay *= 2; + } + + Debug.LogError($"Command acknowledgment failed after {MaxRetries} attempts."); + return false; + } + } + } diff --git a/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs b/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs index c184a35ce9..4cd29965a1 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs @@ -37,6 +37,8 @@ public interface IDataConnectionHandler : IConnectionHandler int GetNetworkedTimestampMilliseconds(); bool GetPlayerRoomOwnershipStatus(int playerId); Task PerformCommand(BaseCommand command); + Task SendCommandToPlayer(BaseCommand command, int playerId); + Task CheckCommandReception(BaseCommand command, int playerId); Task UndoCommand(BaseCommand command); Task RedoCommand(BaseCommand command); Task RpcSyncToSharedAnchor(string uuid); diff --git a/Assets/Scripts/Multiplayer/MultiplayerManager.cs b/Assets/Scripts/Multiplayer/MultiplayerManager.cs index 579e3ce4dd..68882aa4dd 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerManager.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerManager.cs @@ -461,7 +461,24 @@ public async void OnCommandPerformed(BaseCommand command) { await m_Manager.PerformCommand(command); } + } + + public async void SendCommandToPlayer(BaseCommand command, int playerID) + { + if (State == ConnectionState.IN_ROOM) + { + await m_Manager.SendCommandToPlayer(command, playerID); + } + } + + public async Task CheckCommandReception(BaseCommand command, int id) + { + if (State == ConnectionState.IN_ROOM) + { + return await m_Manager.CheckCommandReception(command, id); + } + return false; } public void OnCommandUndo(BaseCommand command) @@ -504,17 +521,6 @@ public async void SynchHistoryComplete(int id) } } - public async void SynchHistoryCompleteForAll() - { - if (State == ConnectionState.IN_ROOM) - { - foreach (var player in m_RemotePlayers) - { - await m_Manager.RpcHistorySyncComplete(player.PlayerId); - } - } - } - async void ShareAnchors() { #if OCULUS_SUPPORTED @@ -577,6 +583,12 @@ public bool IsUserRoomOwner() return isUserRoomOwner; } + public bool IsRemotePlayerStillConnected(int playerId) + { + if (m_RemotePlayers.Any(player => player.PlayerId == playerId)) return true; + return false; + } + public int? GetNetworkedTimestampMilliseconds() { if (State == ConnectionState.IN_ROOM) diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs index 5cd4c9fff4..8af6a71079 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs @@ -24,6 +24,7 @@ using Fusion.Sockets; using TiltBrush; using UnityEditor; +using Photon.Pun; @@ -264,6 +265,20 @@ public async Task PerformCommand(BaseCommand command) return ProcessCommand(command); } + public async Task SendCommandToPlayer(BaseCommand command, int playerId) + { + await Task.Yield(); + PlayerRef playerRef = playerId; + return ProcessCommand(command, playerRef); + } + + public async Task CheckCommandReception(BaseCommand command, int id) + { + PlayerRef targetPlayer = id; + PhotonRPC.RPC_CheckCommand(m_Runner, command.Guid, m_Runner.LocalPlayer, targetPlayer); + return await PhotonRPC.WaitForAcknowledgment(command.Guid); + } + public async Task UndoCommand(BaseCommand command) { PhotonRPC.RPC_Undo(m_Runner, command.GetType().ToString()); @@ -301,7 +316,6 @@ public async Task RpcHistorySyncComplete(int id) return true; } - public async Task RpcSyncHistoryPercentage(int id, int exp, int snt) { PlayerRef playerRef = id; @@ -313,20 +327,20 @@ public async Task RpcSyncHistoryPercentage(int id, int exp, int snt) #endregion #region Command Methods - private bool ProcessCommand(BaseCommand command) + private bool ProcessCommand(BaseCommand command, PlayerRef playerRef = default) { bool success = true; switch (command) { case BrushStrokeCommand: - success &= CommandBrushStroke(command as BrushStrokeCommand); + success &= CommandBrushStroke(command as BrushStrokeCommand, playerRef); break; case DeleteStrokeCommand: - success &= CommandDeleteStroke(command as DeleteStrokeCommand); + success &= CommandDeleteStroke(command as DeleteStrokeCommand, playerRef); break; case SwitchEnvironmentCommand: - success &= CommandSwitchEnvironment(command as SwitchEnvironmentCommand); + success &= CommandSwitchEnvironment(command as SwitchEnvironmentCommand, playerRef); break; case BaseCommand: success &= CommandBase(command); @@ -351,7 +365,7 @@ private bool ProcessCommand(BaseCommand command) return success; } - private bool CommandBrushStroke(BrushStrokeCommand command) + private bool CommandBrushStroke(BrushStrokeCommand command, PlayerRef playerRef = default) { var stroke = command.m_Stroke; int maxPointsPerChunk = NetworkingConstants.MaxControlPointsPerChunk; @@ -373,7 +387,7 @@ private bool CommandBrushStroke(BrushStrokeCommand command) var strokeGuid = Guid.NewGuid(); // First Stroke - PhotonRPC.RPC_BrushStrokeBegin(m_Runner, strokeGuid, netStroke, stroke.m_ControlPoints.Length); + PhotonRPC.RPC_BrushStrokeBegin(m_Runner, strokeGuid, netStroke, stroke.m_ControlPoints.Length, playerRef); // Middle for (int rounds = 1; rounds < numSplits + 1; ++rounds) @@ -388,16 +402,16 @@ private bool CommandBrushStroke(BrushStrokeCommand command) netControlPoints[point] = new NetworkedControlPoint().Init(controlPoints[point]); } - PhotonRPC.RPC_BrushStrokeContinue(m_Runner, strokeGuid, rounds * maxPointsPerChunk, netControlPoints, dropPoints); + PhotonRPC.RPC_BrushStrokeContinue(m_Runner, strokeGuid, rounds * maxPointsPerChunk, netControlPoints, dropPoints, playerRef); } // End - PhotonRPC.RPC_BrushStrokeComplete(m_Runner, strokeGuid, command.Guid, (int)command.NetworkTimestamp, command.ParentGuid, command.ChildrenCount); + PhotonRPC.RPC_BrushStrokeComplete(m_Runner, strokeGuid, command.Guid, (int)command.NetworkTimestamp, command.ParentGuid, command.ChildrenCount, playerRef); } else { // Can send in one. - PhotonRPC.RPC_BrushStrokeFull(m_Runner, new NetworkedStroke().Init(command.m_Stroke), command.Guid, (int)command.NetworkTimestamp, command.ParentGuid, command.ChildrenCount); + PhotonRPC.RPC_BrushStrokeFull(m_Runner, new NetworkedStroke().Init(command.m_Stroke), command.Guid, (int)command.NetworkTimestamp, command.ParentGuid, command.ChildrenCount, playerRef); } return true; } @@ -408,16 +422,16 @@ private bool CommandBase(BaseCommand command) return true; } - private bool CommandDeleteStroke(DeleteStrokeCommand command) + private bool CommandDeleteStroke(DeleteStrokeCommand command, PlayerRef playerRef = default) { - PhotonRPC.RPC_DeleteStroke(m_Runner, command.m_TargetStroke.m_Seed, command.Guid, (int)command.NetworkTimestamp, command.ParentGuid, command.ChildrenCount); + PhotonRPC.RPC_DeleteStroke(m_Runner, command.m_TargetStroke.m_Seed, command.Guid, (int)command.NetworkTimestamp, command.ParentGuid, command.ChildrenCount, playerRef); return true; } - private bool CommandSwitchEnvironment(SwitchEnvironmentCommand command) + private bool CommandSwitchEnvironment(SwitchEnvironmentCommand command, PlayerRef playerRef = default) { Guid environmentGuid = command.m_NextEnvironment.m_Guid; - PhotonRPC.RPC_SwitchEnvironment(m_Runner, environmentGuid, command.Guid, (int)command.NetworkTimestamp, command.ParentGuid, command.ChildrenCount); + PhotonRPC.RPC_SwitchEnvironment(m_Runner, environmentGuid, command.Guid, (int)command.NetworkTimestamp, command.ParentGuid, command.ChildrenCount, playerRef); return true; } #endregion diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs b/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs index 881ac29d1d..db40356b1e 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs @@ -22,6 +22,7 @@ using Fusion; using TiltBrush; using static TiltBrush.SketchControlsScript; +using System.Threading.Tasks; namespace OpenBrush.Multiplayer { @@ -29,6 +30,7 @@ public class PhotonRPC : SimulationBehaviour { private static Dictionary m_inProgressStrokes; private static List m_pendingCommands; + private static Dictionary> CommandAcknowledgments = new(); public void Awake() { @@ -164,8 +166,29 @@ public static void CreateBrushStroke(Stroke stroke, Guid commandGuid, int times AddPendingCommand(preAction, commandGuid, parentGuid, command, childCount); } -#region RPCS - [Rpc(InvokeLocal = false, Channel = RpcChannel.Reliable)] + public static async Task WaitForAcknowledgment(Guid commandGuid, int timeoutMilliseconds = 1000) + { + var tcs = new TaskCompletionSource(); + CommandAcknowledgments[commandGuid] = tcs; + + var timeoutTask = Task.Delay(timeoutMilliseconds); + var acknowledgmentTask = tcs.Task; + var completedTask = await Task.WhenAny(acknowledgmentTask, timeoutTask); + + if (completedTask == acknowledgmentTask) + { + CommandAcknowledgments.Remove(commandGuid); + return await acknowledgmentTask; + } + else + { + CommandAcknowledgments.Remove(commandGuid); + return false; + } + } + + #region RPCS + [Rpc(InvokeLocal = false)] public static void RPC_SyncToSharedAnchor(NetworkRunner runner, string uuid, [RpcTarget] PlayerRef targetPlayer = default) { #if OCULUS_SUPPORTED @@ -173,7 +196,7 @@ public static void RPC_SyncToSharedAnchor(NetworkRunner runner, string uuid, [Rp #endif // OCULUS_SUPPORTED } - [Rpc(InvokeLocal = false, Channel = RpcChannel.Reliable)] + [Rpc(InvokeLocal = false)] public static void RPC_PerformCommand(NetworkRunner runner, string commandName, string guid, string[] data, [RpcTarget] PlayerRef targetPlayer = default) { Debug.Log($"Command recieved: {commandName}"); @@ -199,7 +222,7 @@ public static void RPC_PerformCommand(NetworkRunner runner, string commandName, } } - [Rpc(InvokeLocal = false, Channel = RpcChannel.Reliable)] + [Rpc(InvokeLocal = false)] public static void RPC_Undo(NetworkRunner runner, string commandName, [RpcTarget] PlayerRef targetPlayer = default) { if (SketchMemoryScript.m_Instance.CanUndo()) @@ -208,7 +231,7 @@ public static void RPC_Undo(NetworkRunner runner, string commandName, [RpcTarget } } - [Rpc(InvokeLocal = false, Channel = RpcChannel.Reliable)] + [Rpc(InvokeLocal = false)] public static void RPC_Redo(NetworkRunner runner, string commandName, [RpcTarget] PlayerRef targetPlayer = default) { if (SketchMemoryScript.m_Instance.CanRedo()) @@ -217,7 +240,7 @@ public static void RPC_Redo(NetworkRunner runner, string commandName, [RpcTarget } } - [Rpc(InvokeLocal = false, Channel = RpcChannel.Reliable)] + [Rpc(InvokeLocal = false)] public static void RPC_BaseCommand(NetworkRunner runner, Guid commandGuid, Guid parentGuid = default, int childCount = 0, [RpcTarget] PlayerRef targetPlayer = default) { if (CheckifCommandGuidIsInStack(commandGuid)) return; @@ -229,7 +252,7 @@ public static void RPC_BaseCommand(NetworkRunner runner, Guid commandGuid, Guid AddPendingCommand(() => {}, commandGuid, parentGuid, command, childCount); } - [Rpc(InvokeLocal = false, Channel = RpcChannel.Reliable)] + [Rpc(InvokeLocal = false)] public static void RPC_BrushStrokeFull(NetworkRunner runner, NetworkedStroke strokeData, Guid commandGuid, int timestamp, Guid parentGuid = default, int childCount = 0, [RpcTarget] PlayerRef targetPlayer = default) { @@ -240,7 +263,7 @@ public static void RPC_BrushStrokeFull(NetworkRunner runner, NetworkedStroke str CreateBrushStroke(decode, commandGuid, timestamp , parentGuid, childCount); } - [Rpc(InvokeLocal = false, Channel = RpcChannel.Reliable)] + [Rpc(InvokeLocal = false)] public static void RPC_BrushStrokeBegin(NetworkRunner runner, Guid id, NetworkedStroke strokeData, int finalLength, [RpcTarget] PlayerRef targetPlayer = default) { var decode = NetworkedStroke.ToStroke(strokeData); @@ -260,7 +283,7 @@ public static void RPC_BrushStrokeBegin(NetworkRunner runner, Guid id, Networked m_inProgressStrokes[id] = decode; } - [Rpc(InvokeLocal = false, Channel = RpcChannel.Reliable)] + [Rpc(InvokeLocal = false)] public static void RPC_BrushStrokeContinue(NetworkRunner runner, Guid id, int offset, NetworkedControlPoint[] controlPoints, bool[] dropPoints, [RpcTarget] PlayerRef targetPlayer = default) { if(!m_inProgressStrokes.ContainsKey(id)) @@ -278,7 +301,7 @@ public static void RPC_BrushStrokeContinue(NetworkRunner runner, Guid id, int of } } - [Rpc(InvokeLocal = false, Channel = RpcChannel.Reliable)] + [Rpc(InvokeLocal = false)] public static void RPC_BrushStrokeComplete(NetworkRunner runner, Guid id, Guid commandGuid, int timestamp, Guid parentGuid = default, int childCount = 0, [RpcTarget] PlayerRef targetPlayer = default) { @@ -297,7 +320,7 @@ public static void RPC_BrushStrokeComplete(NetworkRunner runner, Guid id, Guid c m_inProgressStrokes.Remove(id); } - [Rpc(InvokeLocal = false, Channel = RpcChannel.Reliable)] + [Rpc(InvokeLocal = false)] public static void RPC_DeleteStroke(NetworkRunner runner, int seed, Guid commandGuid, int timestamp, Guid parentGuid = default, int childCount = 0, [RpcTarget] PlayerRef targetPlayer = default) { if (CheckifCommandGuidIsInStack(commandGuid)) return; @@ -319,7 +342,7 @@ public static void RPC_DeleteStroke(NetworkRunner runner, int seed, Guid command } } - [Rpc(InvokeLocal = false, Channel = RpcChannel.Reliable)] + [Rpc(InvokeLocal = false)] public static void RPC_SwitchEnvironment(NetworkRunner runner, Guid environmentGuid, Guid commandGuid, int timestamp, Guid parentGuid = default, int childCount = 0, [RpcTarget] PlayerRef targetPlayer = default) { if (CheckifCommandGuidIsInStack(commandGuid)) return; @@ -360,6 +383,22 @@ public static void RPC_HistorySyncCompleted(NetworkRunner runner, [RpcTarget] Pl m_Instance.IssueGlobalCommand(GlobalCommands.HideSynchInfo); } + [Rpc(InvokeLocal = false)] + public static void RPC_CheckCommand(NetworkRunner runner, Guid commandGuid, PlayerRef initiatorPlayer, [RpcTarget] PlayerRef targetPlayer) + { + bool isCommandInStack = CheckifCommandGuidIsInStack(commandGuid); + RPC_ConfirmCommand(runner, commandGuid, isCommandInStack, initiatorPlayer); + } + + [Rpc(InvokeLocal = false)] + public static void RPC_ConfirmCommand(NetworkRunner runner, Guid commandGuid, bool isCommandInStack, [RpcTarget] PlayerRef targetPlayer) + { + if (CommandAcknowledgments.TryGetValue(commandGuid, out var tcs)) + { + tcs.SetResult(isCommandInStack); + CommandAcknowledgments.Remove(commandGuid); + } + } #endregion } From 7f921f9600eff4e5bf61ae38e0e00eafd9343181 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Thu, 5 Dec 2024 13:08:14 +0000 Subject: [PATCH 124/174] Switching to Photon Fusion 2 [skip ci] --- .../Resources/NetworkProjectConfig.fusion | 172 ++--- .../NetworkProjectConfig.fusion.meta | 8 +- .../Fusion/Resources/PhotonAppSettings.asset | 2 + .../User/NetworkPrefabAssetCollection.asset | 168 ----- .../PopUps/PopUpWindow_WaitOnSync.prefab | 597 ++++++++++++++++++ .../PopUps/PopUpWindow_WaitOnSync.prefab.meta | 7 + .../Multiplayer/Photon/PhotonPlayerRig.prefab | 121 +--- .../HistorySynchronizationManager.cs | 178 ++---- .../Multiplayer/Photon/PhotonManager.cs | 58 +- .../Multiplayer/Photon/PhotonPlayerRig.cs | 44 +- .../Scripts/Multiplayer/Photon/PhotonRPC.cs | 349 +++++++--- 11 files changed, 1034 insertions(+), 670 deletions(-) delete mode 100644 Assets/Photon/Fusion/User/NetworkPrefabAssetCollection.asset create mode 100644 Assets/Prefabs/PopUps/PopUpWindow_WaitOnSync.prefab create mode 100644 Assets/Prefabs/PopUps/PopUpWindow_WaitOnSync.prefab.meta diff --git a/Assets/Photon/Fusion/Resources/NetworkProjectConfig.fusion b/Assets/Photon/Fusion/Resources/NetworkProjectConfig.fusion index b5e94af29c..84a91810f2 100644 --- a/Assets/Photon/Fusion/Resources/NetworkProjectConfig.fusion +++ b/Assets/Photon/Fusion/Resources/NetworkProjectConfig.fusion @@ -2,160 +2,68 @@ "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 - } + "Enabled": false, + "HitboxBufferLengthInMs": 200, + "HitboxDefaultCapacity": 512, + "CachedStaticCollidersSize": 64 }, - "SceneLoadSpawnMode": 0, - "DeltaCompressor": 0, + "EnqueueIncompleteSynchronousSpawns": false, "InvokeRenderInBatchMode": true, - "MaxNetworkedObjectCount": 8192, "NetworkIdIsObjectName": false, "HideNetworkObjectInactivityGuard": false, - "EnableHostMigration": false, - "HostMigrationSnapshotInterval": 60, + "AllowClientServerModesInWebGL": false, "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 + "ReplicationFeatures": 1, + "InputTransferMode": 0, + "SimulationUpdateTimeMode": 0, + "PlayerCount": 10, + "TickRateSelection": { + "Client": 64, + "ServerIndex": 0, + "ClientSendIndex": 1, + "ServerSendIndex": 1 + } }, "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 }, + "HostMigration": { + "EnableAutoUpdate": false, + "UpdateDelay": 10 + }, + "EncryptionConfig": { + "EnableEncryption": false + }, "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 + "DelayShape": 0, + "DelayMin": 0.15, + "DelayMax": 0.15, + "DelayPeriod": 0.0, + "DelayThreshold": 0.0, + "AdditionalJitter": 0.05, + "LossChanceShape": 0, + "LossChanceMin": 0.05, + "LossChanceMax": 0.05, + "LossChanceThreshold": 0.0, + "LossChancePeriod": 0.0, + "AdditionalLoss": 0.0 }, "Heap": { - "PageShift": 14, - "PageCount": 128, + "PageShift": 15, + "PageCount": 256, "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": [ + "Fusion.Unity", "Assembly-CSharp", "Assembly-CSharp-firstpass", + "Fusion.Addons.Physics", + "Fusion.Addons.FSM", "PhotonVoice.Fusion" ], "UseSerializableDictionary": true, diff --git a/Assets/Photon/Fusion/Resources/NetworkProjectConfig.fusion.meta b/Assets/Photon/Fusion/Resources/NetworkProjectConfig.fusion.meta index 51d9b65b56..b51f264e1e 100644 --- a/Assets/Photon/Fusion/Resources/NetworkProjectConfig.fusion.meta +++ b/Assets/Photon/Fusion/Resources/NetworkProjectConfig.fusion.meta @@ -1,5 +1,7 @@ fileFormatVersion: 2 -guid: dd4ca0370d231e84e889cd4edcaf0bde +guid: 9c11b716d72ba6c4cb7ca372c6e1590d +labels: +- FusionDefaultGlobal ScriptedImporter: internalIDToNameTable: [] externalObjects: {} @@ -8,4 +10,6 @@ ScriptedImporter: assetBundleName: assetBundleVariant: script: {fileID: 11500000, guid: 66a64a17d0b40f34f9224317a5a84bf2, type: 3} - PrefabAssetsContainerPath: Assets/Photon/Fusion/User/NetworkPrefabAssetCollection.asset + PrefabOptions: + UnloadPrefabOnReleasingLastInstance: 0 + UnloadUnusedPrefabsOnShutdown: 0 diff --git a/Assets/Photon/Fusion/Resources/PhotonAppSettings.asset b/Assets/Photon/Fusion/Resources/PhotonAppSettings.asset index 90885d32de..947441c8ca 100644 --- a/Assets/Photon/Fusion/Resources/PhotonAppSettings.asset +++ b/Assets/Photon/Fusion/Resources/PhotonAppSettings.asset @@ -27,3 +27,5 @@ MonoBehaviour: AuthMode: 0 EnableLobbyStatistics: 0 NetworkLogging: 1 + encryptionMode: 0 + emptyRoomTtl: 0 diff --git a/Assets/Photon/Fusion/User/NetworkPrefabAssetCollection.asset b/Assets/Photon/Fusion/User/NetworkPrefabAssetCollection.asset deleted file mode 100644 index 9fa148b93e..0000000000 --- a/Assets/Photon/Fusion/User/NetworkPrefabAssetCollection.asset +++ /dev/null @@ -1,168 +0,0 @@ -%YAML 1.1 -%TAG !u! tag:unity3d.com,2011: ---- !u!114 &-7851828500534389341 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 0} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: -571403812, guid: e725a070cec140c4caffb81624c8c787, type: 3} - m_Name: NetworkedRig - m_EditorClassIdentifier: - AssetGuid: - RawGuidValue: 7f1f582839553344c8827c8e7f15e4b2 ---- !u!114 &-6679037723637155746 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 0} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: -571403812, guid: e725a070cec140c4caffb81624c8c787, type: 3} - m_Name: PhotonPlayerRig - m_EditorClassIdentifier: - AssetGuid: - RawGuidValue: 9ee52735aebb6a445b4905c8aba8cfe3 ---- !u!114 &-4876441823125888161 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 0} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: -571403812, guid: e725a070cec140c4caffb81624c8c787, type: 3} - m_Name: PlayerBallPrototype - m_EditorClassIdentifier: - AssetGuid: - RawGuidValue: 6eca58070665ea64797a9b124cb6ab03 ---- !u!114 &-3882056053279745226 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 0} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: -571403812, guid: e725a070cec140c4caffb81624c8c787, type: 3} - m_Name: FusionAvatar - m_EditorClassIdentifier: - AssetGuid: - RawGuidValue: eedb965bcb4e568479a4828d17fbc1c3 ---- !u!114 &-2813734544996678366 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 0} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 929548324, guid: e725a070cec140c4caffb81624c8c787, type: 3} - m_Name: ~MISSING~PhotonPlayerRigBkp - m_EditorClassIdentifier: - AssetGuid: - RawGuidValue: a5876108ffed6db47892c3ae59497b56 ---- !u!114 &-2200370131318246874 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 0} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: -571403812, guid: e725a070cec140c4caffb81624c8c787, type: 3} - m_Name: BallPrototype - m_EditorClassIdentifier: - AssetGuid: - RawGuidValue: 61b6ef4ffff3d8f4498968869591d6bf ---- !u!114 &-272827184848844876 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 0} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: -571403812, guid: e725a070cec140c4caffb81624c8c787, type: 3} - m_Name: FusionColocationDriver - m_EditorClassIdentifier: - AssetGuid: - RawGuidValue: 8ee45e6a80f151e48a85e55c1f12b1b1 ---- !u!114 &11400000 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 0} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: -225830702, guid: e725a070cec140c4caffb81624c8c787, type: 3} - m_Name: NetworkPrefabAssetCollection - m_EditorClassIdentifier: ---- !u!114 &1613190245493424334 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 0} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: -571403812, guid: e725a070cec140c4caffb81624c8c787, type: 3} - m_Name: PlayerPrototype - m_EditorClassIdentifier: - AssetGuid: - RawGuidValue: e4df49d0bf125a740a2c14ab6e887572 ---- !u!114 &3073148362435255882 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 0} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: -571403812, guid: e725a070cec140c4caffb81624c8c787, type: 3} - m_Name: NetworkObject - m_EditorClassIdentifier: - AssetGuid: - RawGuidValue: e63233a416c734342a837267990c58aa ---- !u!114 &3549518608579063576 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 0} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: -571403812, guid: e725a070cec140c4caffb81624c8c787, type: 3} - m_Name: PlayerTransformPrototype - m_EditorClassIdentifier: - AssetGuid: - RawGuidValue: 3251faa3ac8a66642bf6f9e8433499da ---- !u!114 &4563594632895129475 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 0} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: -571403812, guid: e725a070cec140c4caffb81624c8c787, type: 3} - m_Name: PlayerRB2DPrototype - m_EditorClassIdentifier: - AssetGuid: - RawGuidValue: dbc9b57ea26fbf84b8cc14b0882fe89b diff --git a/Assets/Prefabs/PopUps/PopUpWindow_WaitOnSync.prefab b/Assets/Prefabs/PopUps/PopUpWindow_WaitOnSync.prefab new file mode 100644 index 0000000000..038cfa161e --- /dev/null +++ b/Assets/Prefabs/PopUps/PopUpWindow_WaitOnSync.prefab @@ -0,0 +1,597 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &2652504797212982477 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 8862736451656621753} + m_Layer: 16 + m_Name: BackgroundMesh + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &8862736451656621753 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2652504797212982477} + serializedVersion: 2 + 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: 5597448937484054268} + - {fileID: 6595699621784020893} + m_Father: {fileID: 6918750040410781998} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &4462233062032953290 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 5938522692040871171} + m_Layer: 16 + m_Name: Foreground + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &5938522692040871171 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4462233062032953290} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: -0.147, z: -0.049} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 5863778336799380782} + - {fileID: 7513470686747838780} + m_Father: {fileID: 6918750040410781998} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &5389001181572028869 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 5597448937484054268} + - component: {fileID: 7633643865989961388} + - component: {fileID: 191522133314116346} + m_Layer: 16 + m_Name: PopupBg + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &5597448937484054268 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5389001181572028869} + serializedVersion: 2 + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 0, z: 0} + m_LocalScale: {x: 100, y: 100, z: 100} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 8862736451656621753} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &7633643865989961388 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5389001181572028869} + m_Mesh: {fileID: 4300004, guid: 13b025046bc7e8549a5e8a4b5b416f65, type: 3} +--- !u!23 &191522133314116346 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5389001181572028869} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: db0305ff9081c3b448ac79e85d26e5d4, 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: 0 + 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!1 &7548996660066679482 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 5863778336799380782} + - component: {fileID: 1539577259540886247} + - component: {fileID: 1773628967189394366} + - component: {fileID: 8099100206524301147} + m_Layer: 16 + m_Name: TextLine + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &5863778336799380782 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7548996660066679482} + 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: 5938522692040871171} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: -0.723125, y: 0.47999996} + m_SizeDelta: {x: 1.5, y: 0.25} + m_Pivot: {x: 0, y: 1} +--- !u!23 &1539577259540886247 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7548996660066679482} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, 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: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + 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!114 &1773628967189394366 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7548996660066679482} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9541d86e2fd84c1d9990edf0852d74ab, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_text: Sketch Synching... + m_isRightToLeft: 0 + m_fontAsset: {fileID: 11400000, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + m_sharedMaterial: {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + m_fontSharedMaterials: [] + m_fontMaterial: {fileID: 0} + m_fontMaterials: [] + m_fontColor32: + serializedVersion: 2 + rgba: 4294967295 + m_fontColor: {r: 1, g: 1, b: 1, a: 1} + m_enableVertexGradient: 0 + m_colorMode: 3 + m_fontColorGradient: + topLeft: {r: 1, g: 1, b: 1, a: 1} + topRight: {r: 1, g: 1, b: 1, a: 1} + bottomLeft: {r: 1, g: 1, b: 1, a: 1} + bottomRight: {r: 1, g: 1, b: 1, a: 1} + m_fontColorGradientPreset: {fileID: 0} + m_spriteAsset: {fileID: 0} + m_tintAllSprites: 0 + m_StyleSheet: {fileID: 0} + m_TextStyleHashCode: -1183493901 + m_overrideHtmlColors: 0 + m_faceColor: + serializedVersion: 2 + rgba: 4294967295 + m_fontSize: 1.915 + m_fontSizeBase: 1.915 + m_fontWeight: 400 + m_enableAutoSizing: 0 + m_fontSizeMin: 18 + m_fontSizeMax: 72 + m_fontStyle: 0 + m_HorizontalAlignment: 1 + m_VerticalAlignment: 256 + m_textAlignment: 65535 + m_characterSpacing: 0 + m_wordSpacing: 0 + m_lineSpacing: 0 + m_lineSpacingMax: 0 + m_paragraphSpacing: 0 + m_charWidthMaxAdj: 0 + m_enableWordWrapping: 1 + m_wordWrappingRatios: 0.4 + m_overflowMode: 0 + m_linkedTextComponent: {fileID: 0} + parentLinkedComponent: {fileID: 0} + m_enableKerning: 1 + m_enableExtraPadding: 0 + checkPaddingRequired: 0 + m_isRichText: 1 + m_parseCtrlCharacters: 1 + m_isOrthographic: 0 + m_isCullingEnabled: 0 + m_horizontalMapping: 0 + m_verticalMapping: 0 + m_uvLineOffset: 0 + m_geometrySortingOrder: 0 + m_IsTextObjectScaleStatic: 0 + m_VertexBufferAutoSizeReduction: 0 + m_useMaxVisibleDescender: 1 + m_pageToDisplay: 1 + m_margin: {x: 0, y: 0, z: 0, w: 0} + m_isUsingLegacyAnimationComponent: 0 + m_isVolumetricText: 0 + _SortingLayer: 0 + _SortingLayerID: 0 + _SortingOrder: 0 + m_hasFontAssetChanged: 0 + m_renderer: {fileID: 1539577259540886247} + m_maskType: 0 +--- !u!114 &8099100206524301147 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7548996660066679482} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 69beb381e244f92449b8c4cf954630e9, type: 3} + m_Name: + m_EditorClassIdentifier: + m_TrackedObjects: + - rid: 3215294302546034690 + references: + version: 2 + RefIds: + - rid: 3215294302546034690 + type: {class: TrackedUGuiGraphic, ns: UnityEngine.Localization.PropertyVariants.TrackedObjects, + asm: Unity.Localization} + data: + m_Target: {fileID: 1773628967189394366} + m_TrackedProperties: + items: + - rid: 3215294302546034691 + m_UpdateType: 0 + - rid: 3215294302546034691 + type: {class: LocalizedStringProperty, ns: UnityEngine.Localization.PropertyVariants.TrackedProperties, + asm: Unity.Localization} + data: + m_Localized: + m_TableReference: + m_TableCollectionName: GUID:c84355079ab3f3e4f8f3812258805f86 + m_TableEntryReference: + m_KeyId: 76036335840321536 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_PropertyPath: m_text +--- !u!1 &7637706609544861618 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 6918750040410781998} + - component: {fileID: 3290215492046785806} + - component: {fileID: 512701638737710248} + m_Layer: 16 + m_Name: PopUpWindow_WaitOnSync + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &6918750040410781998 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7637706609544861618} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 1.967, y: 0.871, z: -0.227} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 5938522692040871171} + - {fileID: 8862736451656621753} + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &3290215492046785806 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7637706609544861618} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1.8, y: 1.2, z: 0.025} + m_Center: {x: 0, y: 0, z: -0.01} +--- !u!114 &512701638737710248 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7637706609544861618} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 739d5b1996234d64992a2ae60c3723e9, type: 3} + m_Name: + m_EditorClassIdentifier: +--- !u!1 &7769116428690201533 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 7513470686747838780} + - component: {fileID: 8199552546709320430} + - component: {fileID: 6802880386612899571} + m_Layer: 16 + m_Name: ProgressBar + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &7513470686747838780 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7769116428690201533} + serializedVersion: 2 + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0.05699998, z: 0} + m_LocalScale: {x: 1.3392899, y: 0.05952, z: 0.0029799999} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 5938522692040871171} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &8199552546709320430 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7769116428690201533} + m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &6802880386612899571 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7769116428690201533} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 4a0eeebe9bd8bbd43adbe477b3bbb6e1, 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!1 &8862480506093304917 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 6595699621784020893} + - component: {fileID: 6713759721725640257} + - component: {fileID: 5066816512544986141} + m_Layer: 16 + m_Name: PopupBorder + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &6595699621784020893 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8862480506093304917} + serializedVersion: 2 + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 0, z: 0} + m_LocalScale: {x: 100, y: 100, z: 100} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 8862736451656621753} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &6713759721725640257 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8862480506093304917} + m_Mesh: {fileID: 4300002, guid: 13b025046bc7e8549a5e8a4b5b416f65, type: 3} +--- !u!23 &5066816512544986141 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8862480506093304917} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 77dd4ff8b1158a84397aba783cd0af05, 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: 0 + 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/PopUps/PopUpWindow_WaitOnSync.prefab.meta b/Assets/Prefabs/PopUps/PopUpWindow_WaitOnSync.prefab.meta new file mode 100644 index 0000000000..ad88667293 --- /dev/null +++ b/Assets/Prefabs/PopUps/PopUpWindow_WaitOnSync.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 393a05ee4f380ac4d8d049e2242356c8 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Resources/Multiplayer/Photon/PhotonPlayerRig.prefab b/Assets/Resources/Multiplayer/Photon/PhotonPlayerRig.prefab index d220c3de20..59eb232eb3 100644 --- a/Assets/Resources/Multiplayer/Photon/PhotonPlayerRig.prefab +++ b/Assets/Resources/Multiplayer/Photon/PhotonPlayerRig.prefab @@ -49,9 +49,6 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: f250b0d6a1df335439813ad43ce3dbdb, type: 3} m_Name: m_EditorClassIdentifier: - WordOffset: 0 - WordCount: 0 - _interpolationDataSource: 0 m_PlayArea: {fileID: 1840530316396516229} m_PlayerHead: {fileID: 2866855712475842972} m_Left: {fileID: 6993845821128094923} @@ -78,14 +75,9 @@ MonoBehaviour: m_Script: {fileID: -1552182283, guid: e725a070cec140c4caffb81624c8c787, type: 3} m_Name: m_EditorClassIdentifier: + SortKey: 2839312778 ObjectInterest: 1 - DefaultInterestGroups: [] - DestroyWhenStateAuthorityLeaves: 1 - AllowStateAuthorityOverride: 0 - AoiPositionSource: {fileID: 0} Flags: 2305 - NetworkGuid: - RawGuidValue: 9ee52735aebb6a445b4905c8aba8cfe3 NestedObjects: [] NetworkedBehaviours: - {fileID: 7215016235458371273} @@ -94,7 +86,6 @@ MonoBehaviour: - {fileID: 6993845821128094923} - {fileID: 1621605232836574636} - {fileID: 4282572128721117942} - SimulationBehaviours: [] --- !u!1 &459460118330075700 GameObject: m_ObjectHideFlags: 0 @@ -140,24 +131,10 @@ MonoBehaviour: m_Script: {fileID: 158639473, guid: e725a070cec140c4caffb81624c8c787, type: 3} m_Name: m_EditorClassIdentifier: - WordOffset: 0 - WordCount: 0 - _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 + SyncScale: 0 + SyncParent: 0 + _autoAOIOverride: 1 + DisableSharedModeInterpolation: 0 --- !u!1 &1184365666732702344 GameObject: m_ObjectHideFlags: 0 @@ -203,24 +180,10 @@ MonoBehaviour: m_Script: {fileID: 158639473, guid: e725a070cec140c4caffb81624c8c787, type: 3} m_Name: m_EditorClassIdentifier: - WordOffset: 0 - WordCount: 0 - _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 + SyncScale: 0 + SyncParent: 0 + _autoAOIOverride: 1 + DisableSharedModeInterpolation: 0 --- !u!1 &2181518286534520838 GameObject: m_ObjectHideFlags: 0 @@ -1360,24 +1323,10 @@ MonoBehaviour: m_Script: {fileID: 158639473, guid: e725a070cec140c4caffb81624c8c787, type: 3} m_Name: m_EditorClassIdentifier: - WordOffset: 0 - WordCount: 0 - _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 + SyncScale: 0 + SyncParent: 0 + _autoAOIOverride: 1 + DisableSharedModeInterpolation: 0 --- !u!1 &2568790808486584579 GameObject: m_ObjectHideFlags: 0 @@ -1423,24 +1372,10 @@ MonoBehaviour: m_Script: {fileID: 158639473, guid: e725a070cec140c4caffb81624c8c787, type: 3} m_Name: m_EditorClassIdentifier: - WordOffset: 0 - WordCount: 0 - _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 + SyncScale: 0 + SyncParent: 0 + _autoAOIOverride: 1 + DisableSharedModeInterpolation: 0 --- !u!1 &3333659737751141766 GameObject: m_ObjectHideFlags: 0 @@ -2738,24 +2673,10 @@ MonoBehaviour: m_Script: {fileID: 158639473, guid: e725a070cec140c4caffb81624c8c787, type: 3} m_Name: m_EditorClassIdentifier: - WordOffset: 0 - WordCount: 0 - _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 + SyncScale: 0 + SyncParent: 0 + _autoAOIOverride: 1 + DisableSharedModeInterpolation: 0 --- !u!1 &8601996063017238699 GameObject: m_ObjectHideFlags: 0 diff --git a/Assets/Scripts/Multiplayer/HistorySynchronizationManager.cs b/Assets/Scripts/Multiplayer/HistorySynchronizationManager.cs index 75b98ebbd8..c11bed8ef9 100644 --- a/Assets/Scripts/Multiplayer/HistorySynchronizationManager.cs +++ b/Assets/Scripts/Multiplayer/HistorySynchronizationManager.cs @@ -13,10 +13,9 @@ // limitations under the License. using System; +using System.Collections; using System.Collections.Generic; using System.Linq; -using System.Threading; -using System.Threading.Tasks; using TiltBrush; using TMPro; using UnityEngine; @@ -34,25 +33,20 @@ public class HistorySynchronizationManager : MonoBehaviour private bool _isWaiting = false; private bool _isSendingCommandHistory = false; private InfoCardAnimation infoCard; - private HashSet currentlyProcessingUsers = new HashSet(); - private Dictionary> userCommandQueues = new Dictionary>(); - private readonly SemaphoreSlim _networkLock = new SemaphoreSlim(1, 1); + void Awake() { m_Instance = this; } - public async void StartSyncronizationForUser(int playerId) - { - - if (!userCommandQueues.ContainsKey(playerId)) - userCommandQueues[playerId] = new Queue(); - PrepareHistory(playerId); + public void StartSyncronizationForUser(int id) + { - if (!currentlyProcessingUsers.Contains(playerId)) - await ProcessQueueForUser(playerId); + StartSynchHistory(id); + SendCurrentTargetEnvironmentCommand(); + StartCoroutine(SendStrokesAndCommandHistory(id)); } #region Syncronization Logic @@ -67,89 +61,52 @@ public void SendCurrentTargetEnvironmentCommand() } } - public void PrepareHistory(int playerId) + public IEnumerator SendStrokesAndCommandHistory(int id) { + if (_isWaiting) yield break; - List strokesWithoutCommand = SketchMemoryScript.m_Instance.GetStrokesWithoutCommand(); - IEnumerable commands = SketchMemoryScript.m_Instance.GetAllOperations(); - - int firstCommandTimestamp = commands.Any() ? commands.First().NetworkTimestamp ?? int.MaxValue : int.MaxValue; - - CreateBrushStrokeCommands(strokesWithoutCommand, firstCommandTimestamp); - - foreach (BaseCommand command in commands) + if (_isSendingCommandHistory) { - HistorySynchronizationCommand c = new HistorySynchronizationCommand(command, playerId); - userCommandQueues[playerId].Enqueue(c); + _isWaiting = true; + while (_isSendingCommandHistory) + { + yield return null; + } + _isWaiting = false; } - } + _isSendingCommandHistory = true; - private async Task ProcessQueueForUser(int playerId) - { - currentlyProcessingUsers.Add(playerId); - StartSynchHistory(playerId); + List strokesWithoutCommand = SketchMemoryScript.m_Instance.GetStrokesWithoutCommand(); + IEnumerable commands = SketchMemoryScript.m_Instance.GetAllOperations(); - if (!userCommandQueues.TryGetValue(playerId, out var commandQueue)) - { - Debug.LogWarning($"No queue found for user {playerId}. Exiting."); - CleanupUserQueue(playerId); - return; - } + int firstCommandTimestamp = commands.Any() ? commands.First().NetworkTimestamp ?? int.MaxValue : int.MaxValue; - int totalCommands = commandQueue.Count; - int processedCommands = 0; + CreateBrushStrokeCommands(strokesWithoutCommand, firstCommandTimestamp); - while (commandQueue.Count > 0) + int packetCounter = 0; + int counter = 0; + foreach (BaseCommand command in commands) { - var command = commandQueue.Peek(); - Debug.Log($"Processing command for User {playerId}."); + int estimatedMessages = EstimateMessagesForCommand(command); - if (!MultiplayerManager.m_Instance.IsRemotePlayerStillConnected(playerId)) + if (packetCounter + estimatedMessages > batchSize) { - Debug.LogWarning($"User {playerId} is no longer connected. Clearing their queue."); - CleanupUserQueue(playerId); - break; - } - - await _networkLock.WaitAsync(); - try - { - bool success = await command.Process(); - - if (success) - { - Debug.Log($"Command successfully acknowledged for User {playerId}."); - commandQueue.Dequeue(); - processedCommands++; - SynchHistoryPercentage(playerId, totalCommands, processedCommands); - } - else - { - Debug.LogError($"Command failed after retries {command.Attempts} for User {playerId}."); - CleanupUserQueue(playerId); - break; - } - } - finally - { - _networkLock.Release(); + yield return null; + yield return new WaitForSeconds(delayBetweenBatches); } + MultiplayerManager.m_Instance.OnCommandPerformed(command); + packetCounter += estimatedMessages; + counter++; + SynchHistoryPercentage(id, commands.Count(), counter); } - Debug.Log($"Finished processing the queue for User {playerId}."); - SynchHistoryComplete(playerId); - currentlyProcessingUsers.Remove(playerId); - userCommandQueues.Remove(playerId); - } + _isSendingCommandHistory = false; + + SynchHistoryComplete(id); - private void CleanupUserQueue(int playerId) - { - currentlyProcessingUsers.Remove(playerId); - userCommandQueues.Remove(playerId); - Debug.Log($"Cleaned up resources for User {playerId}."); } private void CreateBrushStrokeCommands(List strokes, int LastTimestamp) @@ -180,6 +137,19 @@ private void CreateBrushStrokeCommands(List strokes, int LastTimestamp) } } + private int EstimateMessagesForCommand(BaseCommand command) + { + switch (command) + { + case BrushStrokeCommand strokeCommand: + int totalControlPoints = strokeCommand.m_Stroke.m_ControlPoints.Length; + return totalControlPoints <= NetworkingConstants.MaxControlPointsPerChunk + ? 1 + : 2 + ((int)Math.Ceiling((double)totalControlPoints / NetworkingConstants.MaxControlPointsPerChunk) - 1); + default: + return 1; + } + } #endregion #region Remote infoCard commands @@ -199,6 +169,7 @@ private void SynchHistoryComplete(int id) MultiplayerManager.m_Instance.SynchHistoryComplete(id); } + #endregion #region Local infoCard commands @@ -253,53 +224,4 @@ public void HideSynchInfo() #endregion } - - public class HistorySynchronizationCommand - { - public BaseCommand Command { get; private set; } - public int TargetPlayerId { get; private set; } - public bool IsAcknowledged { get; private set; } - public float LastSentTime { get; private set; } - public int Attempts { get; private set; } - - private const int MaxRetries = 5; - private float RetryDelay = 0.1f; - - public HistorySynchronizationCommand(BaseCommand command, int targetPlayerId) - { - Command = command; - IsAcknowledged = false; - LastSentTime = Time.time; - Attempts = 0; - TargetPlayerId = targetPlayerId; - } - - public async Task Process() - { - - while (Attempts < MaxRetries) - { - Attempts++; - LastSentTime = Time.time; - - MultiplayerManager.m_Instance.SendCommandToPlayer(Command, TargetPlayerId); - - bool isAcknowledged = await MultiplayerManager.m_Instance.CheckCommandReception(Command, TargetPlayerId); - - if (isAcknowledged) - { - IsAcknowledged = true; - return true; - } - - Debug.Log($"Attempt {Attempts} failed for command. Retrying in {RetryDelay} seconds..."); - await Task.Delay(TimeSpan.FromSeconds(RetryDelay)); - RetryDelay *= 2; - } - - Debug.LogError($"Command acknowledgment failed after {MaxRetries} attempts."); - return false; - } - } - -} +} \ No newline at end of file diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs index 8af6a71079..43f0ae8663 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs @@ -25,6 +25,7 @@ using TiltBrush; using UnityEditor; using Photon.Pun; +using UnityEngine.SceneManagement; @@ -41,7 +42,7 @@ public class PhotonManager : IDataConnectionHandler, INetworkRunnerCallbacks private PhotonPlayerRig m_LocalPlayer; - private AppSettings m_PhotonAppSettings; + private FusionAppSettings m_PhotonAppSettings; public event Action Disconnected; @@ -56,7 +57,7 @@ public PhotonManager(MultiplayerManager manager) Init(); - m_PhotonAppSettings = new AppSettings + m_PhotonAppSettings = new FusionAppSettings { AppIdFusion = App.Config.PhotonFusionSecrets.ClientId, FixedRegion = "", @@ -149,6 +150,11 @@ public async Task JoinRoom(RoomCreateData roomCreateData) State = ConnectionState.JOINING_ROOM; + var sceneRef = SceneRef.FromIndex(SceneManager.GetActiveScene().buildIndex); + + var sceneInfo = new NetworkSceneInfo(); + sceneInfo.AddSceneRef(sceneRef, LoadSceneMode.Single); + var args = new StartGameArgs() { GameMode = GameMode.Shared, @@ -156,7 +162,7 @@ public async Task JoinRoom(RoomCreateData roomCreateData) CustomPhotonAppSettings = m_PhotonAppSettings, PlayerCount = roomCreateData.maxPlayers != 0 ? roomCreateData.maxPlayers : null, SceneManager = m_Runner.gameObject.GetComponent(), - Scene = UnityEngine.SceneManagement.SceneManager.GetActiveScene().buildIndex, + Scene = sceneInfo, // Pass the configured NetworkSceneInfo }; var result = await m_Runner.StartGame(args); @@ -243,8 +249,8 @@ public int GetPlayerCount() public int GetNetworkedTimestampMilliseconds() { - int tickRate = m_Runner.Simulation.Config.TickRate; - int networkTimeMilliseconds = (int)((m_Runner.Simulation.Tick * 1000) / (double)tickRate); + int tickRate = m_Runner.TickRate; // Access TickRate from Config directly + int networkTimeMilliseconds = (int)((m_Runner.Tick * 1000) / (double)tickRate); // Use m_Runner.Tick directly return networkTimeMilliseconds; } @@ -268,14 +274,14 @@ public async Task PerformCommand(BaseCommand command) public async Task SendCommandToPlayer(BaseCommand command, int playerId) { await Task.Yield(); - PlayerRef playerRef = playerId; + PlayerRef playerRef = PlayerRef.FromEncoded(playerId); return ProcessCommand(command, playerRef); } - public async Task CheckCommandReception(BaseCommand command, int id) + public async Task CheckCommandReception(BaseCommand command, int playerId) { - PlayerRef targetPlayer = id; - PhotonRPC.RPC_CheckCommand(m_Runner, command.Guid, m_Runner.LocalPlayer, targetPlayer); + PlayerRef targetPlayer = PlayerRef.FromEncoded(playerId); + PhotonRPC.RPC_CheckCommand(m_Runner,command.Guid, m_Runner.LocalPlayer, targetPlayer); return await PhotonRPC.WaitForAcknowledgment(command.Guid); } @@ -302,24 +308,24 @@ public async Task RpcSyncToSharedAnchor(string uuid) public async Task RpcStartSyncHistory(int id) { - PlayerRef playerRef = id; - PhotonRPC.RPC_StartHistorySync(m_Runner, id); + PlayerRef playerRef = PlayerRef.FromEncoded(id); + PhotonRPC.RPC_StartHistorySync(m_Runner, playerRef); await Task.Yield(); return true; } public async Task RpcHistorySyncComplete(int id) { - PlayerRef playerRef = id; - PhotonRPC.RPC_HistorySyncCompleted(m_Runner, id); + PlayerRef playerRef = PlayerRef.FromEncoded(id); + PhotonRPC.RPC_HistorySyncCompleted(m_Runner, playerRef); await Task.Yield(); return true; } public async Task RpcSyncHistoryPercentage(int id, int exp, int snt) { - PlayerRef playerRef = id; - PhotonRPC.RPC_HistoryPercentageUpdate(m_Runner, id, exp, snt); + PlayerRef playerRef = PlayerRef.FromEncoded(id); + PhotonRPC.RPC_HistoryPercentageUpdate(m_Runner, playerRef, exp, snt); await Task.Yield(); return true; } @@ -387,7 +393,7 @@ private bool CommandBrushStroke(BrushStrokeCommand command, PlayerRef playerRef var strokeGuid = Guid.NewGuid(); // First Stroke - PhotonRPC.RPC_BrushStrokeBegin(m_Runner, strokeGuid, netStroke, stroke.m_ControlPoints.Length, playerRef); + PhotonRPC.Send_BrushStrokeBegin(m_Runner, strokeGuid, netStroke, stroke.m_ControlPoints.Length); // Middle for (int rounds = 1; rounds < numSplits + 1; ++rounds) @@ -402,36 +408,36 @@ private bool CommandBrushStroke(BrushStrokeCommand command, PlayerRef playerRef netControlPoints[point] = new NetworkedControlPoint().Init(controlPoints[point]); } - PhotonRPC.RPC_BrushStrokeContinue(m_Runner, strokeGuid, rounds * maxPointsPerChunk, netControlPoints, dropPoints, playerRef); + PhotonRPC.Send_BrushStrokeContinue(m_Runner, strokeGuid, rounds * maxPointsPerChunk, netControlPoints, dropPoints); } // End - PhotonRPC.RPC_BrushStrokeComplete(m_Runner, strokeGuid, command.Guid, (int)command.NetworkTimestamp, command.ParentGuid, command.ChildrenCount, playerRef); + PhotonRPC.Send_BrushStrokeComplete(m_Runner, strokeGuid, command.Guid, (int)command.NetworkTimestamp, command.ParentGuid, command.ChildrenCount); } else { // Can send in one. - PhotonRPC.RPC_BrushStrokeFull(m_Runner, new NetworkedStroke().Init(command.m_Stroke), command.Guid, (int)command.NetworkTimestamp, command.ParentGuid, command.ChildrenCount, playerRef); + PhotonRPC.Send_BrushStrokeFull(m_Runner, new NetworkedStroke().Init(command.m_Stroke), command.Guid, (int)command.NetworkTimestamp, command.ParentGuid, command.ChildrenCount); } return true; } private bool CommandBase(BaseCommand command) { - PhotonRPC.RPC_BaseCommand(m_Runner, command.Guid, command.ParentGuid, command.ChildrenCount); + PhotonRPC.Send_BaseCommand(m_Runner, command.Guid, command.ParentGuid, command.ChildrenCount); return true; } private bool CommandDeleteStroke(DeleteStrokeCommand command, PlayerRef playerRef = default) { - PhotonRPC.RPC_DeleteStroke(m_Runner, command.m_TargetStroke.m_Seed, command.Guid, (int)command.NetworkTimestamp, command.ParentGuid, command.ChildrenCount, playerRef); + PhotonRPC.Send_DeleteStroke(m_Runner, command.m_TargetStroke.m_Seed, command.Guid, (int)command.NetworkTimestamp, command.ParentGuid, command.ChildrenCount, playerRef); return true; } private bool CommandSwitchEnvironment(SwitchEnvironmentCommand command, PlayerRef playerRef = default) { Guid environmentGuid = command.m_NextEnvironment.m_Guid; - PhotonRPC.RPC_SwitchEnvironment(m_Runner, environmentGuid, command.Guid, (int)command.NetworkTimestamp, command.ParentGuid, command.ChildrenCount, playerRef); + PhotonRPC.Send_SwitchEnvironment(m_Runner, environmentGuid, command.Guid, (int)command.NetworkTimestamp, command.ParentGuid, command.ChildrenCount, playerRef); return true; } #endregion @@ -440,8 +446,7 @@ private bool CommandSwitchEnvironment(SwitchEnvironmentCommand command, PlayerRe public void OnConnectedToServer(NetworkRunner runner) { - var rpc = m_Runner.gameObject.AddComponent(); - m_Runner.AddSimulationBehaviour(rpc); + var rpc = runner.gameObject.AddComponent(); } public void OnPlayerJoined(NetworkRunner runner, PlayerRef player) @@ -511,6 +516,11 @@ public void OnHostMigration(NetworkRunner runner, HostMigrationToken hostMigrati public void OnReliableDataReceived(NetworkRunner runner, PlayerRef player, ArraySegment data) { } public void OnSceneLoadDone(NetworkRunner runner) { } public void OnSceneLoadStart(NetworkRunner runner) { } + public void OnObjectExitAOI(NetworkRunner runner, NetworkObject obj, PlayerRef player) { } + public void OnObjectEnterAOI(NetworkRunner runner, NetworkObject obj, PlayerRef player){ } + public void OnDisconnectedFromServer(NetworkRunner runner, NetDisconnectReason reason) { } + public void OnReliableDataReceived(NetworkRunner runner, PlayerRef player, ReliableKey key, ArraySegment data) { } + public void OnReliableDataProgress(NetworkRunner runner, PlayerRef player, ReliableKey key, float progress) { } #endregion } diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonPlayerRig.cs b/Assets/Scripts/Multiplayer/Photon/PhotonPlayerRig.cs index 414a5ddfb5..8847f5aa8c 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonPlayerRig.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonPlayerRig.cs @@ -76,28 +76,28 @@ public PlayerRigData ReceiveData() var data = new PlayerRigData(); - if (m_PlayerHead?.InterpolationTarget != null) + if (m_PlayerHead?.transform != null) { - data.HeadPosition = m_PlayerHead.InterpolationTarget.position; - data.HeadRotation = m_PlayerHead.InterpolationTarget.rotation; + data.HeadPosition = m_PlayerHead.transform.position; + data.HeadRotation = m_PlayerHead.transform.rotation; } - if (m_Tool?.InterpolationTarget != null) + if (m_Tool?.transform != null) { - data.ToolPosition = m_Tool.InterpolationTarget.position; - data.ToolRotation = m_Tool.InterpolationTarget.rotation; + data.ToolPosition = m_Tool.transform.position; + data.ToolRotation = m_Tool.transform.rotation; } - if (m_Left?.InterpolationTarget != null) + if (m_Left?.transform != null) { - data.LeftHandPosition = m_Left.InterpolationTarget.position; - data.LeftHandRotation = m_Left.InterpolationTarget.rotation; + data.LeftHandPosition = m_Left.transform.position; + data.LeftHandRotation = m_Left.transform.rotation; } - if (m_Right?.InterpolationTarget != null) + if (m_Right?.transform != null) { - data.RightHandPosition = m_Right.InterpolationTarget.position; - data.RightHandRotation = m_Right.InterpolationTarget.rotation; + data.RightHandPosition = m_Right.transform.position; + data.RightHandRotation = m_Right.transform.rotation; } try @@ -159,8 +159,8 @@ public override void Render() if (Object.HasStateAuthority) { var remoteTR = TrTransform.TR( - m_PlayerHead.InterpolationTarget.position, - m_PlayerHead.InterpolationTarget.rotation + m_PlayerHead.transform.position, + m_PlayerHead.transform.rotation ); App.Scene.AsScene[headTransform] = remoteTR; @@ -170,8 +170,8 @@ public override void Render() { // Remote pointer var toolTR = TrTransform.TR( - m_Tool.InterpolationTarget.position, - m_Tool.InterpolationTarget.rotation + m_Tool.transform.position, + m_Tool.transform.rotation ); App.Scene.AsScene[transientPointer.transform] = toolTR; @@ -188,24 +188,24 @@ public override void Render() // Remote head var remoteTR = TrTransform.TRS( - m_PlayerHead.InterpolationTarget.position, - m_PlayerHead.InterpolationTarget.rotation, + m_PlayerHead.transform.position, + m_PlayerHead.transform.rotation, Scale ); App.Scene.AsScene[headTransform] = remoteTR; // Remote left hand var remoteLeftTR = TrTransform.TRS( - m_Left.InterpolationTarget.position, - m_Left.InterpolationTarget.rotation, + m_Left.transform.position, + m_Left.transform.rotation, Scale ); App.Scene.AsScene[leftHandTransform] = remoteLeftTR; // Remote right hand var remoteRightTR = TrTransform.TRS( - m_Right.InterpolationTarget.position, - m_Right.InterpolationTarget.rotation, + m_Right.transform.position, + m_Right.transform.rotation, Scale ); App.Scene.AsScene[rightHandTransform] = remoteRightTR; diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs b/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs index db40356b1e..1cae81bd96 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs @@ -148,7 +148,142 @@ private static BaseCommand FindParentCommand(Guid parentGuid) return null; } - public static void CreateBrushStroke(Stroke stroke, Guid commandGuid, int timestamp, Guid parentGuid = default, int childCount = 0) + public static void Send_BaseCommand(NetworkRunner runner, Guid commandGuid, Guid parentGuid = default, int childCount = 0, [RpcTarget] PlayerRef targetPlayer = default) + { + if (targetPlayer == default) + { + RPC_BaseCommand(runner, commandGuid, parentGuid, childCount); + } + else + { + RPC_BaseCommand(runner, commandGuid, parentGuid, childCount, targetPlayer); + } + } + + private static void BaseCommand(Guid commandGuid, Guid parentGuid = default, int childCount = 0) + { + if (CheckifCommandGuidIsInStack(commandGuid)) return; + + Debug.Log($"Base command child count: {childCount}"); + var parentCommand = FindParentCommand(parentGuid); + var command = new BaseCommand(parent: parentCommand); + + AddPendingCommand(() => { }, commandGuid, parentGuid, command, childCount); + } + + public static void Send_BrushStrokeFull(NetworkRunner runner, NetworkedStroke strokeData, Guid commandGuid, int timestamp, Guid parentGuid = default, int childCount = 0, [RpcTarget] PlayerRef targetPlayer = default) + { + if (targetPlayer == default) + { + RPC_BrushStrokeFull(runner, strokeData, commandGuid, timestamp, parentGuid, childCount); + } + else + { + RPC_BrushStrokeFull(runner, strokeData, commandGuid, timestamp, parentGuid, childCount, targetPlayer); + } + } + + private static void BrushStrokeFull(NetworkedStroke strokeData, Guid commandGuid, int timestamp, Guid parentGuid = default, int childCount = 0) + { + + if (CheckifCommandGuidIsInStack(commandGuid)) return; + + var decode = NetworkedStroke.ToStroke(strokeData); + + CreateBrushStroke(decode, commandGuid, timestamp, parentGuid, childCount); + } + + public static void Send_BrushStrokeBegin(NetworkRunner runner, Guid id, NetworkedStroke strokeData, int finalLength, [RpcTarget] PlayerRef targetPlayer = default) + { + if (targetPlayer == default) + { + RPC_BrushStrokeBegin(runner, id, strokeData, finalLength); + } + else + { + RPC_BrushStrokeBegin(runner, id, strokeData, finalLength, targetPlayer); + } + } + + private static void BrushStrokeBegin(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; + } + + public static void Send_BrushStrokeContinue(NetworkRunner runner, Guid id, int offset, NetworkedControlPoint[] controlPoints, bool[] dropPoints, [RpcTarget] PlayerRef targetPlayer = default) + { + if (targetPlayer == default) + { + RPC_BrushStrokeContinue(runner, id, offset, controlPoints, dropPoints); + } + else + { + RPC_BrushStrokeContinue(runner, id, offset, controlPoints, dropPoints, targetPlayer); + } + } + + private static void BrushStrokeContinue(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]; + } + } + + public static void Send_BrushStrokeComplete(NetworkRunner runner, Guid id, Guid commandGuid, int timestamp, Guid parentGuid = default, int childCount = 0, [RpcTarget] PlayerRef targetPlayer = default) + { + if (targetPlayer == default) + { + RPC_BrushStrokeComplete(runner, id, commandGuid, timestamp, parentGuid, childCount); + } + else + { + RPC_BrushStrokeComplete(runner, id, commandGuid, timestamp, parentGuid, childCount, targetPlayer); + } + } + + private static void BrushStrokeComplete( Guid id, Guid commandGuid, int timestamp, Guid parentGuid = default, int childCount = 0) + { + + if (CheckifCommandGuidIsInStack(commandGuid)) return; + + if (!m_inProgressStrokes.ContainsKey(id)) + { + Debug.LogError("shouldn't be here!"); + return; + } + + var stroke = m_inProgressStrokes[id]; + + CreateBrushStroke(stroke, commandGuid, timestamp, parentGuid, childCount); + + m_inProgressStrokes.Remove(id); + } + + private static void CreateBrushStroke(Stroke stroke, Guid commandGuid, int timestamp, Guid parentGuid = default, int childCount = 0) { Action preAction = () => @@ -166,6 +301,71 @@ public static void CreateBrushStroke(Stroke stroke, Guid commandGuid, int times AddPendingCommand(preAction, commandGuid, parentGuid, command, childCount); } + public static void Send_DeleteStroke(NetworkRunner runner, int seed, Guid commandGuid, int timestamp, Guid parentGuid = default, int childCount = 0, [RpcTarget] PlayerRef targetPlayer = default) + { + if (targetPlayer == default) + { + RPC_DeleteStroke(runner, seed, commandGuid, timestamp, parentGuid, childCount); + } + else + { + RPC_DeleteStroke(runner, seed, commandGuid, timestamp, parentGuid, childCount, targetPlayer); + } + } + + private static void DeleteStroke(int seed, Guid commandGuid, int timestamp, Guid parentGuid = default, int childCount = 0) + { + if (CheckifCommandGuidIsInStack(commandGuid)) return; + + // TODO : implment GUID for strokesdata. + // The range of int is large (-2,147,483,648 to 2,147,483,647), but collisions are still possible. + 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, commandGuid, timestamp, parent: parentCommand); + + AddPendingCommand(() => { }, commandGuid, parentGuid, command, childCount); + } + else + { + Debug.LogError($"couldn't find stroke with seed: {seed}"); + } + } + + public static void Send_SwitchEnvironment(NetworkRunner runner, Guid environmentGuid, Guid commandGuid, int timestamp, Guid parentGuid = default, int childCount = 0, [RpcTarget] PlayerRef targetPlayer = default) { + + if (targetPlayer == default) + { + RPC_SwitchEnvironment(runner, environmentGuid, commandGuid, timestamp, parentGuid, childCount); + } + else + { + RPC_SwitchEnvironment(runner, environmentGuid, commandGuid, timestamp, parentGuid, childCount, targetPlayer); + } + } + + private static void SwitchEnvironment(Guid environmentGuid, Guid commandGuid, int timestamp, Guid parentGuid = default, int childCount = 0) + { + if (CheckifCommandGuidIsInStack(commandGuid)) return; + + TiltBrush.Environment environment = EnvironmentCatalog.m_Instance.GetEnvironment(environmentGuid); + + if (environment != null) + { + + var parentCommand = FindParentCommand(parentGuid); + var command = new SwitchEnvironmentCommand(environment, commandGuid, timestamp, parent: parentCommand); + + AddPendingCommand(() => { }, commandGuid, parentGuid, command, childCount); + } + else + { + Debug.LogError($"Environment with Guid {environmentGuid} not found."); + } + } + public static async Task WaitForAcknowledgment(Guid commandGuid, int timeoutMilliseconds = 1000) { var tcs = new TaskCompletionSource(); @@ -189,7 +389,7 @@ public static async Task WaitForAcknowledgment(Guid commandGuid, int timeo #region RPCS [Rpc(InvokeLocal = false)] - public static void RPC_SyncToSharedAnchor(NetworkRunner runner, string uuid, [RpcTarget] PlayerRef targetPlayer = default) + public static void RPC_SyncToSharedAnchor(NetworkRunner runner, string uuid) { #if OCULUS_SUPPORTED OculusMRController.m_Instance.RemoteSyncToAnchor(uuid); @@ -197,7 +397,7 @@ public static void RPC_SyncToSharedAnchor(NetworkRunner runner, string uuid, [Rp } [Rpc(InvokeLocal = false)] - public static void RPC_PerformCommand(NetworkRunner runner, string commandName, string guid, string[] data, [RpcTarget] PlayerRef targetPlayer = default) + public static void RPC_PerformCommand(NetworkRunner runner, string commandName, string guid, string[] data) { Debug.Log($"Command recieved: {commandName}"); @@ -223,7 +423,7 @@ public static void RPC_PerformCommand(NetworkRunner runner, string commandName, } [Rpc(InvokeLocal = false)] - public static void RPC_Undo(NetworkRunner runner, string commandName, [RpcTarget] PlayerRef targetPlayer = default) + public static void RPC_Undo(NetworkRunner runner, string commandName) { if (SketchMemoryScript.m_Instance.CanUndo()) { @@ -232,7 +432,7 @@ public static void RPC_Undo(NetworkRunner runner, string commandName, [RpcTarget } [Rpc(InvokeLocal = false)] - public static void RPC_Redo(NetworkRunner runner, string commandName, [RpcTarget] PlayerRef targetPlayer = default) + public static void RPC_Redo(NetworkRunner runner, string commandName) { if (SketchMemoryScript.m_Instance.CanRedo()) { @@ -241,126 +441,87 @@ public static void RPC_Redo(NetworkRunner runner, string commandName, [RpcTarget } [Rpc(InvokeLocal = false)] - public static void RPC_BaseCommand(NetworkRunner runner, Guid commandGuid, Guid parentGuid = default, int childCount = 0, [RpcTarget] PlayerRef targetPlayer = default) + private static void RPC_BaseCommand(NetworkRunner runner, Guid commandGuid, Guid parentGuid = default, int childCount = 0, [RpcTarget] PlayerRef targetPlayer = default) { - if (CheckifCommandGuidIsInStack(commandGuid)) return; - - Debug.Log($"Base command child count: {childCount}"); - var parentCommand = FindParentCommand(parentGuid); - var command = new BaseCommand(parent: parentCommand); - - AddPendingCommand(() => {}, commandGuid, parentGuid, command, childCount); + BaseCommand(commandGuid, parentGuid, childCount); } [Rpc(InvokeLocal = false)] - public static void RPC_BrushStrokeFull(NetworkRunner runner, NetworkedStroke strokeData, Guid commandGuid, int timestamp, Guid parentGuid = default, int childCount = 0, [RpcTarget] PlayerRef targetPlayer = default) + private static void RPC_BaseCommand(NetworkRunner runner, Guid commandGuid, Guid parentGuid = default, int childCount = 0) { - - if (CheckifCommandGuidIsInStack(commandGuid)) return; - - var decode = NetworkedStroke.ToStroke(strokeData); - - CreateBrushStroke(decode, commandGuid, timestamp , parentGuid, childCount); + BaseCommand(commandGuid, parentGuid, childCount); } [Rpc(InvokeLocal = false)] - public static void RPC_BrushStrokeBegin(NetworkRunner runner, Guid id, NetworkedStroke strokeData, int finalLength, [RpcTarget] PlayerRef targetPlayer = default) + private static void RPC_BrushStrokeFull(NetworkRunner runner, NetworkedStroke strokeData, Guid commandGuid, int timestamp, Guid parentGuid = default, int childCount = 0, [RpcTarget] PlayerRef targetPlayer = default) { - 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; + BrushStrokeFull(strokeData, commandGuid, timestamp, parentGuid, childCount); } - + [Rpc(InvokeLocal = false)] - public static void RPC_BrushStrokeContinue(NetworkRunner runner, Guid id, int offset, NetworkedControlPoint[] controlPoints, bool[] dropPoints, [RpcTarget] PlayerRef targetPlayer = default) + private static void RPC_BrushStrokeFull(NetworkRunner runner, NetworkedStroke strokeData, Guid commandGuid, int timestamp, Guid parentGuid = default, int childCount = 0) { - 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]; - } + BrushStrokeFull(strokeData, commandGuid, timestamp, parentGuid, childCount); } [Rpc(InvokeLocal = false)] - public static void RPC_BrushStrokeComplete(NetworkRunner runner, Guid id, Guid commandGuid, int timestamp, Guid parentGuid = default, int childCount = 0, [RpcTarget] PlayerRef targetPlayer = default) + private static void RPC_BrushStrokeBegin(NetworkRunner runner, Guid id, NetworkedStroke strokeData, int finalLength, [RpcTarget] PlayerRef targetPlayer = default) { + BrushStrokeBegin(id, strokeData, finalLength); + } - if (CheckifCommandGuidIsInStack(commandGuid)) return; - - if (!m_inProgressStrokes.ContainsKey(id)) - { - Debug.LogError("shouldn't be here!"); - return; - } - - var stroke = m_inProgressStrokes[id]; - - CreateBrushStroke(stroke, commandGuid, timestamp, parentGuid, childCount); - - m_inProgressStrokes.Remove(id); + [Rpc(InvokeLocal = false)] + private static void RPC_BrushStrokeBegin(NetworkRunner runner, Guid id, NetworkedStroke strokeData, int finalLength) + { + BrushStrokeBegin(id, strokeData, finalLength); } [Rpc(InvokeLocal = false)] - public static void RPC_DeleteStroke(NetworkRunner runner, int seed, Guid commandGuid, int timestamp, Guid parentGuid = default, int childCount = 0, [RpcTarget] PlayerRef targetPlayer = default) + private static void RPC_BrushStrokeContinue(NetworkRunner runner, Guid id, int offset, NetworkedControlPoint[] controlPoints, bool[] dropPoints, [RpcTarget] PlayerRef targetPlayer = default) { - if (CheckifCommandGuidIsInStack(commandGuid)) return; + BrushStrokeContinue(id, offset, controlPoints, dropPoints); + } - // TODO : implment GUID for strokesdata. - // The range of int is large (-2,147,483,648 to 2,147,483,647), but collisions are still possible. - var foundStroke = SketchMemoryScript.m_Instance.GetMemoryList.Where(x => x.m_Seed == seed).First(); + [Rpc(InvokeLocal = false)] + private static void RPC_BrushStrokeContinue(NetworkRunner runner, Guid id, int offset, NetworkedControlPoint[] controlPoints, bool[] dropPoints) + { + BrushStrokeContinue(id, offset, controlPoints, dropPoints); + } - if (foundStroke != null) - { - var parentCommand = FindParentCommand(parentGuid); - var command = new DeleteStrokeCommand(foundStroke, commandGuid, timestamp, parent: parentCommand); + [Rpc(InvokeLocal = false)] + private static void RPC_BrushStrokeComplete(NetworkRunner runner, Guid id, Guid commandGuid, int timestamp, Guid parentGuid = default, int childCount = 0, [RpcTarget] PlayerRef targetPlayer = default) + { + BrushStrokeComplete(id, commandGuid, timestamp, parentGuid, childCount); + } - AddPendingCommand(() => {}, commandGuid, parentGuid, command, childCount); - } - else - { - Debug.LogError($"couldn't find stroke with seed: {seed}"); - } + [Rpc(InvokeLocal = false)] + private static void RPC_BrushStrokeComplete(NetworkRunner runner, Guid id, Guid commandGuid, int timestamp, Guid parentGuid = default, int childCount = 0) + { + BrushStrokeComplete(id, commandGuid, timestamp, parentGuid, childCount); } [Rpc(InvokeLocal = false)] - public static void RPC_SwitchEnvironment(NetworkRunner runner, Guid environmentGuid, Guid commandGuid, int timestamp, Guid parentGuid = default, int childCount = 0, [RpcTarget] PlayerRef targetPlayer = default) + private static void RPC_DeleteStroke(NetworkRunner runner, int seed, Guid commandGuid, int timestamp, Guid parentGuid = default, int childCount = 0, [RpcTarget] PlayerRef targetPlayer = default) { - if (CheckifCommandGuidIsInStack(commandGuid)) return; + DeleteStroke(seed, commandGuid, timestamp, parentGuid, childCount); + } - TiltBrush.Environment environment = EnvironmentCatalog.m_Instance.GetEnvironment(environmentGuid); + [Rpc(InvokeLocal = false)] + private static void RPC_DeleteStroke(NetworkRunner runner, int seed, Guid commandGuid, int timestamp, Guid parentGuid = default, int childCount = 0) + { + DeleteStroke(seed, commandGuid, timestamp, parentGuid, childCount); + } - if (environment != null) - { - - var parentCommand = FindParentCommand(parentGuid); - var command = new SwitchEnvironmentCommand(environment, commandGuid, timestamp, parent: parentCommand); + [Rpc(InvokeLocal = false)] + private static void RPC_SwitchEnvironment(NetworkRunner runner, Guid environmentGuid, Guid commandGuid, int timestamp, Guid parentGuid = default, int childCount = 0, [RpcTarget] PlayerRef targetPlayer = default) + { + SwitchEnvironment(environmentGuid, commandGuid, timestamp, parentGuid, childCount); + } - AddPendingCommand(() => { }, commandGuid, parentGuid, command, childCount); - } - else - { - Debug.LogError($"Environment with Guid {environmentGuid} not found."); - } + [Rpc(InvokeLocal = false)] + private static void RPC_SwitchEnvironment(NetworkRunner runner, Guid environmentGuid, Guid commandGuid, int timestamp, Guid parentGuid = default, int childCount = 0) + { + SwitchEnvironment(environmentGuid, commandGuid, timestamp, parentGuid, childCount); } [Rpc(InvokeLocal = false)] From 59a94b5109362a30479029401333e23fc337a67e Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Thu, 5 Dec 2024 16:09:25 +0000 Subject: [PATCH 125/174] Implementing Large Data Streaming [skip ci] Leveraging Fusion 2 to transmit the latest autosaved data. --- .../Multiplayer/MultiplayerInterfaces.cs | 1 + .../Scripts/Multiplayer/MultiplayerManager.cs | 35 +++++++--- .../Multiplayer/Photon/PhotonManager.cs | 32 +++++++-- Assets/Scripts/Save/SaveLoadScript.cs | 65 +++++++++++++++++++ Assets/Scripts/Save/SketchSnapshot.cs | 49 ++++++++++++++ 5 files changed, 168 insertions(+), 14 deletions(-) diff --git a/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs b/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs index 4cd29965a1..4912af9fbd 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs @@ -36,6 +36,7 @@ public interface IDataConnectionHandler : IConnectionHandler int GetPlayerCount(); int GetNetworkedTimestampMilliseconds(); bool GetPlayerRoomOwnershipStatus(int playerId); + void SendLargeDataToPlayer(int playerId, byte[] largeData); Task PerformCommand(BaseCommand command); Task SendCommandToPlayer(BaseCommand command, int playerId); Task CheckCommandReception(BaseCommand command, int playerId); diff --git a/Assets/Scripts/Multiplayer/MultiplayerManager.cs b/Assets/Scripts/Multiplayer/MultiplayerManager.cs index 68882aa4dd..3cffd137df 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerManager.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerManager.cs @@ -17,14 +17,6 @@ using System.Linq; using System.Threading.Tasks; using UnityEngine; -using System.Collections; -using Fusion; -using System.Security.Cryptography; -using TMPro; - - - - #if OCULUS_SUPPORTED using OVRPlatform = Oculus.Platform; @@ -57,9 +49,12 @@ public class MultiplayerManager : MonoBehaviour public Action> remotePlayerJoined; public Action playerLeft; public Action> roomDataRefreshed; + public Action onLargeDataReceived; + public event Action StateUpdated; public event Action RoomOwnershipUpdated; public event Action UserInfoStateUpdated; + private List m_RoomData = new List(); private double? m_NetworkOffsetTimestamp = null; @@ -161,6 +156,8 @@ void Start() remotePlayerJoined += OnRemotePlayerJoined; playerLeft += OnPlayerLeft; StateUpdated += UpdateSketchMemoryScriptTimeOffset; + onLargeDataReceived += OnLargeDataReceived; + SketchMemoryScript.m_Instance.CommandPerformed += OnCommandPerformed; SketchMemoryScript.m_Instance.CommandUndo += OnCommandUndo; SketchMemoryScript.m_Instance.CommandRedo += OnCommandRedo; @@ -173,6 +170,8 @@ void OnDestroy() remotePlayerJoined -= OnRemotePlayerJoined; playerLeft -= OnPlayerLeft; StateUpdated -= UpdateSketchMemoryScriptTimeOffset; + onLargeDataReceived -= OnLargeDataReceived; + SketchMemoryScript.m_Instance.CommandPerformed -= OnCommandPerformed; SketchMemoryScript.m_Instance.CommandUndo -= OnCommandUndo; SketchMemoryScript.m_Instance.CommandRedo -= OnCommandRedo; @@ -406,10 +405,27 @@ void OnRemotePlayerJoined(int id, ITransientData playerData) if (isUserRoomOwner) { - HistorySynchronizationManager.m_Instance.StartSyncronizationForUser(id); + StartCoroutine(SaveLoadScript.m_Instance.GetLastAutosaveBytes((byte[] autosaveBytes) => + { + if (autosaveBytes != null) + { + Debug.Log($"Successfully retrieved {autosaveBytes.Length} bytes from the autosave."); + m_Manager.SendLargeDataToPlayer(id, autosaveBytes); + } + else + { + Debug.LogWarning("Failed to retrieve autosave bytes. Proceed to share command history"); + HistorySynchronizationManager.m_Instance.StartSyncronizationForUser(id); + } + })); } } + void OnLargeDataReceived(byte[] largeData) + { + SaveLoadScript.m_Instance.LoadFromBytes(largeData); + } + void OnPlayerLeft(int id) { if (m_LocalPlayer.PlayerId == id) @@ -614,6 +630,7 @@ private void UpdateSketchMemoryScriptTimeOffset(ConnectionState state) } + } } diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs index 43f0ae8663..5644978cfd 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs @@ -24,11 +24,8 @@ using Fusion.Sockets; using TiltBrush; using UnityEditor; -using Photon.Pun; using UnityEngine.SceneManagement; - - namespace OpenBrush.Multiplayer { public class PhotonManager : IDataConnectionHandler, INetworkRunnerCallbacks @@ -330,6 +327,13 @@ public async Task RpcSyncHistoryPercentage(int id, int exp, int snt) return true; } + public void SendLargeDataToPlayer(int playerId, byte[] largeData) + { + PlayerRef playerRef = PlayerRef.FromEncoded(playerId); + var key = ReliableKey.FromInts(42, 0, 0, 0); + m_Runner.SendReliableDataToPlayer(playerRef, key, largeData); + } + #endregion #region Command Methods @@ -498,6 +502,26 @@ public void OnSessionListUpdated(NetworkRunner runner, List session m_Manager.roomDataRefreshed?.Invoke(roomData); } + + public void OnReliableDataReceived(NetworkRunner runner, PlayerRef player, ReliableKey key, ArraySegment data) { + + Debug.Log("Server received reliable data"); + + byte[] receivedData = data.Array; + if (receivedData == null || receivedData.Length == 0) + { + Debug.LogWarning("Received data is null or empty."); + return; + } + + m_Manager.onLargeDataReceived?.Invoke(receivedData); + } + + public void OnReliableDataProgress(NetworkRunner runner, PlayerRef player, ReliableKey key, float progress) { + + Debug.Log("Server received reliable data"); + } + #endregion #region Unused Photon Callbacks @@ -519,8 +543,6 @@ public void OnSceneLoadStart(NetworkRunner runner) { } public void OnObjectExitAOI(NetworkRunner runner, NetworkObject obj, PlayerRef player) { } public void OnObjectEnterAOI(NetworkRunner runner, NetworkObject obj, PlayerRef player){ } public void OnDisconnectedFromServer(NetworkRunner runner, NetDisconnectReason reason) { } - public void OnReliableDataReceived(NetworkRunner runner, PlayerRef player, ReliableKey key, ArraySegment data) { } - public void OnReliableDataProgress(NetworkRunner runner, PlayerRef player, ReliableKey key, float progress) { } #endregion } diff --git a/Assets/Scripts/Save/SaveLoadScript.cs b/Assets/Scripts/Save/SaveLoadScript.cs index 3b4525b418..9b490d66d5 100644 --- a/Assets/Scripts/Save/SaveLoadScript.cs +++ b/Assets/Scripts/Save/SaveLoadScript.cs @@ -1021,6 +1021,71 @@ public SketchSnapshot CreateSnapshotWithIcons(out IEnumerator corouti m_CaptureGifSaveIcon ? m_SaveGifRenderTextures : null)); return snapshot; } + + public IEnumerator GetLastAutosaveBytes(Action onComplete) + { + + while (m_AutosaveCoroutine != null) yield return null; + + // Retrieve the autosaved file + string autosaveFile = MostRecentAutosaveFile(); + if (!string.IsNullOrEmpty(autosaveFile) && File.Exists(autosaveFile)) + { + try + { + byte[] fileBytes = File.ReadAllBytes(autosaveFile); + Debug.Log($"Autosave complete. Loaded {fileBytes.Length} bytes from {autosaveFile}"); + onComplete?.Invoke(fileBytes); + } + catch (IOException ex) + { + Debug.LogError($"Failed to read autosave file: {ex.Message}"); + onComplete?.Invoke(null); + } + } + else + { + Debug.LogWarning("Autosave file not found or doesn't exist."); + onComplete?.Invoke(null); + } + } + + public void LoadFromBytes(byte[] data) + { + if (data == null || data.Length == 0) + { + Debug.LogError("LoadFromBytes: Data is null or empty."); + return; + } + + try + { + // Write the byte array to a temporary file + string tempFilePath = Path.Combine(Application.temporaryCachePath, "temp_autosave.tilt"); + File.WriteAllBytes(tempFilePath, data); + + // Load the temporary file into the scene + var fileInfo = new DiskSceneFileInfo(tempFilePath); + if (Load(fileInfo)) + { + Debug.Log("LoadFromBytes: Scene successfully loaded from bytes."); + } + else + { + Debug.LogError("LoadFromBytes: Failed to load scene."); + } + + // Clean up the temporary file + if (File.Exists(tempFilePath)) + { + File.Delete(tempFilePath); + } + } + catch (Exception ex) + { + Debug.LogError($"LoadFromBytes: Error while loading scene from bytes. Exception: {ex.Message}"); + } + } } } // namespace TiltBrush diff --git a/Assets/Scripts/Save/SketchSnapshot.cs b/Assets/Scripts/Save/SketchSnapshot.cs index 3ac268354e..af4e44e35c 100644 --- a/Assets/Scripts/Save/SketchSnapshot.cs +++ b/Assets/Scripts/Save/SketchSnapshot.cs @@ -19,6 +19,8 @@ using System.Text; using UnityEngine; using Newtonsoft.Json; +using ICSharpCode.SharpZipLib.Zip; +using static TiltBrush.SketchWriter; namespace TiltBrush { @@ -211,6 +213,53 @@ static Guid GetForcePrecededBy(Guid original) return brush.m_Guid; } + public string WriteSnapshotToStream(Stream outputStream) + { + try + { + using (var zip = new ZipOutputStream(outputStream)) + { + zip.SetLevel(9); // Set compression level + + // Write metadata + zip.PutNextEntry(new ZipEntry(TiltFile.FN_METADATA)); + using (var writer = new StreamWriter(zip, Encoding.UTF8, 1024, true)) + { + m_JsonSerializer.Serialize(writer, m_Metadata); + } + zip.CloseEntry(); + + // Prepare the necessary data for WriteMemory + List strokes = new List(SketchMemoryScript.m_Instance.GetMemoryList); + IList strokeCopies = EnumerateAdjustedSnapshots(strokes).ToList(); + GroupIdMapping groupIdMapping = new GroupIdMapping(); + List brushList; + + // Write sketch data + zip.PutNextEntry(new ZipEntry(TiltFile.FN_SKETCH)); + WriteMemory(zip, strokeCopies, groupIdMapping, out brushList); + zip.CloseEntry(); + + // Write thumbnail if available + if (Thumbnail != null) + { + zip.PutNextEntry(new ZipEntry(TiltFile.FN_THUMBNAIL)); + zip.Write(Thumbnail, 0, Thumbnail.Length); + zip.CloseEntry(); + } + + // Add other necessary files as needed + } + + return null; // No error + } + catch (Exception ex) + { + return ex.Message; + } + } + + /// Returns null on successful completion. If IO or UnauthorizedAccess exceptions are thrown, /// returns their messages. Should not normally raise exceptions. public string WriteSnapshotToFile(string path) From 3eba50f0a21d4e2af776818ce6e86048ec420295 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Thu, 5 Dec 2024 16:29:36 +0000 Subject: [PATCH 126/174] Update build.yml update with new symbol FUSION2 and new tag Fusion_v2_Voice_2 for icosa-mirror/photon-fusion --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 617e8cc0aa..18026c71d2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -352,7 +352,7 @@ jobs: with: token: ${{ env.PHOTON_PAT }} repository: icosa-mirror/photon-fusion - ref: Fusion_v1.1.10_Voice_2 + ref: Fusion_v2_Voice_2 path: photon-fusion-mirror/ - name: Copy photon files @@ -452,7 +452,7 @@ jobs: - name: Add PHOTON_PAT specific define if: ${{ env.PHOTON_PAT }} run: | - echo -e "\n -define:PHOTON_UNITY_NETWORKING \n-define:PUN_2_0_OR_NEWER \n-define:PUN_2_OR_NEWER \n-define:PUN_2_19_OR_NEWER \n-define:FUSION_WEAVER \n-define:CROSS_PLATFORM_INPUT \n-define:MOBILE_INPUT \n-define:PHOTON_VOICE_DEFINED" | tee -a Assets/csc.rsp + echo -e "\n -define:PHOTON_UNITY_NETWORKING \n-define:PUN_2_0_OR_NEWER \n-define:PUN_2_OR_NEWER \n-define:PUN_2_19_OR_NEWER \n-define:FUSION_WEAVER \n-define:FUSION2 \n-define:CROSS_PLATFORM_INPUT \n-define:MOBILE_INPUT \n-define:PHOTON_VOICE_DEFINED" | tee -a Assets/csc.rsp - name: Update build matrix specific defines in csc.rsp if: ${{ matrix.extra_defines }} From 330aa5bbbfa1ea4c01aaef59668db1b2f62c48cb Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Thu, 5 Dec 2024 17:02:43 +0000 Subject: [PATCH 127/174] Update UnityGLTFSettings.asset --- Assets/Resources/UnityGLTFSettings.asset | 29 +++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/Assets/Resources/UnityGLTFSettings.asset b/Assets/Resources/UnityGLTFSettings.asset index b7aaddc95f..643766150c 100644 --- a/Assets/Resources/UnityGLTFSettings.asset +++ b/Assets/Resources/UnityGLTFSettings.asset @@ -32,6 +32,18 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 056f401eae1a4f76b6f580ebf76127a9, type: 3} m_Name: GPUInstancingImport m_EditorClassIdentifier: +--- !u!114 &-7610138946625775758 +MonoBehaviour: + m_ObjectHideFlags: 3 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: c261146868c45eb4eb6b9987bd8c9084, type: 3} + m_Name: OpenBrushExportPlugin + m_EditorClassIdentifier: --- !u!114 &-7373113640993280472 MonoBehaviour: m_ObjectHideFlags: 3 @@ -63,6 +75,18 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 9571021a85f04ddfb74e8aa5aad9cc5a, type: 3} m_Name: UnlitMaterialsExport m_EditorClassIdentifier: +--- !u!114 &-6092789625841630549 +MonoBehaviour: + m_ObjectHideFlags: 3 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 6843a59b1a844430b7b7c08e8711a6d7, type: 3} + m_Name: OpenBrushLightsImport + m_EditorClassIdentifier: --- !u!114 &-5728475199642485532 MonoBehaviour: m_ObjectHideFlags: 3 @@ -182,7 +206,7 @@ MonoBehaviour: - {fileID: -5020980606651475385} - {fileID: 8372111537548844026} - {fileID: -7716978867629807533} - - {fileID: 6916234453510156686} + - {fileID: -6092789625841630549} ExportPlugins: - {fileID: 242952683485160214} - {fileID: -5728475199642485532} @@ -194,8 +218,7 @@ MonoBehaviour: - {fileID: -7373113640993280472} - {fileID: 7420168740226561727} - {fileID: -6755212205620999988} - - {fileID: 8207833083978872644} - - {fileID: -682178813687408182} + - {fileID: -7610138946625775758} exportNames: 1 exportFullPath: 0 requireExtensions: 0 From 0192aacb1bcf10e41a3ab5c7971b16242bd65ed2 Mon Sep 17 00:00:00 2001 From: Mike Miller Date: Thu, 5 Dec 2024 20:45:06 +0200 Subject: [PATCH 128/174] Fix formatting in Multiplayer defines --- .../Multiplayer/Photon/PhotonManager.cs | 16 +++++---- .../Multiplayer/Photon/PhotonPlayerRig.cs | 18 +++++----- .../Scripts/Multiplayer/Photon/PhotonRPC.cs | 33 ++++++++++--------- .../Multiplayer/Photon/PhotonStructs.cs | 6 ++-- 4 files changed, 39 insertions(+), 34 deletions(-) diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs index 5644978cfd..36104566ad 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs @@ -87,7 +87,7 @@ public async Task Init() State = ConnectionState.INITIALIZED; return true; } - + public void Update() { var copy = m_PlayersSpawning.ToList(); @@ -259,7 +259,7 @@ public bool GetPlayerRoomOwnershipStatus(int playerId) if (remotePlayer != null && remotePlayer.Object != null && remotePlayer.Object.IsValid) return remotePlayer.IsRoomOwner; - else return false; + else return false; } public async Task PerformCommand(BaseCommand command) @@ -278,7 +278,7 @@ public async Task SendCommandToPlayer(BaseCommand command, int playerId) public async Task CheckCommandReception(BaseCommand command, int playerId) { PlayerRef targetPlayer = PlayerRef.FromEncoded(playerId); - PhotonRPC.RPC_CheckCommand(m_Runner,command.Guid, m_Runner.LocalPlayer, targetPlayer); + PhotonRPC.RPC_CheckCommand(m_Runner, command.Guid, m_Runner.LocalPlayer, targetPlayer); return await PhotonRPC.WaitForAcknowledgment(command.Guid); } @@ -455,7 +455,7 @@ public void OnConnectedToServer(NetworkRunner runner) public void OnPlayerJoined(NetworkRunner runner, PlayerRef player) { - + try { @@ -503,7 +503,8 @@ public void OnSessionListUpdated(NetworkRunner runner, List session m_Manager.roomDataRefreshed?.Invoke(roomData); } - public void OnReliableDataReceived(NetworkRunner runner, PlayerRef player, ReliableKey key, ArraySegment data) { + public void OnReliableDataReceived(NetworkRunner runner, PlayerRef player, ReliableKey key, ArraySegment data) + { Debug.Log("Server received reliable data"); @@ -517,7 +518,8 @@ public void OnReliableDataReceived(NetworkRunner runner, PlayerRef player, Relia m_Manager.onLargeDataReceived?.Invoke(receivedData); } - public void OnReliableDataProgress(NetworkRunner runner, PlayerRef player, ReliableKey key, float progress) { + public void OnReliableDataProgress(NetworkRunner runner, PlayerRef player, ReliableKey key, float progress) + { Debug.Log("Server received reliable data"); } @@ -541,7 +543,7 @@ public void OnReliableDataReceived(NetworkRunner runner, PlayerRef player, Array public void OnSceneLoadDone(NetworkRunner runner) { } public void OnSceneLoadStart(NetworkRunner runner) { } public void OnObjectExitAOI(NetworkRunner runner, NetworkObject obj, PlayerRef player) { } - public void OnObjectEnterAOI(NetworkRunner runner, NetworkObject obj, PlayerRef player){ } + public void OnObjectEnterAOI(NetworkRunner runner, NetworkObject obj, PlayerRef player) { } public void OnDisconnectedFromServer(NetworkRunner runner, NetDisconnectReason reason) { } #endregion diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonPlayerRig.cs b/Assets/Scripts/Multiplayer/Photon/PhotonPlayerRig.cs index 8847f5aa8c..cd81817bf6 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonPlayerRig.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonPlayerRig.cs @@ -44,7 +44,7 @@ public class PhotonPlayerRig : NetworkBehaviour, ITransientData [SerializeField] private Transform leftHandTransform; private PlayerRigData transmitData; - + private bool m_IsSpawned = false; public bool IsSpawned => m_IsSpawned; @@ -71,9 +71,9 @@ public void TransmitData(PlayerRigData data) } public PlayerRigData ReceiveData() - { + { if (!m_IsSpawned) return default; - + var data = new PlayerRigData(); if (m_PlayerHead?.transform != null) @@ -102,7 +102,7 @@ public PlayerRigData ReceiveData() try { - data.IsRoomOwner = this.IsRoomOwner; + data.IsRoomOwner = this.IsRoomOwner; data.ExtraData = new ExtraData { OculusPlayerId = this.oculusPlayerId }; data.SceneScale = this.SceneScale; } @@ -120,7 +120,7 @@ public override void Spawned() brushGuid = BrushCatalog.m_Instance.DefaultBrush.m_Guid.ToString(); - if(!Object.HasStateAuthority) + if (!Object.HasStateAuthority) { transientPointer = PointerManager.m_Instance.CreateRemotePointer(); transientPointer.SetBrush(BrushCatalog.m_Instance.DefaultBrush); @@ -136,7 +136,7 @@ public override void FixedUpdateNetwork() { base.FixedUpdateNetwork(); - if(Object.HasStateAuthority) + if (Object.HasStateAuthority) { m_PlayerHead.transform.position = transmitData.HeadPosition; m_PlayerHead.transform.rotation = transmitData.HeadRotation; @@ -162,10 +162,10 @@ public override void Render() m_PlayerHead.transform.position, m_PlayerHead.transform.rotation ); - App.Scene.AsScene[headTransform] = remoteTR; + App.Scene.AsScene[headTransform] = remoteTR; } - + else { // Remote pointer @@ -176,7 +176,7 @@ public override void Render() App.Scene.AsScene[transientPointer.transform] = toolTR; transientPointer.SetColor(brushColor); - if(brushGuid.ToString() != string.Empty) + if (brushGuid.ToString() != string.Empty) { transientPointer.SetBrush(BrushCatalog.m_Instance.GetBrush(new System.Guid(brushGuid.ToString()))); } diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs b/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs index 1cae81bd96..a071b82941 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs @@ -44,7 +44,7 @@ public void Update() } private bool CheckifChildStillPending(PendingCommand pending) - { + { if (pending.TotalExpectedChildren == pending.Command.ChildrenCount) { bool moreChildrenToAssign = false; @@ -103,7 +103,7 @@ private void TryProcessCommands() } // All children present, begin execution - + m_pendingCommands.RemoveAt(0); InvokePreCommands(command); @@ -128,11 +128,13 @@ private static void AddPendingCommand(Action preAction, Guid commandGuid, Guid p m_pendingCommands.Add(pendingCommand); } - private static bool CheckifCommandGuidIsInStack(Guid commandGuid) { + private static bool CheckifCommandGuidIsInStack(Guid commandGuid) + { - if (SketchMemoryScript.m_Instance.IsCommandInStack(commandGuid)) { + if (SketchMemoryScript.m_Instance.IsCommandInStack(commandGuid)) + { //Debug.Log($"Command with Guid {commandGuid} already in stack."); - return true; + return true; } return false; } @@ -265,7 +267,7 @@ public static void Send_BrushStrokeComplete(NetworkRunner runner, Guid id, Guid } } - private static void BrushStrokeComplete( Guid id, Guid commandGuid, int timestamp, Guid parentGuid = default, int childCount = 0) + private static void BrushStrokeComplete(Guid id, Guid commandGuid, int timestamp, Guid parentGuid = default, int childCount = 0) { if (CheckifCommandGuidIsInStack(commandGuid)) return; @@ -283,7 +285,7 @@ private static void BrushStrokeComplete( Guid id, Guid commandGuid, int timestam m_inProgressStrokes.Remove(id); } - private static void CreateBrushStroke(Stroke stroke, Guid commandGuid, int timestamp, Guid parentGuid = default, int childCount = 0) + private static void CreateBrushStroke(Stroke stroke, Guid commandGuid, int timestamp, Guid parentGuid = default, int childCount = 0) { Action preAction = () => @@ -296,7 +298,7 @@ private static void CreateBrushStroke(Stroke stroke, Guid commandGuid, int time var parentCommand = FindParentCommand(parentGuid); - var command = new BrushStrokeCommand( stroke, commandGuid, timestamp, parent: parentCommand); + var command = new BrushStrokeCommand(stroke, commandGuid, timestamp, parent: parentCommand); AddPendingCommand(preAction, commandGuid, parentGuid, command, childCount); } @@ -334,7 +336,8 @@ private static void DeleteStroke(int seed, Guid commandGuid, int timestamp, Guid } } - public static void Send_SwitchEnvironment(NetworkRunner runner, Guid environmentGuid, Guid commandGuid, int timestamp, Guid parentGuid = default, int childCount = 0, [RpcTarget] PlayerRef targetPlayer = default) { + public static void Send_SwitchEnvironment(NetworkRunner runner, Guid environmentGuid, Guid commandGuid, int timestamp, Guid parentGuid = default, int childCount = 0, [RpcTarget] PlayerRef targetPlayer = default) + { if (targetPlayer == default) { @@ -345,7 +348,7 @@ public static void Send_SwitchEnvironment(NetworkRunner runner, Guid environment RPC_SwitchEnvironment(runner, environmentGuid, commandGuid, timestamp, parentGuid, childCount, targetPlayer); } } - + private static void SwitchEnvironment(Guid environmentGuid, Guid commandGuid, int timestamp, Guid parentGuid = default, int childCount = 0) { if (CheckifCommandGuidIsInStack(commandGuid)) return; @@ -378,12 +381,12 @@ public static async Task WaitForAcknowledgment(Guid commandGuid, int timeo if (completedTask == acknowledgmentTask) { CommandAcknowledgments.Remove(commandGuid); - return await acknowledgmentTask; + return await acknowledgmentTask; } else { CommandAcknowledgments.Remove(commandGuid); - return false; + return false; } } @@ -409,7 +412,7 @@ public static void RPC_PerformCommand(NetworkRunner runner, string commandName, // 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; @@ -513,7 +516,7 @@ private static void RPC_DeleteStroke(NetworkRunner runner, int seed, Guid comman } [Rpc(InvokeLocal = false)] - private static void RPC_SwitchEnvironment(NetworkRunner runner, Guid environmentGuid, Guid commandGuid, int timestamp, Guid parentGuid = default, int childCount = 0, [RpcTarget] PlayerRef targetPlayer = default) + private static void RPC_SwitchEnvironment(NetworkRunner runner, Guid environmentGuid, Guid commandGuid, int timestamp, Guid parentGuid = default, int childCount = 0, [RpcTarget] PlayerRef targetPlayer = default) { SwitchEnvironment(environmentGuid, commandGuid, timestamp, parentGuid, childCount); } @@ -525,7 +528,7 @@ private static void RPC_SwitchEnvironment(NetworkRunner runner, Guid environment } [Rpc(InvokeLocal = false)] - public static void RPC_StartHistorySync(NetworkRunner runner, [RpcTarget] PlayerRef targetPlayer ) + public static void RPC_StartHistorySync(NetworkRunner runner, [RpcTarget] PlayerRef targetPlayer) { m_Instance.IssueGlobalCommand(GlobalCommands.DisplaySynchInfo); } diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonStructs.cs b/Assets/Scripts/Multiplayer/Photon/PhotonStructs.cs index 6fcf181f93..f5a0ea7c13 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonStructs.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonStructs.cs @@ -40,7 +40,7 @@ public PendingCommand(Guid guid, BaseCommand command, Action action, int count) ChildCommands = new List(); } } - + public struct NetworkCommandData : INetworkStruct { public Guid CommandGuid; @@ -152,13 +152,13 @@ public NetworkedStroke Init(Stroke data) m_ControlPointsCapacity = data.m_ControlPoints.Length; - for(int i = 0; i < data.m_ControlPoints.Length; i++) + 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++) + for (int i = 0; i < data.m_ControlPointsToDrop.Length; i++) { m_ControlPointsToDrop.Set(i, data.m_ControlPointsToDrop[i]); } From d517cb2120595a707d80327332906cb5c165fc3f Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Thu, 5 Dec 2024 20:40:12 +0000 Subject: [PATCH 129/174] Update PhotonManager.cs CheckExistingUsers() is no longer needed because OnPlayerJoined behaves differently in Fusion 2 compared to Fusion 1. Additionally, PlayerRef can no longer be generated directly from the playerId. Instead, a method (PlayerRef .FromEncoded()) must be used to generate them, therefore we no longer store the PlayerRef.playerId instead we store the PlayerRef .RawEncoded value. --- .../Multiplayer/Photon/PhotonManager.cs | 22 ++++--------------- 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs index 36104566ad..4be775e23a 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs @@ -96,25 +96,12 @@ public void Update() var newPlayer = m_Runner.GetPlayerObject(player); if (newPlayer != null) { - m_Manager.remotePlayerJoined?.Invoke(player.PlayerId, newPlayer.GetComponent()); + m_Manager.remotePlayerJoined?.Invoke(player.RawEncoded, newPlayer.GetComponent()); m_PlayersSpawning.Remove(player); } } } - public void CheckExistingUsers() - { - foreach (PlayerRef player in m_Runner.ActivePlayers) - { - if (player != m_Runner.LocalPlayer) - { - m_PlayersSpawning.Add(player); - } - } - - } - - #region IConnectionHandler Methods public async Task Connect() @@ -122,7 +109,7 @@ public async Task Connect() State = ConnectionState.CONNECTING; await Task.Yield(); - //return true; + var result = await m_Runner.JoinSessionLobby(SessionLobby.Shared, customAppSettings: m_PhotonAppSettings); if (result.Ok) @@ -465,8 +452,7 @@ public void OnPlayerJoined(NetworkRunner runner, PlayerRef player) 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(player.PlayerId, m_LocalPlayer); - CheckExistingUsers(); + m_Manager.localPlayerJoined?.Invoke(player.RawEncoded, m_LocalPlayer); } else { @@ -481,7 +467,7 @@ public void OnPlayerJoined(NetworkRunner runner, PlayerRef player) public void OnPlayerLeft(NetworkRunner runner, PlayerRef player) { - m_Manager.playerLeft?.Invoke(player.PlayerId); + m_Manager.playerLeft?.Invoke(player.RawEncoded); } public void OnSessionListUpdated(NetworkRunner runner, List sessionList) From 3e996ee86356fa0d63c58c8ab5695e73f0f89af7 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Fri, 6 Dec 2024 20:03:30 +0000 Subject: [PATCH 130/174] Multiplayer Stroke Serialization new class which provides a methods to Serializes a LinkedList of Strokes into a byte array and deserialize them in order to use the m_Runner.SendReliableData --- .../Scripts/Multiplayer/MultiplayerManager.cs | 51 +++- .../MultiplayerStrokeSerialization.cs | 162 +++++++++++ .../MultiplayerStrokeSerialization.cs.meta | 11 + .../Multiplayer/Photon/PhotonManager.cs | 16 +- Assets/Scripts/Save/SketchWriter.cs | 252 +++++++++++++++++- Assets/Scripts/SketchBinaryReader.cs | 7 + Assets/Scripts/SketchBinaryWriter.cs | 6 + Assets/Scripts/SketchMemoryScript.cs | 4 +- 8 files changed, 484 insertions(+), 25 deletions(-) create mode 100644 Assets/Scripts/Multiplayer/MultiplayerStrokeSerialization.cs create mode 100644 Assets/Scripts/Multiplayer/MultiplayerStrokeSerialization.cs.meta diff --git a/Assets/Scripts/Multiplayer/MultiplayerManager.cs b/Assets/Scripts/Multiplayer/MultiplayerManager.cs index 3cffd137df..0befb800f7 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerManager.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerManager.cs @@ -405,25 +405,48 @@ void OnRemotePlayerJoined(int id, ITransientData playerData) if (isUserRoomOwner) { - StartCoroutine(SaveLoadScript.m_Instance.GetLastAutosaveBytes((byte[] autosaveBytes) => - { - if (autosaveBytes != null) - { - Debug.Log($"Successfully retrieved {autosaveBytes.Length} bytes from the autosave."); - m_Manager.SendLargeDataToPlayer(id, autosaveBytes); - } - else - { - Debug.LogWarning("Failed to retrieve autosave bytes. Proceed to share command history"); - HistorySynchronizationManager.m_Instance.StartSyncronizationForUser(id); - } - })); + SendStrokesToPlayer(id); } } + async void SendStrokesToPlayer(int id) + { + LinkedList strokes = SketchMemoryScript.m_Instance.GetMemoryList; + const int chunkSize = 100; + List strokeList = strokes.ToList(); + + for (int i = 0; i < strokeList.Count; i += chunkSize) + { + var chunk = strokeList.Skip(i).Take(chunkSize).ToList(); + byte[] strokesData = await MultiplayerStrokeSerialization.SerializeAndCompressMemoryListAsync(chunk); + m_Manager.SendLargeDataToPlayer(id, strokesData); + Debug.Log($"Sent {strokesData.Length} bytes of serialized stroke data (batch {(i / chunkSize) + 1}) to player {id}."); + } + } + + void OnLargeDataReceived(byte[] largeData) { - SaveLoadScript.m_Instance.LoadFromBytes(largeData); + Debug.Log($"Successfully received {largeData.Length} bytes from the autosave."); + + DeserializeReceivedStrokes(largeData); + } + + async void DeserializeReceivedStrokes(byte[] largeData) + { + + // Decompress and deserialize strokes asynchronously + List strokes = await MultiplayerStrokeSerialization.DecompressAndDeserializeMemoryListAsync(largeData); + + Debug.Log($"Successfully deserialized {strokes.Count} strokes."); + + // Handle the strokes (e.g., add them to the scene or memory) + foreach (var stroke in strokes) + { + BrushStrokeCommand c = new BrushStrokeCommand(stroke); + SketchMemoryScript.m_Instance.PerformAndRecordNetworkCommand(c, true); + } + } void OnPlayerLeft(int id) diff --git a/Assets/Scripts/Multiplayer/MultiplayerStrokeSerialization.cs b/Assets/Scripts/Multiplayer/MultiplayerStrokeSerialization.cs new file mode 100644 index 0000000000..72a68395fe --- /dev/null +++ b/Assets/Scripts/Multiplayer/MultiplayerStrokeSerialization.cs @@ -0,0 +1,162 @@ +// 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 System.IO; +using System.IO.Compression; +using UnityEngine; +using TiltBrush; +using System.Threading.Tasks; +using System.Linq; + +namespace OpenBrush.Multiplayer +{ + public static class MultiplayerStrokeSerialization + { + public static async Task SerializeAndCompressMemoryListAsync(List memoryList) + { + byte[] serializedData = await SerializeMemoryList(memoryList); + return await Compress(serializedData); + } + + public static async Task> DecompressAndDeserializeMemoryListAsync(byte[] compressedData) + { + byte[] decompressedData = await Decompress(compressedData); + return await DeserializeMemoryList(decompressedData); + } + + // Serializes a LinkedList of Strokes into a byte array using SketchWriter. + // We did not event anything new we are using SketchWriter.WriteMemory from TiltBrush. + public static async Task SerializeMemoryList(List strokeList) + { + try + { + var strokeSnapshots = SketchWriter.EnumerateAdjustedSnapshots(strokeList).ToList(); + using (var memoryStream = new MemoryStream()) + { + SketchWriter.WriteMemory(memoryStream, strokeSnapshots, new GroupIdMapping()); + Debug.Log($"Serialization complete. Serialized data size: {memoryStream.Length} bytes."); + return memoryStream.ToArray(); + } + } + catch (Exception ex) + { + Debug.LogError($"Error during serialization: {ex.Message}"); + throw; + } + } + + // Deserializes a byte array into a List of Strokes using SketchWriter. + // We did not event anything new we are using SketchWriter.GetStrokes from TiltBrush. + public static async Task> DeserializeMemoryList(byte[] data) + { + try + { + using (var memoryStream = new MemoryStream(data)) + { + var oldGroupToNewGroup = new Dictionary(); + var strokes = SketchWriter.GetStrokes(memoryStream, allowFastPath: true); + + if (strokes != null) + { + Debug.Log($"Successfully deserialized {strokes.Count} strokes from network."); + return strokes; + } + else + { + Debug.LogError("Failed to deserialize strokes."); + return null; + } + } + } + catch (Exception ex) + { + Debug.LogError($"Error during deserialization: {ex.Message}"); + throw; + } + } + + public static Guid[] GetBrushGuidsFromManifest() + { + // List to store brush GUIDs + List brushGuids = new List(); + + // Iterate through each unique brush in the manifest + foreach (BrushDescriptor brush in App.Instance.m_Manifest.UniqueBrushes()) + { + if (brush != null) + { + // Add the brush GUID to the list + brushGuids.Add(brush.m_Guid); + Debug.Log($"Brush: {brush.name}, GUID: {brush.m_Guid}"); + } + else + { + Debug.LogWarning("Encountered a null brush descriptor."); + } + } + + return brushGuids.ToArray(); + } + + // Compresses a byte array using Brotli. + public static async Task Compress(byte[] data) + { + try + { + return await Task.Run(() => + { + using var outputStream = new MemoryStream(); + using var brotliStream = new BrotliStream(outputStream, CompressionMode.Compress, leaveOpen: true); + + brotliStream.Write(data, 0, data.Length); + brotliStream.Flush(); + + Debug.Log($"Compression complete. Compressed data size: {outputStream.Length} bytes."); + + return outputStream.ToArray(); + }); + } + catch (Exception ex) + { + Debug.LogError($"Error during compression: {ex.Message}"); + throw; + } + } + + // Decompresses a Brotli-compressed byte array. + public static async Task Decompress(byte[] compressedData) + { + try + { + return await Task.Run(() => + { + using var input = new MemoryStream(compressedData); + using var brotli = new BrotliStream(input, CompressionMode.Decompress); + using var output = new MemoryStream(); + brotli.CopyTo(output); + Debug.Log($"Decompression complete. Decompressed data size: {output.Length} bytes."); + return output.ToArray(); + }); + } + catch (Exception ex) + { + Debug.LogError($"Error during decompression: {ex.Message}"); + throw; + } + } + + } +} diff --git a/Assets/Scripts/Multiplayer/MultiplayerStrokeSerialization.cs.meta b/Assets/Scripts/Multiplayer/MultiplayerStrokeSerialization.cs.meta new file mode 100644 index 0000000000..23af851873 --- /dev/null +++ b/Assets/Scripts/Multiplayer/MultiplayerStrokeSerialization.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 28356a88a4666d446ba7b0510b0d2620 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs index 4be775e23a..f9eb503787 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs @@ -26,21 +26,18 @@ using UnityEditor; using UnityEngine.SceneManagement; + namespace OpenBrush.Multiplayer { public class PhotonManager : IDataConnectionHandler, INetworkRunnerCallbacks { private NetworkRunner m_Runner; - private MultiplayerManager m_Manager; - private List m_PlayersSpawning; - private PhotonPlayerRig m_LocalPlayer; - private FusionAppSettings m_PhotonAppSettings; - + private int sequenceNumber = 0; public event Action Disconnected; public ConnectionUserInfo UserInfo { get; set; } @@ -150,6 +147,9 @@ public async Task JoinRoom(RoomCreateData roomCreateData) }; var result = await m_Runner.StartGame(args); + m_Runner.ReliableDataSendRate = 60; + m_Runner.Config.Network.ReliableDataTransferModes = NetworkConfiguration.ReliableDataTransfers.ClientToClientWithServerProxy; + if (result.Ok) { @@ -316,8 +316,10 @@ public async Task RpcSyncHistoryPercentage(int id, int exp, int snt) public void SendLargeDataToPlayer(int playerId, byte[] largeData) { + sequenceNumber++; PlayerRef playerRef = PlayerRef.FromEncoded(playerId); - var key = ReliableKey.FromInts(42, 0, 0, 0); + int dataHash = largeData.GetHashCode(); + var key = ReliableKey.FromInts(playerId, sequenceNumber, dataHash, 0); m_Runner.SendReliableDataToPlayer(playerRef, key, largeData); } @@ -492,8 +494,6 @@ public void OnSessionListUpdated(NetworkRunner runner, List session public void OnReliableDataReceived(NetworkRunner runner, PlayerRef player, ReliableKey key, ArraySegment data) { - Debug.Log("Server received reliable data"); - byte[] receivedData = data.Array; if (receivedData == null || receivedData.Length == 0) { diff --git a/Assets/Scripts/Save/SketchWriter.cs b/Assets/Scripts/Save/SketchWriter.cs index 15fd9b30ba..adf23a4690 100644 --- a/Assets/Scripts/Save/SketchWriter.cs +++ b/Assets/Scripts/Save/SketchWriter.cs @@ -278,6 +278,91 @@ public static void WriteMemory(Stream stream, IList s } } + + /// Serializes brush GUIDs directly instead of maintaining an internal mapping or list. + public static void WriteMemory(Stream stream, IList strokeCopies, + GroupIdMapping groupIdMapping) + { + bool allowFastPath = BitConverter.IsLittleEndian; + var writer = new TiltBrush.SketchBinaryWriter(stream); + + writer.UInt32(SKETCH_SENTINEL); + writer.Int32(SKETCH_VERSION); + writer.Int32(0); // reserved for header: must be 0 + // Bump SKETCH_VERSION to >= 6 and remove this comment if non-zero data is written here + writer.UInt32(0); // additional data size + + // strokes + writer.Int32(strokeCopies.Count); + foreach (AdjustedMemoryBrushStroke copy in strokeCopies) + { + var stroke = copy.strokeData; + Guid brushGuid = stroke.m_BrushGuid; + + writer.Guid(brushGuid); + writer.Color(stroke.m_Color); + writer.Float(stroke.m_BrushSize); + + // Determine the stroke extension mask + StrokeExtension strokeExtensionMask = StrokeExtension.Flags | StrokeExtension.Seed; + if (stroke.m_BrushScale != 1) { strokeExtensionMask |= StrokeExtension.Scale; } + if (stroke.m_Group != SketchGroupTag.None) { strokeExtensionMask |= StrokeExtension.Group; } + strokeExtensionMask |= StrokeExtension.Layer; + + writer.UInt32((uint)strokeExtensionMask); + uint controlPointExtensionMask = + (uint)(ControlPointExtension.Pressure | ControlPointExtension.Timestamp); + writer.UInt32(controlPointExtensionMask); + + // Stroke extension fields, in order of appearance in the mask + writer.UInt32((uint)copy.adjustedStrokeFlags); + if ((uint)(strokeExtensionMask & StrokeExtension.Scale) != 0) + { + writer.Float(stroke.m_BrushScale); + } + if ((uint)(strokeExtensionMask & StrokeExtension.Group) != 0) + { + writer.UInt32(groupIdMapping.GetId(stroke.m_Group)); + } + if ((uint)(strokeExtensionMask & StrokeExtension.Seed) != 0) + { + writer.Int32(stroke.m_Seed); + } + if ((uint)(strokeExtensionMask & StrokeExtension.Layer) != 0) + { + writer.UInt32(copy.layerIndex); + } + + // Control points + writer.Int32(stroke.m_ControlPoints.Length); + if (allowFastPath && controlPointExtensionMask == ControlPoint.EXTENSIONS) + { + // Fast path: write ControlPoint[] (semi-)directly into the file + unsafe + { + int size = sizeof(ControlPoint) * stroke.m_ControlPoints.Length; + fixed (ControlPoint* aPoints = stroke.m_ControlPoints) + { + writer.Write((IntPtr)aPoints, size); + } + } + } + else + { + for (int j = 0; j < stroke.m_ControlPoints.Length; ++j) + { + var rControlPoint = stroke.m_ControlPoints[j]; + writer.Vec3(rControlPoint.m_Pos); + writer.Quaternion(rControlPoint.m_Orient); + // Control point extension fields, in order of appearance in the mask + writer.Float(rControlPoint.m_Pressure); + writer.UInt32(rControlPoint.m_TimestampMs); + } + } + } + } + + /// Leaves stream in indeterminate state; caller should Close() upon return. public static bool ReadMemory(Stream stream, Guid[] brushList, bool bAdditive, out bool isLegacy, out Dictionary oldGroupToNewGroup) { @@ -522,5 +607,170 @@ public static List GetStrokes( return result; } + + // Parses a binary stream into List of MemoryBrushStroke the binary need strokes with encoded guids + public static List GetStrokes( + Stream stream, bool allowFastPath, bool squashLayers = false) + { + var reader = new SketchBinaryReader(stream); + + uint sentinel = reader.UInt32(); + if (sentinel != SKETCH_SENTINEL) + { + Debug.LogFormat("Invalid .tilt: bad sentinel"); + return null; + } + + int version = reader.Int32(); + if (version < REQUIRED_SKETCH_VERSION_MIN || + version > REQUIRED_SKETCH_VERSION_MAX) + { + Debug.LogFormat("Invalid .tilt: unsupported version {0}", version); + return null; + } + + reader.Int32(); // reserved for header: must be 0 + uint moreHeader = reader.UInt32(); // additional data size + if (!reader.Skip(moreHeader)) { return null; } + + // strokes + int iNumMemories = reader.Int32(); + var result = new List(); + for (int i = 0; i < iNumMemories; ++i) + { + var stroke = new Stroke(); + + // Read the brush GUID directly from the stream + stroke.m_BrushGuid = reader.ReadGuid(); + stroke.m_Color = reader.Color(); + stroke.m_BrushSize = reader.Float(); + stroke.m_BrushScale = 1f; + stroke.m_Seed = 0; + + uint strokeExtensionMask = reader.UInt32(); + uint controlPointExtensionMask = reader.UInt32(); + + if ((strokeExtensionMask & (int)StrokeExtension.Seed) == 0) + { + // Backfill for old files saved without seeds. + // This is arbitrary but should be determinstic. + unchecked + { + int seed = i; + seed = (seed * 397) ^ stroke.m_BrushGuid.GetHashCode(); + seed = (seed * 397) ^ stroke.m_Color.GetHashCode(); + seed = (seed * 397) ^ stroke.m_BrushSize.GetHashCode(); + stroke.m_Seed = seed; + } + } + + // Process stroke extension fields... + for (var fields = strokeExtensionMask; fields != 0; fields &= (fields - 1)) + { + uint bit = (fields & ~(fields - 1)); + switch ((StrokeExtension)bit) + { + case StrokeExtension.None: + Debug.Assert(false); + break; + case StrokeExtension.Flags: + stroke.m_Flags = (StrokeFlags)reader.UInt32(); + break; + case StrokeExtension.Scale: + stroke.m_BrushScale = reader.Float(); + break; + case StrokeExtension.Group: + { + UInt32 groupId = reader.UInt32(); + stroke.Group = App.GroupManager.GetGroupFromId(groupId); + break; + } + case StrokeExtension.Layer: + UInt32 layerIndex = reader.UInt32(); + if (squashLayers) + { + layerIndex = 0; + } + var canvas = App.Scene.GetOrCreateLayer((int)layerIndex); + stroke.m_IntendedCanvas = canvas; + break; + case StrokeExtension.Seed: + stroke.m_Seed = reader.Int32(); + break; + default: + { + // Skip unknown extension. + if ((bit & (uint)StrokeExtension.MaskSingleWord) != 0) + { + reader.UInt32(); + } + else + { + uint size = reader.UInt32(); + if (!reader.Skip(size)) { return null; } + } + break; + } + } + } + + // Process control points... + int nControlPoints = reader.Int32(); + stroke.m_ControlPoints = new PointerManager.ControlPoint[nControlPoints]; + stroke.m_ControlPointsToDrop = new bool[nControlPoints]; + + if (allowFastPath && controlPointExtensionMask == PointerManager.ControlPoint.EXTENSIONS) + { + unsafe + { + int size = sizeof(PointerManager.ControlPoint) * stroke.m_ControlPoints.Length; + fixed (PointerManager.ControlPoint* aPoints = stroke.m_ControlPoints) + { + if (!reader.ReadInto((IntPtr)aPoints, size)) + { + return null; + } + } + } + } + else + { + for (int j = 0; j < nControlPoints; ++j) + { + PointerManager.ControlPoint rControlPoint; + + rControlPoint.m_Pos = reader.Vec3(); + rControlPoint.m_Orient = reader.Quaternion(); + + rControlPoint.m_Pressure = 1.0f; + rControlPoint.m_TimestampMs = 0; + + for (var fields = controlPointExtensionMask; fields != 0; fields &= (fields - 1)) + { + switch ((ControlPointExtension)(fields & ~(fields - 1))) + { + case ControlPointExtension.None: + Debug.Assert(false); + break; + case ControlPointExtension.Pressure: + rControlPoint.m_Pressure = reader.Float(); + break; + case ControlPointExtension.Timestamp: + rControlPoint.m_TimestampMs = reader.UInt32(); + break; + default: + reader.Int32(); + break; + } + } + stroke.m_ControlPoints[j] = rControlPoint; + } + } + + result.Add(stroke); + } + return result; + } + } -} // namespace TiltBrush +}// namespace TiltBrush \ No newline at end of file diff --git a/Assets/Scripts/SketchBinaryReader.cs b/Assets/Scripts/SketchBinaryReader.cs index 2ca35cbf1e..ac76cd9dcd 100644 --- a/Assets/Scripts/SketchBinaryReader.cs +++ b/Assets/Scripts/SketchBinaryReader.cs @@ -100,6 +100,13 @@ public Quaternion Quaternion() return new Quaternion(Float(), Float(), Float(), Float()); } + + public Guid ReadGuid() + { + int bytesRead = m_stream.Read(m_buf16, 0, 16); + return new Guid(m_buf16); + } + private void LazyCreateBigBuffer() { if (m_bufBig == null) diff --git a/Assets/Scripts/SketchBinaryWriter.cs b/Assets/Scripts/SketchBinaryWriter.cs index 4c86ef31db..eb527093b7 100644 --- a/Assets/Scripts/SketchBinaryWriter.cs +++ b/Assets/Scripts/SketchBinaryWriter.cs @@ -53,6 +53,12 @@ public void Dispose() BaseStream = null; } + public void Guid(Guid guid) + { + byte[] guidBytes = guid.ToByteArray(); + m_stream.Write(guidBytes, 0, guidBytes.Length); + } + public void Color(Color color) { Float(color.r); diff --git a/Assets/Scripts/SketchMemoryScript.cs b/Assets/Scripts/SketchMemoryScript.cs index d83016f933..c6464b00a6 100644 --- a/Assets/Scripts/SketchMemoryScript.cs +++ b/Assets/Scripts/SketchMemoryScript.cs @@ -428,11 +428,11 @@ public void PerformAndRecordCommand(BaseCommand command, bool discardIfNotMerged /// Executes and records a network-synchronized command. /// Note: This method does not include merge logic or parent-child relationship checks, /// as these are already handled by the PhotonRPC system. - public void PerformAndRecordNetworkCommand(BaseCommand command) + public void PerformAndRecordNetworkCommand(BaseCommand command, bool discard = false) { BaseCommand delta = command; delta.Redo(); - m_NetworkStack.Push(command); + if (!discard) m_NetworkStack.Push(command); NetworkOperationStackChanged?.Invoke(); } From 0b4bdc49ce91e1df48d3aa01fe31725347d25d5b Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Tue, 10 Dec 2024 11:49:10 +0000 Subject: [PATCH 131/174] Multiplayer Scene Sync Renaming History Synchronization manager to Multiplayer Scene Sync Migrating the code for sending large bytes from the multiplayer manager to the Multiplayer Scene Sync implementing the choice to sync the scene with commands (old method) or by sharing strokes (new reliable method) --- .../Scripts/Multiplayer/MultiplayerManager.cs | 42 +-------- ...tionManager.cs => MultiplayerSceneSync.cs} | 93 ++++++++++++++++--- ...r.cs.meta => MultiplayerSceneSync.cs.meta} | 0 .../Multiplayer/Photon/PhotonManager.cs | 9 +- .../Scripts/Multiplayer/Photon/PhotonRPC.cs | 4 +- Assets/Scripts/SketchControlsScript.cs | 6 +- 6 files changed, 96 insertions(+), 58 deletions(-) rename Assets/Scripts/Multiplayer/{HistorySynchronizationManager.cs => MultiplayerSceneSync.cs} (70%) rename Assets/Scripts/Multiplayer/{HistorySynchronizationManager.cs.meta => MultiplayerSceneSync.cs.meta} (100%) diff --git a/Assets/Scripts/Multiplayer/MultiplayerManager.cs b/Assets/Scripts/Multiplayer/MultiplayerManager.cs index 0befb800f7..b2c187de4e 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerManager.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerManager.cs @@ -18,6 +18,7 @@ using System.Threading.Tasks; using UnityEngine; + #if OCULUS_SUPPORTED using OVRPlatform = Oculus.Platform; #endif @@ -49,7 +50,6 @@ public class MultiplayerManager : MonoBehaviour public Action> remotePlayerJoined; public Action playerLeft; public Action> roomDataRefreshed; - public Action onLargeDataReceived; public event Action StateUpdated; public event Action RoomOwnershipUpdated; @@ -156,7 +156,6 @@ void Start() remotePlayerJoined += OnRemotePlayerJoined; playerLeft += OnPlayerLeft; StateUpdated += UpdateSketchMemoryScriptTimeOffset; - onLargeDataReceived += OnLargeDataReceived; SketchMemoryScript.m_Instance.CommandPerformed += OnCommandPerformed; SketchMemoryScript.m_Instance.CommandUndo += OnCommandUndo; @@ -170,7 +169,6 @@ void OnDestroy() remotePlayerJoined -= OnRemotePlayerJoined; playerLeft -= OnPlayerLeft; StateUpdated -= UpdateSketchMemoryScriptTimeOffset; - onLargeDataReceived -= OnLargeDataReceived; SketchMemoryScript.m_Instance.CommandPerformed -= OnCommandPerformed; SketchMemoryScript.m_Instance.CommandUndo -= OnCommandUndo; @@ -405,49 +403,17 @@ void OnRemotePlayerJoined(int id, ITransientData playerData) if (isUserRoomOwner) { - SendStrokesToPlayer(id); + MultiplayerSceneSync.m_Instance.StartSyncronizationForUser(id); } } - async void SendStrokesToPlayer(int id) + public void SendLargeDataToPlayer(int playerId, byte[] Data) { - LinkedList strokes = SketchMemoryScript.m_Instance.GetMemoryList; - const int chunkSize = 100; - List strokeList = strokes.ToList(); - - for (int i = 0; i < strokeList.Count; i += chunkSize) - { - var chunk = strokeList.Skip(i).Take(chunkSize).ToList(); - byte[] strokesData = await MultiplayerStrokeSerialization.SerializeAndCompressMemoryListAsync(chunk); - m_Manager.SendLargeDataToPlayer(id, strokesData); - Debug.Log($"Sent {strokesData.Length} bytes of serialized stroke data (batch {(i / chunkSize) + 1}) to player {id}."); - } + m_Manager.SendLargeDataToPlayer(playerId, Data); } - void OnLargeDataReceived(byte[] largeData) - { - Debug.Log($"Successfully received {largeData.Length} bytes from the autosave."); - - DeserializeReceivedStrokes(largeData); - } - async void DeserializeReceivedStrokes(byte[] largeData) - { - - // Decompress and deserialize strokes asynchronously - List strokes = await MultiplayerStrokeSerialization.DecompressAndDeserializeMemoryListAsync(largeData); - - Debug.Log($"Successfully deserialized {strokes.Count} strokes."); - - // Handle the strokes (e.g., add them to the scene or memory) - foreach (var stroke in strokes) - { - BrushStrokeCommand c = new BrushStrokeCommand(stroke); - SketchMemoryScript.m_Instance.PerformAndRecordNetworkCommand(c, true); - } - - } void OnPlayerLeft(int id) { diff --git a/Assets/Scripts/Multiplayer/HistorySynchronizationManager.cs b/Assets/Scripts/Multiplayer/MultiplayerSceneSync.cs similarity index 70% rename from Assets/Scripts/Multiplayer/HistorySynchronizationManager.cs rename to Assets/Scripts/Multiplayer/MultiplayerSceneSync.cs index c11bed8ef9..a0f01c14e2 100644 --- a/Assets/Scripts/Multiplayer/HistorySynchronizationManager.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerSceneSync.cs @@ -15,6 +15,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Diagnostics.Eventing.Reader; using System.Linq; using TiltBrush; using TMPro; @@ -22,11 +23,13 @@ namespace OpenBrush.Multiplayer { - public class HistorySynchronizationManager : MonoBehaviour + public class MultiplayerSceneSync : MonoBehaviour { - public static HistorySynchronizationManager m_Instance; - public int batchSize = 60; - public float delayBetweenBatches = 0.05f; + public static MultiplayerSceneSync m_Instance; + public Action onLargeDataReceived; + [HideInInspector] public int batchSize = 10; + [HideInInspector] public float delayBetweenBatches = 0.05f; + public SyncType m_SyncType = SyncType.Strokes; [HideInInspector] public int numberOfCommandsExpected = 0; [HideInInspector] public int numberOfCommandsSent = 0; @@ -34,22 +37,85 @@ public class HistorySynchronizationManager : MonoBehaviour private bool _isSendingCommandHistory = false; private InfoCardAnimation infoCard; - void Awake() { m_Instance = this; } + void Start() + { + onLargeDataReceived += OnLargeDataReceived; + } - public void StartSyncronizationForUser(int id) + void OnDestroy() { + onLargeDataReceived -= OnLargeDataReceived; + } + public void StartSyncronizationForUser(int id) + { StartSynchHistory(id); - SendCurrentTargetEnvironmentCommand(); - StartCoroutine(SendStrokesAndCommandHistory(id)); + SendCurrentTargetEnvironmentCommand(); // TODO serialize the environmentand send it via large reliable data + + switch (m_SyncType) + { + case SyncType.Strokes: + SendStrokesToPlayer(id); + break; + case SyncType.Commands: + StartCoroutine(SendCommandHistory(id)); + break; + } + + } + + #region Syncronization Logic Strokes + async void SendStrokesToPlayer(int id) + { + LinkedList strokes = SketchMemoryScript.m_Instance.GetMemoryList; + const int chunkSize = 5; + List strokeList = strokes.ToList(); + + int counter = 0; + for (int i = 0; i < strokeList.Count; i += chunkSize) + { + var chunk = strokeList.Skip(i).Take(chunkSize).ToList(); + byte[] strokesData = await MultiplayerStrokeSerialization.SerializeAndCompressMemoryListAsync(chunk); + MultiplayerManager.m_Instance.SendLargeDataToPlayer(id, strokesData); + counter += chunk.Count; + //Debug.Log($"Sent {strokesData.Length} bytes of serialized stroke data (batch {(i / chunkSize) + 1}) to player {id}."); + } + SynchHistoryComplete(id); } - #region Syncronization Logic + async void DeserializeReceivedStrokes(byte[] largeData) + { + + // Decompress and deserialize strokes asynchronously + List strokes = await MultiplayerStrokeSerialization.DecompressAndDeserializeMemoryListAsync(largeData); + + Debug.Log($"Successfully deserialized {strokes.Count} strokes."); + + // Handle the strokes (e.g., add them to the scene or memory) + foreach (var stroke in strokes) + { + BrushStrokeCommand c = new BrushStrokeCommand(stroke); + SketchMemoryScript.m_Instance.MemoryListAdd(stroke); + SketchMemoryScript.m_Instance.PerformAndRecordNetworkCommand(c, true); + } + + } + + void OnLargeDataReceived(byte[] largeData) + { + Debug.Log($"[Multiplayer Scene Sync]Successfully received {largeData.Length} bytes from the autosave."); + + DeserializeReceivedStrokes(largeData); + } + + #endregion + + #region Syncronization Logic Commands public void SendCurrentTargetEnvironmentCommand() { TiltBrush.Environment targetEnvironment = SceneSettings.m_Instance.GetDesiredPreset(); @@ -61,7 +127,7 @@ public void SendCurrentTargetEnvironmentCommand() } } - public IEnumerator SendStrokesAndCommandHistory(int id) + public IEnumerator SendCommandHistory(int id) { if (_isWaiting) yield break; @@ -155,7 +221,6 @@ private int EstimateMessagesForCommand(BaseCommand command) #region Remote infoCard commands private void StartSynchHistory(int id) { - MultiplayerManager.m_Instance.StartSynchHistory(id); } @@ -224,4 +289,10 @@ public void HideSynchInfo() #endregion } + + public enum SyncType + { + Strokes, + Commands + } } \ No newline at end of file diff --git a/Assets/Scripts/Multiplayer/HistorySynchronizationManager.cs.meta b/Assets/Scripts/Multiplayer/MultiplayerSceneSync.cs.meta similarity index 100% rename from Assets/Scripts/Multiplayer/HistorySynchronizationManager.cs.meta rename to Assets/Scripts/Multiplayer/MultiplayerSceneSync.cs.meta diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs index f9eb503787..d94a08e7c2 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs @@ -147,8 +147,8 @@ public async Task JoinRoom(RoomCreateData roomCreateData) }; var result = await m_Runner.StartGame(args); - m_Runner.ReliableDataSendRate = 60; - m_Runner.Config.Network.ReliableDataTransferModes = NetworkConfiguration.ReliableDataTransfers.ClientToClientWithServerProxy; + //m_Runner.ReliableDataSendRate = 60; + //m_Runner.Config.Network.ReliableDataTransferModes = NetworkConfiguration.ReliableDataTransfers.ClientToClientWithServerProxy; if (result.Ok) @@ -493,6 +493,7 @@ public void OnSessionListUpdated(NetworkRunner runner, List session public void OnReliableDataReceived(NetworkRunner runner, PlayerRef player, ReliableKey key, ArraySegment data) { + Debug.Log("Server received complete reliable data"); byte[] receivedData = data.Array; if (receivedData == null || receivedData.Length == 0) @@ -501,13 +502,13 @@ public void OnReliableDataReceived(NetworkRunner runner, PlayerRef player, Relia return; } - m_Manager.onLargeDataReceived?.Invoke(receivedData); + MultiplayerSceneSync.m_Instance.onLargeDataReceived?.Invoke(receivedData); } public void OnReliableDataProgress(NetworkRunner runner, PlayerRef player, ReliableKey key, float progress) { - Debug.Log("Server received reliable data"); + Debug.Log("Server received Partial reliable data"); } #endregion diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs b/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs index a071b82941..c4204b02a8 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs @@ -536,8 +536,8 @@ public static void RPC_StartHistorySync(NetworkRunner runner, [RpcTarget] Player [Rpc(InvokeLocal = false)] public static void RPC_HistoryPercentageUpdate(NetworkRunner runner, [RpcTarget] PlayerRef targetPlayer, int expected, int sent) { - HistorySynchronizationManager.m_Instance.numberOfCommandsExpected = expected; - HistorySynchronizationManager.m_Instance.numberOfCommandsSent = sent; + MultiplayerSceneSync.m_Instance.numberOfCommandsExpected = expected; + MultiplayerSceneSync.m_Instance.numberOfCommandsSent = sent; m_Instance.IssueGlobalCommand(GlobalCommands.SynchInfoPercentageUpdate); } diff --git a/Assets/Scripts/SketchControlsScript.cs b/Assets/Scripts/SketchControlsScript.cs index 64969ee25e..db75f61eab 100644 --- a/Assets/Scripts/SketchControlsScript.cs +++ b/Assets/Scripts/SketchControlsScript.cs @@ -4861,13 +4861,13 @@ public void IssueGlobalCommand(GlobalCommands rEnum, int iParam1 = -1, SketchSurfacePanel.m_Instance.EatToolsInput(); break; case GlobalCommands.DisplaySynchInfo: - HistorySynchronizationManager.m_Instance.DisplaySynchInfo(); + MultiplayerSceneSync.m_Instance.DisplaySynchInfo(); break; case GlobalCommands.SynchInfoPercentageUpdate: - HistorySynchronizationManager.m_Instance.SynchInfoPercentageUpdate(); + MultiplayerSceneSync.m_Instance.SynchInfoPercentageUpdate(); break; case GlobalCommands.HideSynchInfo: - HistorySynchronizationManager.m_Instance.HideSynchInfo(); + MultiplayerSceneSync.m_Instance.HideSynchInfo(); break; case GlobalCommands.RepaintOptions: break; // Intentionally blank. case GlobalCommands.Null: break; // Intentionally blank. From 330e69eb292411dd906daa0b102921a7b524845c Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Tue, 10 Dec 2024 16:19:39 +0000 Subject: [PATCH 132/174] Implement display of progress for stroke synchronization 1 - implement strokes guid 2 - Implement display of progress for stroke synchronization --- .../Multiplayer/MultiplayerInterfaces.cs | 1 + .../Scripts/Multiplayer/MultiplayerManager.cs | 10 ++ .../Multiplayer/MultiplayerSceneSync.cs | 105 +++++++++++++----- .../Multiplayer/Photon/PhotonManager.cs | 7 ++ .../Scripts/Multiplayer/Photon/PhotonRPC.cs | 35 ++++-- Assets/Scripts/SketchMemoryScript.cs | 5 + Assets/Scripts/Stroke.cs | 4 + Assets/Scripts/StrokeData.cs | 1 + 8 files changed, 132 insertions(+), 36 deletions(-) diff --git a/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs b/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs index 4912af9fbd..bc3a0687d7 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs @@ -40,6 +40,7 @@ public interface IDataConnectionHandler : IConnectionHandler Task PerformCommand(BaseCommand command); Task SendCommandToPlayer(BaseCommand command, int playerId); Task CheckCommandReception(BaseCommand command, int playerId); + Task CheckStrokeReception(Stroke stroke, int playerId); Task UndoCommand(BaseCommand command); Task RedoCommand(BaseCommand command); Task RpcSyncToSharedAnchor(string uuid); diff --git a/Assets/Scripts/Multiplayer/MultiplayerManager.cs b/Assets/Scripts/Multiplayer/MultiplayerManager.cs index b2c187de4e..b08e58e241 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerManager.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerManager.cs @@ -486,6 +486,16 @@ public async Task CheckCommandReception(BaseCommand command, int id) return false; } + public async Task CheckStrokeReception(Stroke stroke, int id) + { + if (State == ConnectionState.IN_ROOM) + { + return await m_Manager.CheckStrokeReception(stroke, id); + } + + return false; + } + public void OnCommandUndo(BaseCommand command) { if (State == ConnectionState.IN_ROOM) diff --git a/Assets/Scripts/Multiplayer/MultiplayerSceneSync.cs b/Assets/Scripts/Multiplayer/MultiplayerSceneSync.cs index a0f01c14e2..81aca55969 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerSceneSync.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerSceneSync.cs @@ -15,12 +15,13 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Diagnostics.Eventing.Reader; using System.Linq; +using System.Threading.Tasks; using TiltBrush; using TMPro; using UnityEngine; + namespace OpenBrush.Multiplayer { public class MultiplayerSceneSync : MonoBehaviour @@ -35,7 +36,7 @@ public class MultiplayerSceneSync : MonoBehaviour private bool _isWaiting = false; private bool _isSendingCommandHistory = false; - private InfoCardAnimation infoCard; + void Awake() { @@ -73,6 +74,7 @@ public void StartSyncronizationForUser(int id) async void SendStrokesToPlayer(int id) { LinkedList strokes = SketchMemoryScript.m_Instance.GetMemoryList; + StartSyncProgressDisplayForSrokes(id, strokes); const int chunkSize = 5; List strokeList = strokes.ToList(); @@ -85,7 +87,6 @@ async void SendStrokesToPlayer(int id) counter += chunk.Count; //Debug.Log($"Sent {strokesData.Length} bytes of serialized stroke data (batch {(i / chunkSize) + 1}) to player {id}."); } - SynchHistoryComplete(id); } async void DeserializeReceivedStrokes(byte[] largeData) @@ -108,7 +109,7 @@ async void DeserializeReceivedStrokes(byte[] largeData) void OnLargeDataReceived(byte[] largeData) { - Debug.Log($"[Multiplayer Scene Sync]Successfully received {largeData.Length} bytes from the autosave."); + //Debug.Log($"[Multiplayer Scene Sync]Successfully received {largeData.Length} bytes from the autosave."); DeserializeReceivedStrokes(largeData); } @@ -149,7 +150,9 @@ public IEnumerator SendCommandHistory(int id) int firstCommandTimestamp = commands.Any() ? commands.First().NetworkTimestamp ?? int.MaxValue : int.MaxValue; - CreateBrushStrokeCommands(strokesWithoutCommand, firstCommandTimestamp); + CreateBrushStrokeCommands(strokesWithoutCommand, firstCommandTimestamp); // this add the strokes without commands to the IEnumerable commands + + StartSyncProgressDisplayForCommands(id, commands.ToList()); int packetCounter = 0; int counter = 0; @@ -166,13 +169,10 @@ public IEnumerator SendCommandHistory(int id) MultiplayerManager.m_Instance.OnCommandPerformed(command); packetCounter += estimatedMessages; counter++; - SynchHistoryPercentage(id, commands.Count(), counter); } _isSendingCommandHistory = false; - SynchHistoryComplete(id); - } private void CreateBrushStrokeCommands(List strokes, int LastTimestamp) @@ -219,6 +219,43 @@ private int EstimateMessagesForCommand(BaseCommand command) #endregion #region Remote infoCard commands + + public async void StartSyncProgressDisplayForSrokes(int TargetPlayerId, LinkedList strokes) + { + StartSynchHistory(TargetPlayerId); + + int sentStrokes = 0; + foreach (var stroke in strokes) + { + while (await MultiplayerManager.m_Instance.CheckStrokeReception(stroke, TargetPlayerId)) + { + await Task.Delay(200); + } + sentStrokes++; + SynchHistoryPercentage(TargetPlayerId, strokes.Count, sentStrokes); + } + + SynchHistoryComplete(TargetPlayerId); + } + + public async void StartSyncProgressDisplayForCommands(int TargetPlayerId, List commands) + { + StartSynchHistory(TargetPlayerId); + + int sentStrokes = 0; + foreach (var command in commands) + { + while (await MultiplayerManager.m_Instance.CheckCommandReception(command, TargetPlayerId)) + { + await Task.Delay(200); + } + sentStrokes++; + SynchHistoryPercentage(TargetPlayerId, commands.Count, sentStrokes); + } + + SynchHistoryComplete(TargetPlayerId); + } + private void StartSynchHistory(int id) { MultiplayerManager.m_Instance.StartSynchHistory(id); @@ -238,54 +275,66 @@ private void SynchHistoryComplete(int id) #endregion #region Local infoCard commands + + private InfoCardAnimation infoCard; + private readonly object infoCardLock = new object(); + public void DisplaySynchInfo() { - OutputWindowScript.m_Instance.CreateInfoCardAtController( - InputManager.ControllerName.Brush, - "Synch Started!", - fPopScalar: 1.0f - ); - RetrieveInfoCard(); + lock (infoCardLock) + { + if (infoCard == null) + { + OutputWindowScript.m_Instance.CreateInfoCardAtController( + InputManager.ControllerName.Brush, + "Synch Started!", + fPopScalar: 1.0f + ); + infoCard = RetrieveInfoCard(); + } + } } public InfoCardAnimation RetrieveInfoCard() { - InfoCardAnimation[] allInfoCards = UnityEngine.Object.FindObjectsOfType(); - + InfoCardAnimation[] allInfoCards = FindObjectsOfType(); foreach (var card in allInfoCards) { TextMeshPro textComponent = card.GetComponentInChildren(); if (textComponent != null && textComponent.text.Contains("Synch")) { - infoCard = card; return card; } } - return null; } public void SynchInfoPercentageUpdate() { - int percentage = (int)((float)SketchMemoryScript.AllStrokesCount() / numberOfCommandsExpected * 100); - string text = $"Synch {percentage}%"; + lock (infoCardLock) + { + int percentage = (int)((float)SketchMemoryScript.AllStrokesCount() / numberOfCommandsExpected * 100); + string text = $"Synch {percentage}%"; - if (infoCard == null) infoCard = RetrieveInfoCard(); - if (infoCard == null) DisplaySynchInfo(); + if (infoCard == null) DisplaySynchInfo(); - infoCard.GetComponentInChildren().text = text; - infoCard.UpdateHoldingDuration(5f); + infoCard.GetComponentInChildren().text = text; + infoCard.UpdateHoldingDuration(5f); + } } public void HideSynchInfo() { - if (infoCard == null) infoCard = RetrieveInfoCard(); - if (infoCard == null) DisplaySynchInfo(); + lock (infoCardLock) + { + if (infoCard == null) return; - infoCard.GetComponentInChildren().text = "Synch Ended!"; - infoCard.UpdateHoldingDuration(3.0f); + infoCard.GetComponentInChildren().text = "Synch Ended!"; + infoCard.UpdateHoldingDuration(3.0f); + } } + #endregion } diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs index d94a08e7c2..3a6382a235 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs @@ -269,6 +269,13 @@ public async Task CheckCommandReception(BaseCommand command, int playerId) return await PhotonRPC.WaitForAcknowledgment(command.Guid); } + public async Task CheckStrokeReception(Stroke stroke, int playerId) + { + PlayerRef targetPlayer = PlayerRef.FromEncoded(playerId); + PhotonRPC.RPC_CheckStroke(m_Runner, stroke.m_Guid, m_Runner.LocalPlayer, targetPlayer); + return await PhotonRPC.WaitForAcknowledgment(stroke.m_Guid); + } + public async Task UndoCommand(BaseCommand command) { PhotonRPC.RPC_Undo(m_Runner, command.GetType().ToString()); diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs b/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs index c4204b02a8..50eb6be15d 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs @@ -30,12 +30,13 @@ public class PhotonRPC : SimulationBehaviour { private static Dictionary m_inProgressStrokes; private static List m_pendingCommands; - private static Dictionary> CommandAcknowledgments = new(); + private static Dictionary> m_acknowledgments; public void Awake() { m_inProgressStrokes = new(); m_pendingCommands = new(); + m_acknowledgments = new(); } public void Update() @@ -139,6 +140,17 @@ private static bool CheckifCommandGuidIsInStack(Guid commandGuid) return false; } + private static bool CheckifStrokeGuidIsInMemory(Guid strokeGuid) + { + + if (SketchMemoryScript.m_Instance.IsStrokeInMemory(strokeGuid)) + { + //Debug.Log($"Stroke with Guid {strokeGuid} already in memory."); + return true; + } + return false; + } + private static BaseCommand FindParentCommand(Guid parentGuid) { PendingCommand pendingParent = m_pendingCommands.FirstOrDefault(x => x.Guid == parentGuid); @@ -372,7 +384,7 @@ private static void SwitchEnvironment(Guid environmentGuid, Guid commandGuid, in public static async Task WaitForAcknowledgment(Guid commandGuid, int timeoutMilliseconds = 1000) { var tcs = new TaskCompletionSource(); - CommandAcknowledgments[commandGuid] = tcs; + m_acknowledgments[commandGuid] = tcs; var timeoutTask = Task.Delay(timeoutMilliseconds); var acknowledgmentTask = tcs.Task; @@ -380,12 +392,12 @@ public static async Task WaitForAcknowledgment(Guid commandGuid, int timeo if (completedTask == acknowledgmentTask) { - CommandAcknowledgments.Remove(commandGuid); + m_acknowledgments.Remove(commandGuid); return await acknowledgmentTask; } else { - CommandAcknowledgments.Remove(commandGuid); + m_acknowledgments.Remove(commandGuid); return false; } } @@ -551,16 +563,23 @@ public static void RPC_HistorySyncCompleted(NetworkRunner runner, [RpcTarget] Pl public static void RPC_CheckCommand(NetworkRunner runner, Guid commandGuid, PlayerRef initiatorPlayer, [RpcTarget] PlayerRef targetPlayer) { bool isCommandInStack = CheckifCommandGuidIsInStack(commandGuid); - RPC_ConfirmCommand(runner, commandGuid, isCommandInStack, initiatorPlayer); + RPC_Confirm(runner, commandGuid, isCommandInStack, initiatorPlayer); + } + + [Rpc(InvokeLocal = false)] + public static void RPC_CheckStroke(NetworkRunner runner, Guid strokeGuid, PlayerRef initiatorPlayer, [RpcTarget] PlayerRef targetPlayer) + { + bool isCommandInStack = CheckifStrokeGuidIsInMemory(strokeGuid); + RPC_Confirm(runner, strokeGuid, isCommandInStack, initiatorPlayer); } [Rpc(InvokeLocal = false)] - public static void RPC_ConfirmCommand(NetworkRunner runner, Guid commandGuid, bool isCommandInStack, [RpcTarget] PlayerRef targetPlayer) + public static void RPC_Confirm(NetworkRunner runner, Guid commandGuid, bool isCommandInStack, [RpcTarget] PlayerRef targetPlayer) { - if (CommandAcknowledgments.TryGetValue(commandGuid, out var tcs)) + if (m_acknowledgments.TryGetValue(commandGuid, out var tcs)) { tcs.SetResult(isCommandInStack); - CommandAcknowledgments.Remove(commandGuid); + m_acknowledgments.Remove(commandGuid); } } diff --git a/Assets/Scripts/SketchMemoryScript.cs b/Assets/Scripts/SketchMemoryScript.cs index c6464b00a6..7b7420ec5e 100644 --- a/Assets/Scripts/SketchMemoryScript.cs +++ b/Assets/Scripts/SketchMemoryScript.cs @@ -1425,6 +1425,11 @@ public static List GetStrokesBetween(int start, int end) return result; } + public bool IsStrokeInMemory(Guid strokeGuid) + { + return m_MemoryList.Any(stroke => stroke.m_Guid == strokeGuid); + } + public bool IsCommandInStack(Guid commandGuid) { return IsCommandInOperationStack(commandGuid) || diff --git a/Assets/Scripts/Stroke.cs b/Assets/Scripts/Stroke.cs index 209e5c1886..0ced2ffa0e 100644 --- a/Assets/Scripts/Stroke.cs +++ b/Assets/Scripts/Stroke.cs @@ -163,6 +163,7 @@ public Stroke() { m_NodeByTime = new LinkedListNode(this); m_PlaybackNode = new LinkedListNode(this); + m_Guid = Guid.NewGuid(); } /// Clones the passed stroke into a new NotCreated stroke. @@ -185,6 +186,9 @@ public Stroke(Stroke existing) : base(existing) // And we can't use field initializers for the linked list creation. m_NodeByTime = new LinkedListNode(this); m_PlaybackNode = new LinkedListNode(this); + + if (existing.m_Guid != null) + m_Guid = Guid.NewGuid(); } /// Makes a copy of stroke, if one has not already been made. diff --git a/Assets/Scripts/StrokeData.cs b/Assets/Scripts/StrokeData.cs index f5dc088eba..896d15fdf0 100644 --- a/Assets/Scripts/StrokeData.cs +++ b/Assets/Scripts/StrokeData.cs @@ -34,6 +34,7 @@ public class StrokeData // Not currently serialized. public int m_Seed; public SketchGroupTag m_Group = SketchGroupTag.None; + public Guid m_Guid; // Reference the BrushStrokeCommand that created this stroke with a WeakReference. // This allows the garbage collector to collect the BrushStrokeCommand if it's no From 65d3a8ac60cfb6b5c658fbd7533838447d1a24da Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Wed, 11 Dec 2024 14:42:33 +0000 Subject: [PATCH 133/174] Change Label on menu alpha->beta Commented some debug messages --- Assets/Prefabs/Panels/MultiplayerPanel.prefab | 110 ++++++++++++++++++ .../Multiplayer/Photon/PhotonManager.cs | 4 +- 2 files changed, 112 insertions(+), 2 deletions(-) diff --git a/Assets/Prefabs/Panels/MultiplayerPanel.prefab b/Assets/Prefabs/Panels/MultiplayerPanel.prefab index bf248b3074..de8519fd18 100644 --- a/Assets/Prefabs/Panels/MultiplayerPanel.prefab +++ b/Assets/Prefabs/Panels/MultiplayerPanel.prefab @@ -309,6 +309,7 @@ Transform: - {fileID: 420492} - {fileID: 480900} - {fileID: 6533003833265780531} + - {fileID: 3728028955869875737} - {fileID: 4696385544466816} - {fileID: 4410849844686322} m_Father: {fileID: 415082} @@ -5410,6 +5411,110 @@ Transform: m_Children: [] m_Father: {fileID: 415082} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1001 &1116501068073845947 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + serializedVersion: 3 + m_TransformParent: {fileID: 499980} + m_Modifications: + - target: {fileID: 2206467382688623040, guid: 2538d4a3972b37c4c9e8649481dd9a2b, + type: 3} + propertyPath: m_Name + value: Beta Tag + objectReference: {fileID: 0} + - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, + type: 3} + propertyPath: m_RootOrder + value: 2 + objectReference: {fileID: 0} + - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, + type: 3} + propertyPath: m_LocalScale.x + value: 2 + objectReference: {fileID: 0} + - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, + type: 3} + propertyPath: m_LocalScale.y + value: 2 + objectReference: {fileID: 0} + - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, + type: 3} + propertyPath: m_LocalScale.z + value: 2 + objectReference: {fileID: 0} + - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, + type: 3} + propertyPath: m_LocalPosition.x + value: 0.823 + objectReference: {fileID: 0} + - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, + type: 3} + propertyPath: m_LocalPosition.y + value: 1.249 + objectReference: {fileID: 0} + - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, + type: 3} + propertyPath: m_LocalPosition.z + value: -0.02 + objectReference: {fileID: 0} + - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, + type: 3} + propertyPath: m_LocalRotation.w + value: 0.92387956 + objectReference: {fileID: 0} + - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, + type: 3} + propertyPath: m_LocalRotation.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, + type: 3} + propertyPath: m_LocalRotation.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, + type: 3} + propertyPath: m_LocalRotation.z + value: -0.38268343 + objectReference: {fileID: 0} + - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, + type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, + type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, + type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: -45 + objectReference: {fileID: 0} + - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, + type: 3} + propertyPath: m_ConstrainProportionsScale + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 6612620938781836974, guid: 2538d4a3972b37c4c9e8649481dd9a2b, + type: 3} + propertyPath: managedReferences[3215294302546034781].m_Localized.m_TableEntryReference.m_KeyId + value: 176097607781543936 + objectReference: {fileID: 0} + m_RemovedComponents: [] + m_RemovedGameObjects: [] + m_AddedGameObjects: [] + m_AddedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: 2538d4a3972b37c4c9e8649481dd9a2b, type: 3} +--- !u!4 &3728028955869875737 stripped +Transform: + m_CorrespondingSourceObject: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, + type: 3} + m_PrefabInstance: {fileID: 1116501068073845947} + m_PrefabAsset: {fileID: 0} --- !u!1001 &7380239823636675985 PrefabInstance: m_ObjectHideFlags: 0 @@ -5423,6 +5528,11 @@ PrefabInstance: propertyPath: m_Name value: AlphaTag objectReference: {fileID: 0} + - target: {fileID: 2206467382688623040, guid: 2538d4a3972b37c4c9e8649481dd9a2b, + type: 3} + propertyPath: m_IsActive + value: 0 + objectReference: {fileID: 0} - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, type: 3} propertyPath: m_RootOrder diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs index 3a6382a235..754cc14c19 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs @@ -500,7 +500,7 @@ public void OnSessionListUpdated(NetworkRunner runner, List session public void OnReliableDataReceived(NetworkRunner runner, PlayerRef player, ReliableKey key, ArraySegment data) { - Debug.Log("Server received complete reliable data"); + //Debug.Log("Server received complete reliable data"); byte[] receivedData = data.Array; if (receivedData == null || receivedData.Length == 0) @@ -515,7 +515,7 @@ public void OnReliableDataReceived(NetworkRunner runner, PlayerRef player, Relia public void OnReliableDataProgress(NetworkRunner runner, PlayerRef player, ReliableKey key, float progress) { - Debug.Log("Server received Partial reliable data"); + //Debug.Log("Server received Partial reliable data"); } #endregion From 5d623a2225a27c244aa3b5314d5cfe5d2f3ec404 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Wed, 11 Dec 2024 16:04:26 +0000 Subject: [PATCH 134/174] Implement failsafe logic for GenerateUniqueRoomName We limit the attempts to 10 --- Assets/Scripts/GUI/MultiplayerPanel.cs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/Assets/Scripts/GUI/MultiplayerPanel.cs b/Assets/Scripts/GUI/MultiplayerPanel.cs index a2734ea151..7201c6f079 100644 --- a/Assets/Scripts/GUI/MultiplayerPanel.cs +++ b/Assets/Scripts/GUI/MultiplayerPanel.cs @@ -113,11 +113,22 @@ protected override void OnDisablePanel() private static string GenerateUniqueRoomName() { + const int maxAttempts = 10; string roomName; + int attempts = 0; + do { roomName = GenerateRandomRoomName(); - } while (MultiplayerManager.m_Instance != null && MultiplayerManager.m_Instance.DoesRoomNameExist(roomName)); + attempts++; + } while (MultiplayerManager.m_Instance != null && + MultiplayerManager.m_Instance.DoesRoomNameExist(roomName) && + attempts < maxAttempts); + + if (attempts >= maxAttempts) + { + return "default room"; + } return roomName; } From 7c5c333dc3ca5f60347386924a4d06fd1ca00113 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Thu, 12 Dec 2024 13:35:02 +0000 Subject: [PATCH 135/174] Hide nickname --- Assets/Prefabs/Panels/MultiplayerPanel.prefab | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Assets/Prefabs/Panels/MultiplayerPanel.prefab b/Assets/Prefabs/Panels/MultiplayerPanel.prefab index de8519fd18..2aa3a2c6aa 100644 --- a/Assets/Prefabs/Panels/MultiplayerPanel.prefab +++ b/Assets/Prefabs/Panels/MultiplayerPanel.prefab @@ -1167,7 +1167,7 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: 0.147, y: 0.14799947} + m_AnchoredPosition: {x: 0.147, y: -0.024000466} m_SizeDelta: {x: 1.104868, y: 1.4460607} m_Pivot: {x: 0.5, y: 0.5} --- !u!23 &3100622910448847287 @@ -2812,7 +2812,7 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: 0.466, y: -0.14800048} + m_AnchoredPosition: {x: 0.466, y: -0.3200004} m_SizeDelta: {x: 1.104868, y: 1.4460607} m_Pivot: {x: 0.5, y: 0.5} --- !u!23 &8677733851088090031 @@ -2966,7 +2966,7 @@ GameObject: m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 - m_IsActive: 1 + m_IsActive: 0 --- !u!224 &2677652558882906679 RectTransform: m_ObjectHideFlags: 0 @@ -3768,7 +3768,7 @@ Transform: m_GameObject: {fileID: 6530187638887373145} serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: 0.208, y: -0.48400003, z: -0.042} + m_LocalPosition: {x: 0.208, y: -0.38200003, z: -0.042} m_LocalScale: {x: 0.33, y: 0.33, z: 0.33} m_ConstrainProportionsScale: 0 m_Children: [] @@ -3958,7 +3958,7 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: 0.147, y: -0.73} + m_AnchoredPosition: {x: 0.147, y: -0.64} m_SizeDelta: {x: 1.104868, y: 1.4460607} m_Pivot: {x: 0.5, y: 0.5} --- !u!23 &4811598343171871539 @@ -4124,7 +4124,7 @@ Transform: m_GameObject: {fileID: 7101128147202961155} serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: -0.206, y: -0.48400003, z: -0.042} + m_LocalPosition: {x: -0.206, y: -0.382, z: -0.042} m_LocalScale: {x: 0.33, y: 0.33, z: 0.33} m_ConstrainProportionsScale: 0 m_Children: [] @@ -4299,7 +4299,7 @@ GameObject: m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 - m_IsActive: 1 + m_IsActive: 0 --- !u!4 &2683548132759635989 Transform: m_ObjectHideFlags: 0 @@ -4680,7 +4680,7 @@ Transform: m_GameObject: {fileID: 7521510091458286424} serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: -0.619, y: 0.496, z: -0.042} + m_LocalPosition: {x: -0.619, y: 0.324, z: -0.042} m_LocalScale: {x: 0.15, y: 0.15, z: 0.15} m_ConstrainProportionsScale: 0 m_Children: [] From 2a249a5c113761c50645b794b74d29eeea68a618 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Fri, 13 Dec 2024 08:33:28 +0000 Subject: [PATCH 136/174] Update MultiplayerPanel.cs Updating phrases "Switch to beginner mode to Join Room" -> "Switch to Beginner Mode to use Multiplayer" "You are Room Owner" -> "You are the Room Owner" "You are Not Room Owner" -> "You are not the Room Owner" "$"The room {data.roomName} already exist your joining an existing session." -> "$"Room {data.roomName} already exists. You will be joining an existing session." "/n" -> "\n" --- Assets/Scripts/GUI/MultiplayerPanel.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Assets/Scripts/GUI/MultiplayerPanel.cs b/Assets/Scripts/GUI/MultiplayerPanel.cs index 7201c6f079..2fd83a38b1 100644 --- a/Assets/Scripts/GUI/MultiplayerPanel.cs +++ b/Assets/Scripts/GUI/MultiplayerPanel.cs @@ -189,7 +189,7 @@ private void OnStateUpdated(ConnectionState newState) private void OnRoomOwnershipUpdated(bool isRoomOwner) { if (!m_RoomOwnership) return; - m_RoomOwnership.text = isRoomOwner ? "You are Room Owner" : "You are Not Room Owner"; + m_RoomOwnership.text = isRoomOwner ? "You are the Room Owner" : "You are not the Room Owner"; } private Tuple CheckAdvancedModeActive() @@ -197,7 +197,7 @@ private Tuple CheckAdvancedModeActive() if (PanelManager.m_Instance != null) { bool isAdvancedModeActive = PanelManager.m_Instance.AdvancedModeActive(); - return Tuple.Create(isAdvancedModeActive, "Switch to beginner mode to Join Room"); + return Tuple.Create(isAdvancedModeActive, "Switch to beginner mode use Multiplayer"); } return Tuple.Create(false, ""); } @@ -221,7 +221,7 @@ private Tuple CheckIfRoomExist() if (MultiplayerManager.m_Instance != null && MultiplayerManager.m_Instance.State == ConnectionState.IN_LOBBY) { if (MultiplayerManager.m_Instance.DoesRoomNameExist(data.roomName)) - return Tuple.Create(true, $"The room {data.roomName} already exist your joining an existing session."); + return Tuple.Create(true, $"Room {data.roomName} already exists. You will be joining an existing session."); } return Tuple.Create(false, ""); @@ -241,7 +241,7 @@ private void Alerts() if (isTriggered) { shouldShowAlert = true; - alertMessage += message + "/n"; + alertMessage += message + "\n"; break; } } From dbb7a8a28bea39203b418fb5beae341809adad99 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Fri, 13 Dec 2024 08:39:42 +0000 Subject: [PATCH 137/174] What's New Button On Admin Panel Updating the localization strings for the what's new button on admin panel --- .../Settings/Localization/Strings/Strings Shared Data.asset | 6 +++++- Assets/Settings/Localization/Strings/Strings_de.asset | 2 +- Assets/Settings/Localization/Strings/Strings_en.asset | 2 +- Assets/Settings/Localization/Strings/Strings_es.asset | 2 +- Assets/Settings/Localization/Strings/Strings_fr.asset | 2 +- Assets/Settings/Localization/Strings/Strings_ja.asset | 2 +- Assets/Settings/Localization/Strings/Strings_ko.asset | 2 +- Assets/Settings/Localization/Strings/Strings_zh.asset | 2 +- 8 files changed, 12 insertions(+), 8 deletions(-) diff --git a/Assets/Settings/Localization/Strings/Strings Shared Data.asset b/Assets/Settings/Localization/Strings/Strings Shared Data.asset index d002e87a90..14c4cbfdd7 100644 --- a/Assets/Settings/Localization/Strings/Strings Shared Data.asset +++ b/Assets/Settings/Localization/Strings/Strings Shared Data.asset @@ -52,7 +52,7 @@ MonoBehaviour: m_Metadata: m_Items: [] - m_Id: 5064506084139008 - m_Key: ADMIN_PANEL_SAVE_SKETCH_BUTTON_DESCRIPTION + m_Key: ADMIN_PANEL_WHAT_S_NEW m_Metadata: m_Items: [] - m_Id: 5065176065482752 @@ -3371,6 +3371,10 @@ MonoBehaviour: m_Key: erase text m_Metadata: m_Items: [] + - m_Id: 310819533771841536 + m_Key: New Entry + m_Metadata: + m_Items: [] m_Metadata: m_Items: [] m_KeyGenerator: diff --git a/Assets/Settings/Localization/Strings/Strings_de.asset b/Assets/Settings/Localization/Strings/Strings_de.asset index 4e21e371a5..3bfed66005 100644 --- a/Assets/Settings/Localization/Strings/Strings_de.asset +++ b/Assets/Settings/Localization/Strings/Strings_de.asset @@ -201,7 +201,7 @@ MonoBehaviour: m_Metadata: m_Items: [] - m_Id: 5064506084139008 - m_Localized: Skizze speichern + m_Localized: Was gibt's Neues m_Metadata: m_Items: [] - m_Id: 5065040224559104 diff --git a/Assets/Settings/Localization/Strings/Strings_en.asset b/Assets/Settings/Localization/Strings/Strings_en.asset index f46dfceb03..c1779de9b9 100644 --- a/Assets/Settings/Localization/Strings/Strings_en.asset +++ b/Assets/Settings/Localization/Strings/Strings_en.asset @@ -201,7 +201,7 @@ MonoBehaviour: m_Metadata: m_Items: [] - m_Id: 5064506084139008 - m_Localized: Save Sketch + m_Localized: What's New m_Metadata: m_Items: [] - m_Id: 5065040224559104 diff --git a/Assets/Settings/Localization/Strings/Strings_es.asset b/Assets/Settings/Localization/Strings/Strings_es.asset index af9f4122b1..0c899b6aa3 100644 --- a/Assets/Settings/Localization/Strings/Strings_es.asset +++ b/Assets/Settings/Localization/Strings/Strings_es.asset @@ -201,7 +201,7 @@ MonoBehaviour: m_Metadata: m_Items: [] - m_Id: 5064506084139008 - m_Localized: Guardar dibujo + m_Localized: "Qu\xE9 hay de nuevo" m_Metadata: m_Items: [] - m_Id: 5065040224559104 diff --git a/Assets/Settings/Localization/Strings/Strings_fr.asset b/Assets/Settings/Localization/Strings/Strings_fr.asset index 18d8bc5e93..6a3ea7d9dd 100644 --- a/Assets/Settings/Localization/Strings/Strings_fr.asset +++ b/Assets/Settings/Localization/Strings/Strings_fr.asset @@ -199,7 +199,7 @@ MonoBehaviour: m_Metadata: m_Items: [] - m_Id: 5064506084139008 - m_Localized: Enregistrer le croquis + m_Localized: Quoi de neuf m_Metadata: m_Items: [] - m_Id: 5065040224559104 diff --git a/Assets/Settings/Localization/Strings/Strings_ja.asset b/Assets/Settings/Localization/Strings/Strings_ja.asset index dede3791ab..1979b8ef60 100644 --- a/Assets/Settings/Localization/Strings/Strings_ja.asset +++ b/Assets/Settings/Localization/Strings/Strings_ja.asset @@ -200,7 +200,7 @@ MonoBehaviour: m_Metadata: m_Items: [] - m_Id: 5064506084139008 - m_Localized: "\u30B9\u30B1\u30C3\u30C1\u3092\u4FDD\u5B58" + m_Localized: "\u65B0\u7740\u60C5\u5831" m_Metadata: m_Items: [] - m_Id: 5065040224559104 diff --git a/Assets/Settings/Localization/Strings/Strings_ko.asset b/Assets/Settings/Localization/Strings/Strings_ko.asset index 394d3438a4..b68e7c7ccb 100644 --- a/Assets/Settings/Localization/Strings/Strings_ko.asset +++ b/Assets/Settings/Localization/Strings/Strings_ko.asset @@ -197,7 +197,7 @@ MonoBehaviour: m_Metadata: m_Items: [] - m_Id: 5064506084139008 - m_Localized: "\uC2A4\uCF00\uCE58 \uC800\uC7A5" + m_Localized: "\uC0C8\uB85C\uC6B4 \uC18C\uC2DD" m_Metadata: m_Items: [] - m_Id: 5065040224559104 diff --git a/Assets/Settings/Localization/Strings/Strings_zh.asset b/Assets/Settings/Localization/Strings/Strings_zh.asset index ed52155add..0dbfafacbb 100644 --- a/Assets/Settings/Localization/Strings/Strings_zh.asset +++ b/Assets/Settings/Localization/Strings/Strings_zh.asset @@ -195,7 +195,7 @@ MonoBehaviour: m_Metadata: m_Items: [] - m_Id: 5064506084139008 - m_Localized: "\u4FDD\u5B58\u8349\u56FE" + m_Localized: "\u6709\u4EC0\u4E48\u65B0\u5185\u5BB9" m_Metadata: m_Items: [] - m_Id: 5065040224559104 From 26cf8c78d9cbd0d11c35c1b80a879f55de9dfa5b Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Fri, 13 Dec 2024 10:16:06 +0000 Subject: [PATCH 138/174] Implement avatar speaking feedback Detect if the user is transmitting through the voice channel and display speaking feedback on the remote avatar by changing the HMD color. --- .../Multiplayer/Photon/PhotonPlayerRig.prefab | 9 ++- Assets/Scenes/Main.unity | 3 +- .../Multiplayer/MultiplayerDataStructs.cs | 1 + .../Multiplayer/MultiplayerInterfaces.cs | 3 +- .../Scripts/Multiplayer/MultiplayerManager.cs | 4 +- .../Multiplayer/Photon/PhotonPlayerRig.cs | 59 +++++++++++++++++++ .../Multiplayer/Photon/PhotonVoiceManager.cs | 37 +++++++++++- 7 files changed, 109 insertions(+), 7 deletions(-) diff --git a/Assets/Resources/Multiplayer/Photon/PhotonPlayerRig.prefab b/Assets/Resources/Multiplayer/Photon/PhotonPlayerRig.prefab index 59eb232eb3..88dccac166 100644 --- a/Assets/Resources/Multiplayer/Photon/PhotonPlayerRig.prefab +++ b/Assets/Resources/Multiplayer/Photon/PhotonPlayerRig.prefab @@ -60,6 +60,7 @@ MonoBehaviour: headTransform: {fileID: 5041384770245166357} rightHandTransform: {fileID: 5267290339349674983} leftHandTransform: {fileID: 2164438665615948153} + HMDMeshRenderer: {fileID: 7120707258077581555} m_PlayerId: 0 m_LeftControllerModel: {fileID: 4048656607324710010} m_RightControllerModel: {fileID: 2292257892099998613} @@ -77,7 +78,7 @@ MonoBehaviour: m_EditorClassIdentifier: SortKey: 2839312778 ObjectInterest: 1 - Flags: 2305 + Flags: 264449 NestedObjects: [] NetworkedBehaviours: - {fileID: 7215016235458371273} @@ -2843,3 +2844,9 @@ Transform: type: 3} m_PrefabInstance: {fileID: 8559495263563694728} m_PrefabAsset: {fileID: 0} +--- !u!23 &7120707258077581555 stripped +MeshRenderer: + m_CorrespondingSourceObject: {fileID: 1448111865499013755, guid: 834f4ac71ec35d148acacfca28f04405, + type: 3} + m_PrefabInstance: {fileID: 8559495263563694728} + m_PrefabAsset: {fileID: 0} diff --git a/Assets/Scenes/Main.unity b/Assets/Scenes/Main.unity index bd145b959c..9e4f9d06b7 100644 --- a/Assets/Scenes/Main.unity +++ b/Assets/Scenes/Main.unity @@ -15233,7 +15233,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 91175dcc15224463780e01a8a98b1b60, type: 3} m_Name: m_EditorClassIdentifier: - voiceDetection: 0 + voiceDetection: 1 voiceDetectionThreshold: 0.01 voiceDetectionDelayMs: 500 interestGroup: 0 @@ -15318,6 +15318,7 @@ MonoBehaviour: m_EditorClassIdentifier: batchSize: 60 delayBetweenBatches: 0.05 + m_SyncType: 0 numberOfCommandsExpected: 0 numberOfCommandsSent: 0 --- !u!1 &1057179852 diff --git a/Assets/Scripts/Multiplayer/MultiplayerDataStructs.cs b/Assets/Scripts/Multiplayer/MultiplayerDataStructs.cs index 68922d0186..1c6d51012e 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerDataStructs.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerDataStructs.cs @@ -37,6 +37,7 @@ public struct PlayerRigData public ExtraData ExtraData; public bool IsRoomOwner; public float SceneScale; + public bool isReceivingVoiceTransmission; } diff --git a/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs b/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs index bc3a0687d7..eef8dd7666 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs @@ -54,9 +54,10 @@ public interface IDataConnectionHandler : IConnectionHandler public interface IVoiceConnectionHandler : IConnectionHandler { - + void Update(); bool StartSpeaking(); bool StopSpeaking(); + public bool isTransmitting { get; } } diff --git a/Assets/Scripts/Multiplayer/MultiplayerManager.cs b/Assets/Scripts/Multiplayer/MultiplayerManager.cs index b08e58e241..25eeae1818 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerManager.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerManager.cs @@ -318,6 +318,7 @@ void Update() } m_Manager.Update(); + m_VoiceManager.Update(); // Transmit local player data relative to scene origin var headRelativeToScene = App.Scene.AsScene[App.VrSdk.GetVrCamera().transform]; @@ -350,7 +351,8 @@ void Update() OculusPlayerId = myOculusUserId, }, IsRoomOwner = isUserRoomOwner, - SceneScale = App.Scene.Pose.scale + SceneScale = App.Scene.Pose.scale, + isReceivingVoiceTransmission = m_VoiceManager.isTransmitting }; if (m_LocalPlayer != null) diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonPlayerRig.cs b/Assets/Scripts/Multiplayer/Photon/PhotonPlayerRig.cs index cd81817bf6..a71536577a 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonPlayerRig.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonPlayerRig.cs @@ -18,6 +18,7 @@ using Fusion; using TiltBrush; using System; +using System.Collections; namespace OpenBrush.Multiplayer { @@ -36,14 +37,19 @@ public class PhotonPlayerRig : NetworkBehaviour, ITransientData [Networked] public ulong oculusPlayerId { get; set; } [Networked] public bool IsRoomOwner { get; set; } [Networked] public float SceneScale { get; set; } + [Networked] public bool isReceivingVoiceTransmission { get; set; } PointerScript transientPointer; // The offset transforms. [SerializeField] private Transform headTransform; [SerializeField] private Transform rightHandTransform; [SerializeField] private Transform leftHandTransform; + // The 3D model of the headset + [SerializeField] private Renderer HMDMeshRenderer; private PlayerRigData transmitData; + private Color originalColor; + private Coroutine fadeCoroutine; private bool m_IsSpawned = false; public bool IsSpawned => m_IsSpawned; @@ -68,6 +74,15 @@ public void TransmitData(PlayerRigData data) brushGuid = data.BrushData.Guid; IsRoomOwner = data.IsRoomOwner; SceneScale = data.SceneScale; + isReceivingVoiceTransmission = data.isReceivingVoiceTransmission; + } + + private void Awake() + { + if (HMDMeshRenderer != null && HMDMeshRenderer.material.HasProperty("_EmissionColor")) + { + originalColor = HMDMeshRenderer.material.GetColor("_EmissionColor"); + } } public PlayerRigData ReceiveData() @@ -105,6 +120,7 @@ public PlayerRigData ReceiveData() data.IsRoomOwner = this.IsRoomOwner; data.ExtraData = new ExtraData { OculusPlayerId = this.oculusPlayerId }; data.SceneScale = this.SceneScale; + data.isReceivingVoiceTransmission = this.isReceivingVoiceTransmission; } catch (InvalidOperationException ex) { @@ -210,6 +226,9 @@ public override void Render() ); App.Scene.AsScene[rightHandTransform] = remoteRightTR; + //HMD color + if (isReceivingVoiceTransmission) FadeHMDMeshColor(Color.red); + else FadeHMDMeshColor(originalColor); } } @@ -242,7 +261,47 @@ void OnDestroy() m_IsSpawned = false; } + + public void UpdateHMDMeshColor(Color color) + { + + if (HMDMeshRenderer != null && HMDMeshRenderer.material.HasProperty("_EmissionColor")) + { + HMDMeshRenderer.material.SetColor("_EmissionColor", color); + } + + } + + public void FadeHMDMeshColor(Color targetColor) + { + if (fadeCoroutine != null) + { + StopCoroutine(fadeCoroutine); + } + fadeCoroutine = StartCoroutine(FadeColorRoutine(targetColor)); + } + + private IEnumerator FadeColorRoutine(Color targetColor) + { + + if (HMDMeshRenderer == null || !HMDMeshRenderer.material.HasProperty("_EmissionColor")) yield break; + + Color startColor = HMDMeshRenderer.material.GetColor("_EmissionColor"); + float elapsedTime = 0f; + + while (elapsedTime < 0.2f) + { + elapsedTime += Time.deltaTime; + float t = Mathf.Clamp01(elapsedTime / 0.2f); + Color currentColor = Color.Lerp(startColor, targetColor, t); + HMDMeshRenderer.material.SetColor("_EmissionColor", currentColor); + yield return null; + } + + HMDMeshRenderer.material.SetColor("_EmissionColor", targetColor); + } } + } #endif // FUSION_WEAVER diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonVoiceManager.cs b/Assets/Scripts/Multiplayer/Photon/PhotonVoiceManager.cs index a9a1e5a7a2..aba45887ef 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonVoiceManager.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonVoiceManager.cs @@ -26,20 +26,25 @@ public class PhotonVoiceManager : IVoiceConnectionHandler, IConnectionCallbacks, IMatchmakingCallbacks { + public ConnectionUserInfo UserInfo { get; set; } + public ConnectionState State { get; private set; } + public string LastError { get; private set; } + public bool isTransmitting { get; private set; } + private VoiceConnection m_VoiceConnection; private MultiplayerManager m_Manager; private AppSettings m_PhotonVoiceAppSettings; private Recorder m_Recorder; private bool ConnectedToMaster = false; + private bool wasTransmitting = false; + - public ConnectionUserInfo UserInfo { get; set; } - public ConnectionState State { get; private set; } - public string LastError { get; private set; } public PhotonVoiceManager(MultiplayerManager manager) { m_Manager = manager; Init(); + isTransmitting = false; } public async Task Init() @@ -231,6 +236,32 @@ public bool StopSpeaking() return false; } + public void Update() + { + UpdateSpeechDetection(); + } + + private void UpdateSpeechDetection() + { + if (m_Recorder == null) + { + return; + } + + isTransmitting = m_Recorder.IsCurrentlyTransmitting; + + if (isTransmitting && !wasTransmitting) + {; + Debug.Log("[PhotonVoiceManager] Speech started."); + } + else if (!isTransmitting && wasTransmitting) + { + Debug.Log("[PhotonVoiceManager] Speech stopped."); + } + + wasTransmitting = isTransmitting; + } + #region MatchmakingCallbacks From e70f8870dbcb3768741753567b17b813ad255f51 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Fri, 13 Dec 2024 12:38:09 +0000 Subject: [PATCH 139/174] Implementing nickname display on remote avatars --- Assets/Editor/MultiplayerManagerEditor.cs | 16 ++ Assets/Prefabs/HMDMesh.prefab | 2 +- Assets/Prefabs/Panels/MultiplayerPanel.prefab | 18 +- .../Multiplayer/Photon/PhotonPlayerRig.prefab | 185 +++++++++++++++++- Assets/Scripts/GUI/MultiplayerPanel.cs | 3 +- .../Multiplayer/MultiplayerDataStructs.cs | 2 +- .../Scripts/Multiplayer/MultiplayerManager.cs | 13 +- .../Multiplayer/Photon/PhotonManager.cs | 6 +- .../Multiplayer/Photon/PhotonPlayerRig.cs | 31 ++- 9 files changed, 255 insertions(+), 21 deletions(-) diff --git a/Assets/Editor/MultiplayerManagerEditor.cs b/Assets/Editor/MultiplayerManagerEditor.cs index 782a53f61d..91434ebf73 100644 --- a/Assets/Editor/MultiplayerManagerEditor.cs +++ b/Assets/Editor/MultiplayerManagerEditor.cs @@ -110,11 +110,27 @@ public override void OnInspectorGUI() else remoteUsersRegistered = "Not Assigned"; //Registered remote players + EditorGUILayout.BeginHorizontal(); GUILayout.Label("Registered Remote Players IDs:", EditorStyles.boldLabel); EditorGUILayout.LabelField($"{remoteUsersRegistered}"); EditorGUILayout.EndHorizontal(); + // Display and edit Nickname + GUILayout.Space(10); + EditorGUILayout.LabelField("Nickname", GUI.skin.horizontalSlider); + string currentNickname = multiplayerManager.UserInfo.Nickname; + GUILayout.Label("Nickname", EditorStyles.boldLabel); + string newNickname = EditorGUILayout.TextField("Edit Nickname", currentNickname); + if (newNickname != currentNickname) + { + ConnectionUserInfo updatedUserInfo = multiplayerManager.UserInfo; + updatedUserInfo.Nickname = newNickname; + multiplayerManager.UserInfo = updatedUserInfo; + EditorUtility.SetDirty(target); // Mark the target as dirty to apply changes + } + + Repaint(); } diff --git a/Assets/Prefabs/HMDMesh.prefab b/Assets/Prefabs/HMDMesh.prefab index 6656788973..0a620cf954 100644 --- a/Assets/Prefabs/HMDMesh.prefab +++ b/Assets/Prefabs/HMDMesh.prefab @@ -25,13 +25,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1448111865499013753} + serializedVersion: 2 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: diff --git a/Assets/Prefabs/Panels/MultiplayerPanel.prefab b/Assets/Prefabs/Panels/MultiplayerPanel.prefab index 2aa3a2c6aa..1370db6278 100644 --- a/Assets/Prefabs/Panels/MultiplayerPanel.prefab +++ b/Assets/Prefabs/Panels/MultiplayerPanel.prefab @@ -1167,7 +1167,7 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: 0.147, y: -0.024000466} + m_AnchoredPosition: {x: 0.147, y: 0.138} m_SizeDelta: {x: 1.104868, y: 1.4460607} m_Pivot: {x: 0.5, y: 0.5} --- !u!23 &3100622910448847287 @@ -2812,7 +2812,7 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: 0.466, y: -0.3200004} + m_AnchoredPosition: {x: 0.466, y: -0.1400001} m_SizeDelta: {x: 1.104868, y: 1.4460607} m_Pivot: {x: 0.5, y: 0.5} --- !u!23 &8677733851088090031 @@ -2966,7 +2966,7 @@ GameObject: m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 - m_IsActive: 0 + m_IsActive: 1 --- !u!224 &2677652558882906679 RectTransform: m_ObjectHideFlags: 0 @@ -3768,7 +3768,7 @@ Transform: m_GameObject: {fileID: 6530187638887373145} serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: 0.208, y: -0.38200003, z: -0.042} + m_LocalPosition: {x: 0.208, y: -0.4470001, z: -0.042} m_LocalScale: {x: 0.33, y: 0.33, z: 0.33} m_ConstrainProportionsScale: 0 m_Children: [] @@ -3958,7 +3958,7 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: 0.147, y: -0.64} + m_AnchoredPosition: {x: 0.147, y: -0.755} m_SizeDelta: {x: 1.104868, y: 1.4460607} m_Pivot: {x: 0.5, y: 0.5} --- !u!23 &4811598343171871539 @@ -4124,7 +4124,7 @@ Transform: m_GameObject: {fileID: 7101128147202961155} serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: -0.206, y: -0.382, z: -0.042} + m_LocalPosition: {x: -0.206, y: -0.447, z: -0.042} m_LocalScale: {x: 0.33, y: 0.33, z: 0.33} m_ConstrainProportionsScale: 0 m_Children: [] @@ -4299,7 +4299,7 @@ GameObject: m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 - m_IsActive: 0 + m_IsActive: 1 --- !u!4 &2683548132759635989 Transform: m_ObjectHideFlags: 0 @@ -4680,7 +4680,7 @@ Transform: m_GameObject: {fileID: 7521510091458286424} serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: -0.619, y: 0.324, z: -0.042} + m_LocalPosition: {x: -0.619, y: 0.504, z: -0.042} m_LocalScale: {x: 0.15, y: 0.15, z: 0.15} m_ConstrainProportionsScale: 0 m_Children: [] @@ -4935,7 +4935,7 @@ MonoBehaviour: m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] - m_text: Allerts + m_text: Alerts m_isRightToLeft: 0 m_fontAsset: {fileID: 11400000, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} m_sharedMaterial: {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} diff --git a/Assets/Resources/Multiplayer/Photon/PhotonPlayerRig.prefab b/Assets/Resources/Multiplayer/Photon/PhotonPlayerRig.prefab index 88dccac166..334ead09bf 100644 --- a/Assets/Resources/Multiplayer/Photon/PhotonPlayerRig.prefab +++ b/Assets/Resources/Multiplayer/Photon/PhotonPlayerRig.prefab @@ -57,10 +57,13 @@ MonoBehaviour: _oculusPlayerId: 0 _IsRoomOwner: 0 _SceneScale: 0 + _isReceivingVoiceTransmission: 0 + _Nickname: headTransform: {fileID: 5041384770245166357} rightHandTransform: {fileID: 5267290339349674983} leftHandTransform: {fileID: 2164438665615948153} HMDMeshRenderer: {fileID: 7120707258077581555} + NicknameText: {fileID: 4731291393105903535} m_PlayerId: 0 m_LeftControllerModel: {fileID: 4048656607324710010} m_RightControllerModel: {fileID: 2292257892099998613} @@ -2471,6 +2474,177 @@ MeshRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_AdditionalVertexStreams: {fileID: 0} +--- !u!1 &4787901167856176139 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1111145119338584871} + - component: {fileID: 889561678788871044} + - component: {fileID: 4731291393105903535} + m_Layer: 0 + m_Name: NickNameText + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1111145119338584871 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4787901167856176139} + m_LocalRotation: {x: 0, y: 1, z: 0, w: 0} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0.15, y: 0.15, z: 0.15} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 5041384770245166357} + m_LocalEulerAnglesHint: {x: 0, y: 180, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: -0.009, y: -0.07} + m_SizeDelta: {x: 20, y: 5} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!23 &889561678788871044 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4787901167856176139} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, 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: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + 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!114 &4731291393105903535 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4787901167856176139} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9541d86e2fd84c1d9990edf0852d74ab, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_text: Nickname + m_isRightToLeft: 0 + m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2} + m_sharedMaterial: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2} + m_fontSharedMaterials: [] + m_fontMaterial: {fileID: 0} + m_fontMaterials: [] + m_fontColor32: + serializedVersion: 2 + rgba: 4294967295 + m_fontColor: {r: 1, g: 1, b: 1, a: 1} + m_enableVertexGradient: 0 + m_colorMode: 3 + m_fontColorGradient: + topLeft: {r: 1, g: 1, b: 1, a: 1} + topRight: {r: 1, g: 1, b: 1, a: 1} + bottomLeft: {r: 1, g: 1, b: 1, a: 1} + bottomRight: {r: 1, g: 1, b: 1, a: 1} + m_fontColorGradientPreset: {fileID: 0} + m_spriteAsset: {fileID: 0} + m_tintAllSprites: 0 + m_StyleSheet: {fileID: 0} + m_TextStyleHashCode: -1183493901 + m_overrideHtmlColors: 0 + m_faceColor: + serializedVersion: 2 + rgba: 4294967295 + m_fontSize: 36 + m_fontSizeBase: 36 + m_fontWeight: 400 + m_enableAutoSizing: 0 + m_fontSizeMin: 18 + m_fontSizeMax: 72 + m_fontStyle: 0 + m_HorizontalAlignment: 2 + m_VerticalAlignment: 256 + m_textAlignment: 65535 + m_characterSpacing: 0 + m_wordSpacing: 0 + m_lineSpacing: 0 + m_lineSpacingMax: 0 + m_paragraphSpacing: 0 + m_charWidthMaxAdj: 0 + m_enableWordWrapping: 1 + m_wordWrappingRatios: 0.4 + m_overflowMode: 0 + m_linkedTextComponent: {fileID: 0} + parentLinkedComponent: {fileID: 0} + m_enableKerning: 1 + m_enableExtraPadding: 0 + checkPaddingRequired: 0 + m_isRichText: 1 + m_parseCtrlCharacters: 1 + m_isOrthographic: 0 + m_isCullingEnabled: 0 + m_horizontalMapping: 0 + m_verticalMapping: 0 + m_uvLineOffset: 0 + m_geometrySortingOrder: 0 + m_IsTextObjectScaleStatic: 0 + m_VertexBufferAutoSizeReduction: 0 + m_useMaxVisibleDescender: 1 + m_pageToDisplay: 1 + m_margin: {x: 1.7013121, y: -0.40430713, z: 1.7602301, w: 1.2983242} + m_isUsingLegacyAnimationComponent: 0 + m_isVolumetricText: 0 + _SortingLayer: 0 + _SortingLayerID: 0 + _SortingOrder: 0 + m_hasFontAssetChanged: 0 + m_renderer: {fileID: 889561678788871044} + m_maskType: 0 --- !u!1 &5681254972916902966 GameObject: m_ObjectHideFlags: 0 @@ -2487,22 +2661,27 @@ GameObject: m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!4 &5041384770245166357 -Transform: +--- !u!224 &5041384770245166357 +RectTransform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 5681254972916902966} - serializedVersion: 2 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} + - {fileID: 1111145119338584871} m_Father: {fileID: 6718544642020818535} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 20, y: 5} + m_Pivot: {x: 0.5, y: 0.5} --- !u!1 &5736389435991655852 GameObject: m_ObjectHideFlags: 0 diff --git a/Assets/Scripts/GUI/MultiplayerPanel.cs b/Assets/Scripts/GUI/MultiplayerPanel.cs index 2fd83a38b1..d79b492792 100644 --- a/Assets/Scripts/GUI/MultiplayerPanel.cs +++ b/Assets/Scripts/GUI/MultiplayerPanel.cs @@ -44,7 +44,7 @@ public string NickName get { - if (MultiplayerManager.m_Instance != null) return MultiplayerManager.m_Instance.UserInfo.Nickname; + if (MultiplayerManager.m_Instance) return MultiplayerManager.m_Instance.UserInfo.Nickname; return ""; } set @@ -87,6 +87,7 @@ public void Awake() MultiplayerManager.m_Instance.RoomOwnershipUpdated += OnRoomOwnershipUpdated; } + NickName = "Nickname" + UnityEngine.Random.Range(0, 1000).ToString(); } protected override void OnEnablePanel() diff --git a/Assets/Scripts/Multiplayer/MultiplayerDataStructs.cs b/Assets/Scripts/Multiplayer/MultiplayerDataStructs.cs index 1c6d51012e..8c7a63cdf1 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerDataStructs.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerDataStructs.cs @@ -38,7 +38,7 @@ public struct PlayerRigData public bool IsRoomOwner; public float SceneScale; public bool isReceivingVoiceTransmission; - + public string Nickname; } [System.Serializable] diff --git a/Assets/Scripts/Multiplayer/MultiplayerManager.cs b/Assets/Scripts/Multiplayer/MultiplayerManager.cs index 25eeae1818..fb6217319b 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerManager.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerManager.cs @@ -19,6 +19,7 @@ using UnityEngine; + #if OCULUS_SUPPORTED using OVRPlatform = Oculus.Platform; #endif @@ -92,6 +93,7 @@ public ConnectionUserInfo UserInfo } } } + private string m_oldNickName = null; [HideInInspector] public RoomCreateData data; @@ -317,6 +319,12 @@ void Update() return; } + if (State != ConnectionState.IN_ROOM) + { + m_oldNickName = null; + return; + } + m_Manager.Update(); m_VoiceManager.Update(); @@ -352,9 +360,12 @@ void Update() }, IsRoomOwner = isUserRoomOwner, SceneScale = App.Scene.Pose.scale, - isReceivingVoiceTransmission = m_VoiceManager.isTransmitting + isReceivingVoiceTransmission = m_VoiceManager.isTransmitting, + Nickname = UserInfo.Nickname //TODO: remove from PlayerRigData or encode it and use photon to retrieve the string }; + + if (m_LocalPlayer != null) { m_LocalPlayer.TransmitData(data); diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs index 754cc14c19..7a7f107ff1 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs @@ -155,7 +155,11 @@ public async Task JoinRoom(RoomCreateData roomCreateData) { State = ConnectionState.IN_ROOM; ControllerConsoleScript.m_Instance.AddNewLine("[PhotonManager] Joined Room"); - UserInfo = new ConnectionUserInfo { UserId = m_Runner.UserId }; + UserInfo = new ConnectionUserInfo { + Nickname = UserInfo.Nickname, + UserId = m_Runner.UserId, + Role = UserInfo.Role, + }; } else { diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonPlayerRig.cs b/Assets/Scripts/Multiplayer/Photon/PhotonPlayerRig.cs index a71536577a..48b2d1d762 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonPlayerRig.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonPlayerRig.cs @@ -19,6 +19,7 @@ using TiltBrush; using System; using System.Collections; +using TMPro; namespace OpenBrush.Multiplayer { @@ -38,6 +39,7 @@ public class PhotonPlayerRig : NetworkBehaviour, ITransientData [Networked] public bool IsRoomOwner { get; set; } [Networked] public float SceneScale { get; set; } [Networked] public bool isReceivingVoiceTransmission { get; set; } + [Networked] public string Nickname { get; set; } PointerScript transientPointer; // The offset transforms. @@ -46,6 +48,8 @@ public class PhotonPlayerRig : NetworkBehaviour, ITransientData [SerializeField] private Transform leftHandTransform; // The 3D model of the headset [SerializeField] private Renderer HMDMeshRenderer; + // The Nickname of the player + [SerializeField] private TextMeshPro NicknameText; private PlayerRigData transmitData; private Color originalColor; @@ -75,6 +79,7 @@ public void TransmitData(PlayerRigData data) IsRoomOwner = data.IsRoomOwner; SceneScale = data.SceneScale; isReceivingVoiceTransmission = data.isReceivingVoiceTransmission; + Nickname = data.Nickname; } private void Awake() @@ -121,6 +126,12 @@ public PlayerRigData ReceiveData() data.ExtraData = new ExtraData { OculusPlayerId = this.oculusPlayerId }; data.SceneScale = this.SceneScale; data.isReceivingVoiceTransmission = this.isReceivingVoiceTransmission; + if (!string.IsNullOrEmpty(this.Nickname)) + { + data.Nickname = this.Nickname; + NicknameText.text = this.Nickname; + } + } catch (InvalidOperationException ex) { @@ -142,6 +153,11 @@ public override void Spawned() transientPointer.SetBrush(BrushCatalog.m_Instance.DefaultBrush); transientPointer.SetColor(App.BrushColor.CurrentColor); } + else + { + NicknameText.text = ""; + NicknameText.gameObject.SetActive(false); + } UpdateControllerVisibility(); @@ -262,14 +278,19 @@ void OnDestroy() m_IsSpawned = false; } - public void UpdateHMDMeshColor(Color color) + public void UpdateColor(Color color) { if (HMDMeshRenderer != null && HMDMeshRenderer.material.HasProperty("_EmissionColor")) { HMDMeshRenderer.material.SetColor("_EmissionColor", color); } - + + if (NicknameText != null) + { + NicknameText.outlineColor = color; + } + } public void FadeHMDMeshColor(Color targetColor) @@ -284,7 +305,9 @@ public void FadeHMDMeshColor(Color targetColor) private IEnumerator FadeColorRoutine(Color targetColor) { - if (HMDMeshRenderer == null || !HMDMeshRenderer.material.HasProperty("_EmissionColor")) yield break; + if (HMDMeshRenderer == null || + !HMDMeshRenderer.material.HasProperty("_EmissionColor") || + NicknameText == null) yield break; Color startColor = HMDMeshRenderer.material.GetColor("_EmissionColor"); float elapsedTime = 0f; @@ -294,7 +317,7 @@ private IEnumerator FadeColorRoutine(Color targetColor) elapsedTime += Time.deltaTime; float t = Mathf.Clamp01(elapsedTime / 0.2f); Color currentColor = Color.Lerp(startColor, targetColor, t); - HMDMeshRenderer.material.SetColor("_EmissionColor", currentColor); + UpdateColor(currentColor); yield return null; } From 7c322d152c42868285f36c44c9b3fef7b8fffb4c Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Fri, 13 Dec 2024 17:21:44 +0000 Subject: [PATCH 140/174] changing TAB to beta --- Assets/Prefabs/Panels/MultiplayerPanel.prefab | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Assets/Prefabs/Panels/MultiplayerPanel.prefab b/Assets/Prefabs/Panels/MultiplayerPanel.prefab index 1370db6278..f4e68921e8 100644 --- a/Assets/Prefabs/Panels/MultiplayerPanel.prefab +++ b/Assets/Prefabs/Panels/MultiplayerPanel.prefab @@ -5424,6 +5424,11 @@ PrefabInstance: propertyPath: m_Name value: Beta Tag objectReference: {fileID: 0} + - target: {fileID: 2556331195588322189, guid: 2538d4a3972b37c4c9e8649481dd9a2b, + type: 3} + propertyPath: m_text + value: BETA + objectReference: {fileID: 0} - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, type: 3} propertyPath: m_RootOrder From 34d315388750495ee4ad2c8fc4e021a0ce3512ba Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Fri, 13 Dec 2024 17:26:30 +0000 Subject: [PATCH 141/174] Simplify connection state ConnectionState.IN_ROOM -> "In Room" --- Assets/Scripts/GUI/MultiplayerPanel.cs | 29 +++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/Assets/Scripts/GUI/MultiplayerPanel.cs b/Assets/Scripts/GUI/MultiplayerPanel.cs index d79b492792..4c94524b1c 100644 --- a/Assets/Scripts/GUI/MultiplayerPanel.cs +++ b/Assets/Scripts/GUI/MultiplayerPanel.cs @@ -183,10 +183,37 @@ private async void Disconnect() private void OnStateUpdated(ConnectionState newState) { if (!m_State) return; - m_State.text = "State: " + newState.ToString(); + m_State.text = "State: " + StateToString(newState); UpdateDisplay(); } + private string StateToString(ConnectionState newState) + { + switch (newState) + { + case ConnectionState.INITIALISING: + return "Initialising"; + case ConnectionState.INITIALIZED: + return "Initialized"; + case ConnectionState.DISCONNECTED: + return "Disconnected"; + case ConnectionState.DISCONNECTING: + return "Disconnecting"; + case ConnectionState.CONNECTING: + return "Connecting"; + case ConnectionState.AUTHENTICATING: + return "Authenticating"; + case ConnectionState.IN_LOBBY: + return "In Lobby"; + case ConnectionState.IN_ROOM: + return "In Room"; + case ConnectionState.ERROR: + return "Error"; + default: + return "Unknown"; + } + } + private void OnRoomOwnershipUpdated(bool isRoomOwner) { if (!m_RoomOwnership) return; From dcf37cb61c252977b6a88bbea4a6b9d6978954cd Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Sat, 14 Dec 2024 14:58:28 +0000 Subject: [PATCH 142/174] Implement PhotonRPCBatcher to Reduce Network Congestion and Ensure Reliable Command Delivery Problem: Occasionally, commands fail to be delivered or executed, leading to inconsistencies in the scene. The issue stems from commands being sent but not received, likely due to network congestion suppressing them. Solution: To reduce congestion, we limit the number of RPCs sent per frame by using the PhotonRPCBatcher. --- .../Multiplayer/MultiplayerSceneSync.cs | 30 +---- .../Multiplayer/Photon/PhotonManager.cs | 109 +++++++++++------- .../Multiplayer/Photon/PhotonRPCBatcher.cs | 56 +++++++++ .../Photon/PhotonRPCBatcher.cs.meta | 11 ++ 4 files changed, 135 insertions(+), 71 deletions(-) create mode 100644 Assets/Scripts/Multiplayer/Photon/PhotonRPCBatcher.cs create mode 100644 Assets/Scripts/Multiplayer/Photon/PhotonRPCBatcher.cs.meta diff --git a/Assets/Scripts/Multiplayer/MultiplayerSceneSync.cs b/Assets/Scripts/Multiplayer/MultiplayerSceneSync.cs index 81aca55969..aa8cd6ac53 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerSceneSync.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerSceneSync.cs @@ -154,22 +154,7 @@ public IEnumerator SendCommandHistory(int id) StartSyncProgressDisplayForCommands(id, commands.ToList()); - int packetCounter = 0; - int counter = 0; - foreach (BaseCommand command in commands) - { - int estimatedMessages = EstimateMessagesForCommand(command); - - if (packetCounter + estimatedMessages > batchSize) - { - yield return null; - yield return new WaitForSeconds(delayBetweenBatches); - } - - MultiplayerManager.m_Instance.OnCommandPerformed(command); - packetCounter += estimatedMessages; - counter++; - } + foreach (BaseCommand command in commands) MultiplayerManager.m_Instance.OnCommandPerformed(command); _isSendingCommandHistory = false; @@ -203,19 +188,6 @@ private void CreateBrushStrokeCommands(List strokes, int LastTimestamp) } } - private int EstimateMessagesForCommand(BaseCommand command) - { - switch (command) - { - case BrushStrokeCommand strokeCommand: - int totalControlPoints = strokeCommand.m_Stroke.m_ControlPoints.Length; - return totalControlPoints <= NetworkingConstants.MaxControlPointsPerChunk - ? 1 - : 2 + ((int)Math.Ceiling((double)totalControlPoints / NetworkingConstants.MaxControlPointsPerChunk) - 1); - default: - return 1; - } - } #endregion #region Remote infoCard commands diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs index 7a7f107ff1..5e1c129b4e 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs @@ -269,34 +269,39 @@ public async Task SendCommandToPlayer(BaseCommand command, int playerId) public async Task CheckCommandReception(BaseCommand command, int playerId) { PlayerRef targetPlayer = PlayerRef.FromEncoded(playerId); - PhotonRPC.RPC_CheckCommand(m_Runner, command.Guid, m_Runner.LocalPlayer, targetPlayer); + PhotonRPCBatcher.EnqueueRPC(() => + { PhotonRPC.RPC_CheckCommand(m_Runner, command.Guid, m_Runner.LocalPlayer, targetPlayer); }); return await PhotonRPC.WaitForAcknowledgment(command.Guid); } public async Task CheckStrokeReception(Stroke stroke, int playerId) { PlayerRef targetPlayer = PlayerRef.FromEncoded(playerId); - PhotonRPC.RPC_CheckStroke(m_Runner, stroke.m_Guid, m_Runner.LocalPlayer, targetPlayer); + PhotonRPCBatcher.EnqueueRPC(() => + { PhotonRPC.RPC_CheckStroke(m_Runner, stroke.m_Guid, m_Runner.LocalPlayer, targetPlayer); }); return await PhotonRPC.WaitForAcknowledgment(stroke.m_Guid); } public async Task UndoCommand(BaseCommand command) { - PhotonRPC.RPC_Undo(m_Runner, command.GetType().ToString()); + PhotonRPCBatcher.EnqueueRPC(() => + { 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()); + PhotonRPCBatcher.EnqueueRPC(() => + { 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); + PhotonRPCBatcher.EnqueueRPC(() => + { PhotonRPC.RPC_SyncToSharedAnchor(m_Runner, uuid); }); await Task.Yield(); return true; } @@ -304,7 +309,8 @@ public async Task RpcSyncToSharedAnchor(string uuid) public async Task RpcStartSyncHistory(int id) { PlayerRef playerRef = PlayerRef.FromEncoded(id); - PhotonRPC.RPC_StartHistorySync(m_Runner, playerRef); + PhotonRPCBatcher.EnqueueRPC(() => + { PhotonRPC.RPC_StartHistorySync(m_Runner, playerRef); }); await Task.Yield(); return true; } @@ -312,7 +318,8 @@ public async Task RpcStartSyncHistory(int id) public async Task RpcHistorySyncComplete(int id) { PlayerRef playerRef = PlayerRef.FromEncoded(id); - PhotonRPC.RPC_HistorySyncCompleted(m_Runner, playerRef); + PhotonRPCBatcher.EnqueueRPC(() => + { PhotonRPC.RPC_HistorySyncCompleted(m_Runner, playerRef);}); await Task.Yield(); return true; } @@ -320,7 +327,8 @@ public async Task RpcHistorySyncComplete(int id) public async Task RpcSyncHistoryPercentage(int id, int exp, int snt) { PlayerRef playerRef = PlayerRef.FromEncoded(id); - PhotonRPC.RPC_HistoryPercentageUpdate(m_Runner, playerRef, exp, snt); + PhotonRPCBatcher.EnqueueRPC(() => + { PhotonRPC.RPC_HistoryPercentageUpdate(m_Runner, playerRef, exp, snt);}); await Task.Yield(); return true; } @@ -380,68 +388,85 @@ private bool CommandBrushStroke(BrushStrokeCommand command, PlayerRef playerRef var stroke = command.m_Stroke; int maxPointsPerChunk = NetworkingConstants.MaxControlPointsPerChunk; + int totalPoints = stroke.m_ControlPoints.Length; - if (stroke.m_ControlPoints.Length > maxPointsPerChunk) + // Calculate how many chunks in total we need, including the initial one. + int numberOfChunks = (int)Math.Ceiling((double)totalPoints / maxPointsPerChunk); + + // If we can fit everything in a single message: + if (numberOfChunks == 1) { - // Split and Send - int numSplits = stroke.m_ControlPoints.Length / maxPointsPerChunk; + // Send it all at once as a full stroke + PhotonRPCBatcher.EnqueueRPC(() => + { PhotonRPC.Send_BrushStrokeFull( m_Runner,new NetworkedStroke().Init(stroke),command.Guid, (int)command.NetworkTimestamp, command.ParentGuid, command.ChildrenCount ); }); + return true; + } - var firstStroke = new Stroke(stroke) - { - m_ControlPoints = stroke.m_ControlPoints.Take(maxPointsPerChunk).ToArray(), - m_ControlPointsToDrop = stroke.m_ControlPointsToDrop.Take(maxPointsPerChunk).ToArray() - }; + // More than one chunk: break it down. - var netStroke = new NetworkedStroke().Init(firstStroke); + // Prepare the first chunk + int firstChunkSize = Math.Min(maxPointsPerChunk, totalPoints); + var firstStroke = new Stroke(stroke) + { + m_ControlPoints = stroke.m_ControlPoints.Take(firstChunkSize).ToArray(), + m_ControlPointsToDrop = stroke.m_ControlPointsToDrop.Take(firstChunkSize).ToArray() + }; - var strokeGuid = Guid.NewGuid(); + var netStroke = new NetworkedStroke().Init(firstStroke); + var strokeGuid = Guid.NewGuid(); - // First Stroke - PhotonRPC.Send_BrushStrokeBegin(m_Runner, strokeGuid, netStroke, stroke.m_ControlPoints.Length); + // Send the initial Begin call + PhotonRPCBatcher.EnqueueRPC(() => + { PhotonRPC.Send_BrushStrokeBegin( m_Runner, strokeGuid, netStroke, totalPoints);}); - // Middle - for (int rounds = 1; rounds < numSplits + 1; ++rounds) - { - var controlPoints = stroke.m_ControlPoints.Skip(rounds * maxPointsPerChunk).Take(maxPointsPerChunk).ToArray(); - var dropPoints = stroke.m_ControlPointsToDrop.Skip(rounds * maxPointsPerChunk).Take(maxPointsPerChunk).ToArray(); + // Send the middle "Continue" chunks (if any) + for (int chunkIndex = 1; chunkIndex < numberOfChunks; chunkIndex++) + { + int offset = chunkIndex * maxPointsPerChunk; + int chunkSize = Math.Min(maxPointsPerChunk, totalPoints - offset); - var netControlPoints = new NetworkedControlPoint[controlPoints.Length]; + // Extract this chunk of control points and drop flags + var controlPoints = stroke.m_ControlPoints.Skip(offset).Take(chunkSize).ToArray(); + var dropPoints = stroke.m_ControlPointsToDrop.Skip(offset).Take(chunkSize).ToArray(); - for (int point = 0; point < controlPoints.Length; ++point) - { - netControlPoints[point] = new NetworkedControlPoint().Init(controlPoints[point]); - } - - PhotonRPC.Send_BrushStrokeContinue(m_Runner, strokeGuid, rounds * maxPointsPerChunk, netControlPoints, dropPoints); + // Convert to NetworkedControlPoint + var netControlPoints = new NetworkedControlPoint[chunkSize]; + for (int i = 0; i < chunkSize; ++i) + { + netControlPoints[i] = new NetworkedControlPoint().Init(controlPoints[i]); } - // End - PhotonRPC.Send_BrushStrokeComplete(m_Runner, strokeGuid, command.Guid, (int)command.NetworkTimestamp, command.ParentGuid, command.ChildrenCount); - } - else - { - // Can send in one. - PhotonRPC.Send_BrushStrokeFull(m_Runner, new NetworkedStroke().Init(command.m_Stroke), command.Guid, (int)command.NetworkTimestamp, command.ParentGuid, command.ChildrenCount); + PhotonRPCBatcher.EnqueueRPC(() => + { PhotonRPC.Send_BrushStrokeContinue(m_Runner,strokeGuid, offset,netControlPoints,dropPoints);}); } + + // After all chunks have been sent, send the Complete call + PhotonRPCBatcher.EnqueueRPC(() => + { PhotonRPC.Send_BrushStrokeComplete( m_Runner,strokeGuid, command.Guid, (int)command.NetworkTimestamp, command.ParentGuid, command.ChildrenCount ); }); + return true; } + private bool CommandBase(BaseCommand command) { - PhotonRPC.Send_BaseCommand(m_Runner, command.Guid, command.ParentGuid, command.ChildrenCount); + PhotonRPCBatcher.EnqueueRPC(() => + { PhotonRPC.Send_BaseCommand(m_Runner, command.Guid, command.ParentGuid, command.ChildrenCount); }); return true; } private bool CommandDeleteStroke(DeleteStrokeCommand command, PlayerRef playerRef = default) { - PhotonRPC.Send_DeleteStroke(m_Runner, command.m_TargetStroke.m_Seed, command.Guid, (int)command.NetworkTimestamp, command.ParentGuid, command.ChildrenCount, playerRef); + PhotonRPCBatcher.EnqueueRPC(() => + { PhotonRPC.Send_DeleteStroke(m_Runner, command.m_TargetStroke.m_Seed, command.Guid, (int)command.NetworkTimestamp, command.ParentGuid, command.ChildrenCount, playerRef); }); return true; } private bool CommandSwitchEnvironment(SwitchEnvironmentCommand command, PlayerRef playerRef = default) { Guid environmentGuid = command.m_NextEnvironment.m_Guid; - PhotonRPC.Send_SwitchEnvironment(m_Runner, environmentGuid, command.Guid, (int)command.NetworkTimestamp, command.ParentGuid, command.ChildrenCount, playerRef); + PhotonRPCBatcher.EnqueueRPC(() => + { PhotonRPC.Send_SwitchEnvironment(m_Runner, environmentGuid, command.Guid, (int)command.NetworkTimestamp, command.ParentGuid, command.ChildrenCount, playerRef); }); return true; } #endregion diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonRPCBatcher.cs b/Assets/Scripts/Multiplayer/Photon/PhotonRPCBatcher.cs new file mode 100644 index 0000000000..6a16b8e895 --- /dev/null +++ b/Assets/Scripts/Multiplayer/Photon/PhotonRPCBatcher.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Concurrent; +using System.Threading; +using System.Threading.Tasks; + + +// A static class for batching Photon RPC calls using async/await. +public static class PhotonRPCBatcher +{ + + private static int delayBetweenBatchesMs = 100; + private static ConcurrentQueue rpcQueue = new ConcurrentQueue(); + private static CancellationTokenSource cts = new CancellationTokenSource(); + private static bool isRunning = false; + + // Enqueues an RPC action to be sent. + public static void EnqueueRPC(Action rpcAction) + { + rpcQueue.Enqueue(rpcAction); + + // If not running yet, start the processing loop + if (!isRunning) + { + isRunning = true; + StartProcessingLoop(); + } + } + + // Starts the asynchronous loop that processes the RPC queue. + private static async void StartProcessingLoop() + { + + while (!cts.Token.IsCancellationRequested) + { + if (rpcQueue.TryDequeue(out Action rpcAction)) rpcAction?.Invoke(); + else + { + isRunning = false; + break; + } + + await Task.Delay(delayBetweenBatchesMs, cts.Token); + } + + isRunning = false; + } + + + // Stops the processing loop (if needed). + public static void Stop() + { + cts.Cancel(); + cts = new CancellationTokenSource(); + } + +} diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonRPCBatcher.cs.meta b/Assets/Scripts/Multiplayer/Photon/PhotonRPCBatcher.cs.meta new file mode 100644 index 0000000000..35ee62003e --- /dev/null +++ b/Assets/Scripts/Multiplayer/Photon/PhotonRPCBatcher.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bb1026450b30eca47a4c16779d4aeda7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From 21391ed7bf81cf2f743a0bed9d9820b9a1d2039f Mon Sep 17 00:00:00 2001 From: Mike Miller Date: Mon, 16 Dec 2024 05:54:26 +0200 Subject: [PATCH 143/174] Revert unintended get_license.yml change --- .github/workflows/get_license.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/get_license.yml b/.github/workflows/get_license.yml index a86886bf28..13ee4ee968 100644 --- a/.github/workflows/get_license.yml +++ b/.github/workflows/get_license.yml @@ -14,8 +14,8 @@ jobs: uses: game-ci/unity-request-activation-file@v2 with: unityVersion: 2019.4.25f1 + # Upload artifact (Unity_v20XX.X.XXXX.alf) - name: Expose as artifact - # Upload artifact (Unity_v20XX.X.XXXX.alf) uses: actions/upload-artifact@v4 with: name: ${{ steps.getManualLicenseFile.outputs.filePath }} From 6dddd5bf9edf167bcd1691b57b7c8466981f0251 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Wed, 18 Dec 2024 11:15:26 +0000 Subject: [PATCH 144/174] Remote player controller size fix --- Assets/Resources/Multiplayer/Photon/PhotonPlayerRig.prefab | 4 ++-- Assets/Scripts/Multiplayer/Photon/PhotonPlayerRig.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Assets/Resources/Multiplayer/Photon/PhotonPlayerRig.prefab b/Assets/Resources/Multiplayer/Photon/PhotonPlayerRig.prefab index 334ead09bf..4b507b3ed6 100644 --- a/Assets/Resources/Multiplayer/Photon/PhotonPlayerRig.prefab +++ b/Assets/Resources/Multiplayer/Photon/PhotonPlayerRig.prefab @@ -246,7 +246,7 @@ Transform: serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} - m_LocalScale: {x: 0.15, y: 0.15, z: 0.15} + m_LocalScale: {x: 0.1, y: 0.1, z: 0.1} m_ConstrainProportionsScale: 0 m_Children: - {fileID: 2292257892099907457} @@ -2268,7 +2268,7 @@ Transform: serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} - m_LocalScale: {x: 0.15, y: 0.15, z: 0.15} + m_LocalScale: {x: 0.1, y: 0.1, z: 0.1} m_ConstrainProportionsScale: 0 m_Children: - {fileID: 4048656607325141076} diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonPlayerRig.cs b/Assets/Scripts/Multiplayer/Photon/PhotonPlayerRig.cs index 48b2d1d762..6e6f167d89 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonPlayerRig.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonPlayerRig.cs @@ -215,7 +215,7 @@ public override void Render() transientPointer.BrushSize01 = brushSize; // Calculate the scale based on the scene scale - float clampedSceneScale = Mathf.Clamp(SceneScale, 0.01f, float.MaxValue); + float clampedSceneScale = Mathf.Clamp(SceneScale, 0.1f, 9.8f); float Scale = 1 / clampedSceneScale; // Remote head From 2dca59a2e1cc868731bc1d0fb219d104ea53230c Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Wed, 18 Dec 2024 11:18:07 +0000 Subject: [PATCH 145/174] Synch -> Sync --- Assets/Scripts/Multiplayer/MultiplayerSceneSync.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Assets/Scripts/Multiplayer/MultiplayerSceneSync.cs b/Assets/Scripts/Multiplayer/MultiplayerSceneSync.cs index aa8cd6ac53..dc626bbecb 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerSceneSync.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerSceneSync.cs @@ -259,7 +259,7 @@ public void DisplaySynchInfo() { OutputWindowScript.m_Instance.CreateInfoCardAtController( InputManager.ControllerName.Brush, - "Synch Started!", + "Sync Started!", fPopScalar: 1.0f ); infoCard = RetrieveInfoCard(); @@ -273,7 +273,7 @@ public InfoCardAnimation RetrieveInfoCard() foreach (var card in allInfoCards) { TextMeshPro textComponent = card.GetComponentInChildren(); - if (textComponent != null && textComponent.text.Contains("Synch")) + if (textComponent != null && textComponent.text.Contains("Sync")) { return card; } @@ -286,7 +286,7 @@ public void SynchInfoPercentageUpdate() lock (infoCardLock) { int percentage = (int)((float)SketchMemoryScript.AllStrokesCount() / numberOfCommandsExpected * 100); - string text = $"Synch {percentage}%"; + string text = $"Sync {percentage}%"; if (infoCard == null) DisplaySynchInfo(); @@ -301,7 +301,7 @@ public void HideSynchInfo() { if (infoCard == null) return; - infoCard.GetComponentInChildren().text = "Synch Ended!"; + infoCard.GetComponentInChildren().text = "Sync Ended!"; infoCard.UpdateHoldingDuration(3.0f); } } From 7c0a61064863d3ee9d5bf925aa593212631070b5 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Wed, 18 Dec 2024 11:50:18 +0000 Subject: [PATCH 146/174] Sync not reaching 100% on display --- .../Multiplayer/MultiplayerSceneSync.cs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/Assets/Scripts/Multiplayer/MultiplayerSceneSync.cs b/Assets/Scripts/Multiplayer/MultiplayerSceneSync.cs index dc626bbecb..f6215b69ba 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerSceneSync.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerSceneSync.cs @@ -251,7 +251,8 @@ private void SynchHistoryComplete(int id) private InfoCardAnimation infoCard; private readonly object infoCardLock = new object(); - public void DisplaySynchInfo() + + public void DisplaySynchInfo(string text = "Sync Started!") { lock (infoCardLock) { @@ -259,7 +260,7 @@ public void DisplaySynchInfo() { OutputWindowScript.m_Instance.CreateInfoCardAtController( InputManager.ControllerName.Brush, - "Sync Started!", + text, fPopScalar: 1.0f ); infoCard = RetrieveInfoCard(); @@ -288,7 +289,11 @@ public void SynchInfoPercentageUpdate() int percentage = (int)((float)SketchMemoryScript.AllStrokesCount() / numberOfCommandsExpected * 100); string text = $"Sync {percentage}%"; - if (infoCard == null) DisplaySynchInfo(); + if (infoCard == null) + { + DisplaySynchInfo(text); + return; + } infoCard.GetComponentInChildren().text = text; infoCard.UpdateHoldingDuration(5f); @@ -299,10 +304,15 @@ public void HideSynchInfo() { lock (infoCardLock) { - if (infoCard == null) return; + if (infoCard == null) + { + DisplaySynchInfo("Sync Ended!"); + return; + } infoCard.GetComponentInChildren().text = "Sync Ended!"; infoCard.UpdateHoldingDuration(3.0f); + ControllerConsoleScript.m_Instance.AddNewLine("Sync Ended!"); } } From 6bbbbecc0d82392b243879679addc403e3964311 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Wed, 18 Dec 2024 11:59:39 +0000 Subject: [PATCH 147/174] Nickname disappearing from panel after disconnection --- Assets/Scripts/Multiplayer/Photon/PhotonManager.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs index 5e1c129b4e..88bafc58f2 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs @@ -193,7 +193,12 @@ public async Task Disconnect() { State = ConnectionState.DISCONNECTED; ControllerConsoleScript.m_Instance.AddNewLine("[PhotonManager] Disconnected successfully"); - UserInfo = new ConnectionUserInfo { UserId = m_Runner.UserId }; + UserInfo = new ConnectionUserInfo + { + Nickname = UserInfo.Nickname, + UserId = m_Runner.UserId, + Role = UserInfo.Role, + }; } else { From 914d5fd14b8d9a85bfc3567f9175b87002f05745 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Wed, 18 Dec 2024 15:03:06 +0000 Subject: [PATCH 148/174] Persistent nickname we save the nickname in the PlayerPrefsDataStore --- Assets/Scripts/GUI/MultiplayerPanel.cs | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/Assets/Scripts/GUI/MultiplayerPanel.cs b/Assets/Scripts/GUI/MultiplayerPanel.cs index 4c94524b1c..069a56291d 100644 --- a/Assets/Scripts/GUI/MultiplayerPanel.cs +++ b/Assets/Scripts/GUI/MultiplayerPanel.cs @@ -12,9 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. +using Google.Apis.PeopleService.v1.Data; using OpenBrush.Multiplayer; using System; using System.Collections.Generic; +using System.Threading.Tasks; using TMPro; using UnityEngine; @@ -29,6 +31,8 @@ public class MultiplayerPanel : BasePanel [SerializeField] private TextMeshPro m_AlertsErrors; [SerializeField] private TextMeshPro m_RoomOwnership; + private PlayerPrefsDataStore m_multiplayer; + public string RoomName { get { return data.roomName; } @@ -57,6 +61,7 @@ public string NickName }; MultiplayerManager.m_Instance.UserInfo = ui; UpdateDisplay(); + SaveNickname(value); } } @@ -87,13 +92,28 @@ public void Awake() MultiplayerManager.m_Instance.RoomOwnershipUpdated += OnRoomOwnershipUpdated; } - NickName = "Nickname" + UnityEngine.Random.Range(0, 1000).ToString(); + } + + public async void RetrieveUsername() + { + var storedNickname = await m_multiplayer.GetAsync("nickname"); + NickName = storedNickname ?? "UnamedUser"; + Debug.Log($"Nickname after assignment: {NickName}"); + } + + + private async void SaveNickname(string nickname) + { + await m_multiplayer.StoreAsync("nickname", nickname); } protected override void OnEnablePanel() { base.OnEnablePanel(); + m_multiplayer = new PlayerPrefsDataStore("Multiplayer"); + RetrieveUsername(); + if (MultiplayerManager.m_Instance == null) return; if (MultiplayerManager.m_Instance.State == ConnectionState.INITIALIZED || MultiplayerManager.m_Instance.State == ConnectionState.DISCONNECTED) { From f52e95cd0ae4ae80872f4d23ca956552828ac80c Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Wed, 18 Dec 2024 15:21:19 +0000 Subject: [PATCH 149/174] What's New" button string overwritten Fix --- Assets/Prefabs/Panels/AdminPanel.prefab | 7 +++---- .../Localization/Strings/Strings Shared Data.asset | 8 ++++++++ Assets/Settings/Localization/Strings/Strings_de.asset | 4 ++++ Assets/Settings/Localization/Strings/Strings_es.asset | 4 ++++ Assets/Settings/Localization/Strings/Strings_fr.asset | 4 ++++ Assets/Settings/Localization/Strings/Strings_ja.asset | 4 ++++ Assets/Settings/Localization/Strings/Strings_ko.asset | 4 ++++ Assets/Settings/Localization/Strings/Strings_zh.asset | 4 ++++ 8 files changed, 35 insertions(+), 4 deletions(-) diff --git a/Assets/Prefabs/Panels/AdminPanel.prefab b/Assets/Prefabs/Panels/AdminPanel.prefab index cd6539df84..84b164e987 100644 --- a/Assets/Prefabs/Panels/AdminPanel.prefab +++ b/Assets/Prefabs/Panels/AdminPanel.prefab @@ -187,7 +187,6 @@ MonoBehaviour: m_MemoryWarningButton: {fileID: 1712905289553642} m_MemoryWarningColor: {r: 0.78676474, g: 0.09256055, b: 0.09256055, a: 1} m_MultiplayerButton: {fileID: 5787249127137797294} - m_EmptyButton: {fileID: 1283872556223845477} m_ButtonRotationAngle: 45 m_ShareButtonLoggedOutExtraText: m_TableReference: @@ -422,7 +421,7 @@ MonoBehaviour: m_TableReference: m_TableCollectionName: GUID:c84355079ab3f3e4f8f3812258805f86 m_TableEntryReference: - m_KeyId: 5064506084139008 + m_KeyId: 312729904472465408 m_Key: m_FallbackState: 0 m_WaitForCompletion: 0 @@ -2605,7 +2604,7 @@ MonoBehaviour: m_TableReference: m_TableCollectionName: GUID:c84355079ab3f3e4f8f3812258805f86 m_TableEntryReference: - m_KeyId: 5064506084139008 + m_KeyId: 312730512516521984 m_Key: m_FallbackState: 0 m_WaitForCompletion: 0 @@ -3336,7 +3335,7 @@ MonoBehaviour: m_EditorClassIdentifier: m_DescriptionType: 0 m_DescriptionYOffset: 0 - m_DescriptionText: ADMIN_PANEL_SAVE_SKETCH_BUTTON_DESCRIPTION + m_DescriptionText: ADMIN_PANEL_WHAT_S_NEW_BUTTON_DESCRIPTION m_LocalizedDescription: m_TableReference: m_TableCollectionName: GUID:c84355079ab3f3e4f8f3812258805f86 diff --git a/Assets/Settings/Localization/Strings/Strings Shared Data.asset b/Assets/Settings/Localization/Strings/Strings Shared Data.asset index 14c4cbfdd7..37faa4e532 100644 --- a/Assets/Settings/Localization/Strings/Strings Shared Data.asset +++ b/Assets/Settings/Localization/Strings/Strings Shared Data.asset @@ -3375,6 +3375,14 @@ MonoBehaviour: m_Key: New Entry m_Metadata: m_Items: [] + - m_Id: 312729904472465408 + m_Key: New Entry 1 + m_Metadata: + m_Items: [] + - m_Id: 312730512516521984 + m_Key: ADMIN_PANEL_SAVE_SKETCH_BUTTON_DESCRIPTION + m_Metadata: + m_Items: [] m_Metadata: m_Items: [] m_KeyGenerator: diff --git a/Assets/Settings/Localization/Strings/Strings_de.asset b/Assets/Settings/Localization/Strings/Strings_de.asset index 3bfed66005..69d0c16865 100644 --- a/Assets/Settings/Localization/Strings/Strings_de.asset +++ b/Assets/Settings/Localization/Strings/Strings_de.asset @@ -3596,6 +3596,10 @@ MonoBehaviour: m_Localized: "Text l\xF6schen" m_Metadata: m_Items: [] + - m_Id: 312730512516521984 + m_Localized: Skizze speichern + m_Metadata: + m_Items: [] references: version: 2 RefIds: [] diff --git a/Assets/Settings/Localization/Strings/Strings_es.asset b/Assets/Settings/Localization/Strings/Strings_es.asset index 0c899b6aa3..84af1e952b 100644 --- a/Assets/Settings/Localization/Strings/Strings_es.asset +++ b/Assets/Settings/Localization/Strings/Strings_es.asset @@ -3568,6 +3568,10 @@ MonoBehaviour: m_Localized: Borrar texto m_Metadata: m_Items: [] + - m_Id: 312730512516521984 + m_Localized: Qu\xE9 hay de nuevo + m_Metadata: + m_Items: [] references: version: 2 RefIds: [] diff --git a/Assets/Settings/Localization/Strings/Strings_fr.asset b/Assets/Settings/Localization/Strings/Strings_fr.asset index 6a3ea7d9dd..4aa137bc25 100644 --- a/Assets/Settings/Localization/Strings/Strings_fr.asset +++ b/Assets/Settings/Localization/Strings/Strings_fr.asset @@ -3537,6 +3537,10 @@ MonoBehaviour: m_Localized: Effacer le texte m_Metadata: m_Items: [] + - m_Id: 312730512516521984 + m_Localized: Quoi de neuf + m_Metadata: + m_Items: [] references: version: 2 RefIds: [] diff --git a/Assets/Settings/Localization/Strings/Strings_ja.asset b/Assets/Settings/Localization/Strings/Strings_ja.asset index 1979b8ef60..4d369184db 100644 --- a/Assets/Settings/Localization/Strings/Strings_ja.asset +++ b/Assets/Settings/Localization/Strings/Strings_ja.asset @@ -3490,6 +3490,10 @@ MonoBehaviour: m_Localized: "\u30C6\u30AD\u30B9\u30C8\u3092\u6D88\u53BB" m_Metadata: m_Items: [] + - m_Id: 312730512516521984 + m_Localized: "\u65B0\u7740\u60C5\u5831" + m_Metadata: + m_Items: [] references: version: 2 RefIds: [] diff --git a/Assets/Settings/Localization/Strings/Strings_ko.asset b/Assets/Settings/Localization/Strings/Strings_ko.asset index b68e7c7ccb..6692744251 100644 --- a/Assets/Settings/Localization/Strings/Strings_ko.asset +++ b/Assets/Settings/Localization/Strings/Strings_ko.asset @@ -3556,6 +3556,10 @@ MonoBehaviour: m_Localized: "\uD14D\uC2A4\uD2B8 \uC9C0\uC6B0\uAE30" m_Metadata: m_Items: [] + - m_Id: 312730512516521984 + m_Localized: "\uC0C8\uB85C\uC6B4 \uC18C\uC2DD" + m_Metadata: + m_Items: [] references: version: 2 RefIds: [] diff --git a/Assets/Settings/Localization/Strings/Strings_zh.asset b/Assets/Settings/Localization/Strings/Strings_zh.asset index 0dbfafacbb..ffe2d58b33 100644 --- a/Assets/Settings/Localization/Strings/Strings_zh.asset +++ b/Assets/Settings/Localization/Strings/Strings_zh.asset @@ -3486,6 +3486,10 @@ MonoBehaviour: m_Localized: "\u6E05\u9664\u6587\u672C" m_Metadata: m_Items: [] + - m_Id: 312730512516521984 + m_Localized: "\u6709\u4EC0\u4E48\u65B0\u5185\u5BB9" + m_Metadata: + m_Items: [] references: version: 2 RefIds: [] From 5e9fe0596d79284c1f4cc2cf790db6fad0f7201c Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Wed, 18 Dec 2024 15:52:36 +0000 Subject: [PATCH 150/174] New Define -> MP_PHOTON --- .github/workflows/build.yml | 2 +- .../Scripts/Multiplayer/MultiplayerManager.cs | 4 ++-- .../Scripts/Multiplayer/Photon/PhotonManager.cs | 2 +- Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs | 2 +- .../Multiplayer/Photon/PhotonRPCBatcher.cs | 17 +++++++++++++++++ .../Multiplayer/Photon/PhotonVoiceManager.cs | 2 +- 6 files changed, 23 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 60e1e0cc83..97173fa80b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -452,7 +452,7 @@ jobs: - name: Add PHOTON_PAT specific define if: ${{ env.PHOTON_PAT }} run: | - echo -e "\n -define:PHOTON_UNITY_NETWORKING \n-define:PUN_2_0_OR_NEWER \n-define:PUN_2_OR_NEWER \n-define:PUN_2_19_OR_NEWER \n-define:FUSION_WEAVER \n-define:FUSION2 \n-define:CROSS_PLATFORM_INPUT \n-define:MOBILE_INPUT \n-define:PHOTON_VOICE_DEFINED" | tee -a Assets/csc.rsp + echo -e "\n -define:MP_PHOTON \n -define:PHOTON_UNITY_NETWORKING \n-define:PUN_2_0_OR_NEWER \n-define:PUN_2_OR_NEWER \n-define:PUN_2_19_OR_NEWER \n-define:FUSION_WEAVER \n-define:FUSION2 \n-define:CROSS_PLATFORM_INPUT \n-define:MOBILE_INPUT \n-define:PHOTON_VOICE_DEFINED" | tee -a Assets/csc.rsp - name: Update build matrix specific defines in csc.rsp if: ${{ matrix.extra_defines }} diff --git a/Assets/Scripts/Multiplayer/MultiplayerManager.cs b/Assets/Scripts/Multiplayer/MultiplayerManager.cs index fb6217319b..9b40f24143 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerManager.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerManager.cs @@ -136,13 +136,13 @@ void Start() switch (m_MultiplayerType) { case MultiplayerType.Photon: -#if FUSION_WEAVER +#if MP_PHOTON m_Manager = new PhotonManager(this); m_Manager.Disconnected += OnConnectionHandlerDisconnected; if (m_Manager != null) ControllerConsoleScript.m_Instance.AddNewLine("PhotonManager Loaded"); else ControllerConsoleScript.m_Instance.AddNewLine("PhotonManager Not Loaded"); #endif -#if PHOTON_UNITY_NETWORKING && PHOTON_VOICE_DEFINED +#if MP_PHOTON m_VoiceManager = new PhotonVoiceManager(this); if (m_VoiceManager != null) ControllerConsoleScript.m_Instance.AddNewLine("PhotonVoiceManager Loaded"); else ControllerConsoleScript.m_Instance.AddNewLine("PhotonVoiceManager Not Loaded"); diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs index 88bafc58f2..d1db580652 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#if FUSION_WEAVER +#if MP_PHOTON using System; using System.Collections.Generic; diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs b/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs index 50eb6be15d..5f0ef51085 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs @@ -13,7 +13,7 @@ // limitations under the License. -#if FUSION_WEAVER +#if MP_PHOTON using System; using System.Linq; diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonRPCBatcher.cs b/Assets/Scripts/Multiplayer/Photon/PhotonRPCBatcher.cs index 6a16b8e895..94bf17856f 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonRPCBatcher.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonRPCBatcher.cs @@ -1,3 +1,19 @@ +// 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 MP_PHOTON + using System; using System.Collections.Concurrent; using System.Threading; @@ -54,3 +70,4 @@ public static void Stop() } } +#endif \ No newline at end of file diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonVoiceManager.cs b/Assets/Scripts/Multiplayer/Photon/PhotonVoiceManager.cs index aba45887ef..1d18e6ab65 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonVoiceManager.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonVoiceManager.cs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#if PHOTON_UNITY_NETWORKING && PHOTON_VOICE_DEFINED +#if MP_PHOTON using OpenBrush.Multiplayer; using Photon.Pun; From 6c55e12ab145cc48ab253ba28f3b08826ac3cbc1 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Wed, 18 Dec 2024 15:56:48 +0000 Subject: [PATCH 151/174] Update README.md --- README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.md b/README.md index 2979a01eab..4f13b48ac3 100644 --- a/README.md +++ b/README.md @@ -230,6 +230,20 @@ Follow these steps to build your app for Oculus Quest: `../Builds/OculusMobile_Release_OpenBrush/`. 1. Run `adb install com.Icosa.OpenBrush.apk`. +### Enabling Multiplayer Photon Fusion + +1. Download and install both [Photon Fusion 2.0.3 SDK](https://doc.photonengine.com/fusion/current/getting-started/sdk-download) and [Photon Voice 2](https://assetstore.unity.com/packages/tools/audio/photon-voice-2-130518?srsltid=AfmBOoqJifR_h-nIp73IL5F83GSvOmk3WqKytS1YsxrEUuZqVfAv5kQ9) Or alternatively Download [Photon Fusion 2.0.3 SDK + Photon Voice 2] (https://github.com/icosa-mirror/photon-fusion/releases/tag/Fusion_v2_Voice_2) +1. Restart Unity to trigger the creation of the Photon Fusion and Photon Voice specific defines. +1. In the **Standalone** and **Android** tabs of the Player settings, go to **Other Settings** > **Scripting Define Symbols**. +1. Click the + button to create a new entry. +1. Add `MP_PHOTON` and press **Apply**. +1. Follow the steps to [create your secrets file](#-Generating-Secrets-file). + Add 2 new items to the **Secrets** field. +1. Add these IDs to the `Secrets` file. Both `Photon Fusion` and `Photon Voice` should + have their own entries. +1. Put the app IDs in the `Client ID` field for each. + + ### Publishing to Oculus stores Note: _Tilt Brush_ is a Google trademark. If you intend to publish a cloned From 3bc4043ee1ab9ac442f66ed9e4235a1b6074a9ca Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Wed, 18 Dec 2024 16:17:06 +0000 Subject: [PATCH 152/174] Missing En Description --- Assets/Settings/Localization/Strings/Strings_en.asset | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Assets/Settings/Localization/Strings/Strings_en.asset b/Assets/Settings/Localization/Strings/Strings_en.asset index c1779de9b9..608a133428 100644 --- a/Assets/Settings/Localization/Strings/Strings_en.asset +++ b/Assets/Settings/Localization/Strings/Strings_en.asset @@ -3568,6 +3568,10 @@ MonoBehaviour: m_Localized: Erase text m_Metadata: m_Items: [] + - m_Id: 312730512516521984 + m_Localized: Save Sketch + m_Metadata: + m_Items: [] references: version: 2 RefIds: [] From c017b457c646a114b053af90601dc4879cacbcc7 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Wed, 18 Dec 2024 16:19:00 +0000 Subject: [PATCH 153/174] Fix Labs and Scripts panels --- Assets/Prefabs/Panels/AdminPanel.prefab | 7 +++---- Assets/Prefabs/Panels/AdminPanel_Mobile.prefab | 18 ++++++++++++++++++ Assets/Scenes/Main.unity | 2 +- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/Assets/Prefabs/Panels/AdminPanel.prefab b/Assets/Prefabs/Panels/AdminPanel.prefab index 84b164e987..9ef1cc81b7 100644 --- a/Assets/Prefabs/Panels/AdminPanel.prefab +++ b/Assets/Prefabs/Panels/AdminPanel.prefab @@ -61,13 +61,12 @@ MonoBehaviour: - m_PopUpPrefab: {fileID: 1000012632495268, guid: ce8b545b77426514d8145079c55ac5e7, type: 3} m_Command: 47 - - m_PopUpPrefab: {fileID: 1269800539083404, guid: 8c11503e6d0a24d409b1d8c992652d3b, - type: 3} + - m_PopUpPrefab: {fileID: 197348, guid: d6d515da978a4db419b15f960d57d718, type: 3} m_Command: 31 - - m_PopUpPrefab: {fileID: 1153581385733574731, guid: 545e16288627ef8439942b5ec3dac49e, + - m_PopUpPrefab: {fileID: 1000012632495268, guid: 4d2503fe0d5c9ae48a510658ac9df4a9, type: 3} m_Command: 96 - - m_PopUpPrefab: {fileID: 563727746153209697, guid: 60ddeefaa28cf76478a6671a1193e2c9, + - m_PopUpPrefab: {fileID: 8644332587479430734, guid: 4abd2477ca9b56d4ebbc36af78d00b5d, type: 3} m_Command: 30 m_PanelDescription: diff --git a/Assets/Prefabs/Panels/AdminPanel_Mobile.prefab b/Assets/Prefabs/Panels/AdminPanel_Mobile.prefab index 1c422e5154..cb9e4b1151 100644 --- a/Assets/Prefabs/Panels/AdminPanel_Mobile.prefab +++ b/Assets/Prefabs/Panels/AdminPanel_Mobile.prefab @@ -52,6 +52,24 @@ PrefabInstance: propertyPath: m_LocalEulerAnglesHint.z value: 0 objectReference: {fileID: 0} + - target: {fileID: 114828844590133850, guid: f5b291cbc819737469486452ea6748b8, + type: 3} + propertyPath: m_PanelPopUpMap.Array.data[2].m_PopUpPrefab + value: + objectReference: {fileID: 1269800539083404, guid: 8c11503e6d0a24d409b1d8c992652d3b, + type: 3} + - target: {fileID: 114828844590133850, guid: f5b291cbc819737469486452ea6748b8, + type: 3} + propertyPath: m_PanelPopUpMap.Array.data[3].m_PopUpPrefab + value: + objectReference: {fileID: 1153581385733574731, guid: 545e16288627ef8439942b5ec3dac49e, + type: 3} + - target: {fileID: 114828844590133850, guid: f5b291cbc819737469486452ea6748b8, + type: 3} + propertyPath: m_PanelPopUpMap.Array.data[4].m_PopUpPrefab + value: + objectReference: {fileID: 563727746153209697, guid: 60ddeefaa28cf76478a6671a1193e2c9, + type: 3} m_RemovedComponents: [] m_RemovedGameObjects: [] m_AddedGameObjects: [] diff --git a/Assets/Scenes/Main.unity b/Assets/Scenes/Main.unity index 9e4f9d06b7..fe583c1ace 100644 --- a/Assets/Scenes/Main.unity +++ b/Assets/Scenes/Main.unity @@ -30484,7 +30484,7 @@ MonoBehaviour: m_ModeMono: 1 m_ModeQuest: 1 m_Basic: 0 - m_Advanced: 0 + m_Advanced: 1 - m_PanelPrefab: {fileID: 199434, guid: de22d465caf3f20419b98ce5290a7e57, type: 3} m_ModeVr: 1 m_ModeMono: 1 From 6f494e5c7b18c9f01d9da050ad812c3334f6b485 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Wed, 18 Dec 2024 16:25:50 +0000 Subject: [PATCH 154/174] Disable clear sketch when in multiplayer todo --> 1. enable just for room owner 2. Create RPC comand --- Assets/Scripts/SketchControlsScript.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Assets/Scripts/SketchControlsScript.cs b/Assets/Scripts/SketchControlsScript.cs index db75f61eab..9f25b03344 100644 --- a/Assets/Scripts/SketchControlsScript.cs +++ b/Assets/Scripts/SketchControlsScript.cs @@ -5056,7 +5056,7 @@ public bool IsCommandAvailable(GlobalCommands rEnum, int iParam = -1) (VrAssetService.m_Instance.UploadProgress <= 0.0f) && IsCommandAvailable(GlobalCommands.UploadToGenericCloud); case GlobalCommands.NewSketch: - return SketchHasChanges(); + return SketchHasChanges() && !(MultiplayerManager.m_Instance.State == ConnectionState.IN_ROOM); case GlobalCommands.Credits: case GlobalCommands.AshleysSketch: return !SketchHasChanges() && !SketchMemoryScript.m_Instance.IsMemoryDirty(); From 29ee1e07a5a9864f35ff4f80fd4b280584fc8ace Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Wed, 18 Dec 2024 16:47:06 +0000 Subject: [PATCH 155/174] Update PhotonStructs.cs miss defined --- Assets/Scripts/Multiplayer/Photon/PhotonStructs.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonStructs.cs b/Assets/Scripts/Multiplayer/Photon/PhotonStructs.cs index f5a0ea7c13..ba5ca4c6b9 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonStructs.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonStructs.cs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#if FUSION_WEAVER +#if MP_PHOTON using System; using UnityEngine; From ae2de6c2a04b3165a29bd255467e878774d047aa Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Wed, 18 Dec 2024 18:16:53 +0000 Subject: [PATCH 156/174] Update MultiplayerPanel.prefab Beta Tag via localization --- Assets/Prefabs/Panels/MultiplayerPanel.prefab | 521 ++++++++++++++---- 1 file changed, 412 insertions(+), 109 deletions(-) diff --git a/Assets/Prefabs/Panels/MultiplayerPanel.prefab b/Assets/Prefabs/Panels/MultiplayerPanel.prefab index f4e68921e8..242124bdb5 100644 --- a/Assets/Prefabs/Panels/MultiplayerPanel.prefab +++ b/Assets/Prefabs/Panels/MultiplayerPanel.prefab @@ -948,6 +948,301 @@ MeshRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_AdditionalVertexStreams: {fileID: 0} +--- !u!1 &420082715076421484 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 7629051910496749139} + - component: {fileID: 787750087721286071} + - component: {fileID: 3172630236204918582} + - component: {fileID: 6105232356552213013} + m_Layer: 0 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &7629051910496749139 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 420082715076421484} + 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: 3728028955869875737} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 20, y: 5} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!23 &787750087721286071 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 420082715076421484} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, 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: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + 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!114 &3172630236204918582 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 420082715076421484} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9541d86e2fd84c1d9990edf0852d74ab, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_text: BETA + m_isRightToLeft: 0 + m_fontAsset: {fileID: 11400000, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + m_sharedMaterial: {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + m_fontSharedMaterials: [] + m_fontMaterial: {fileID: 0} + m_fontMaterials: [] + m_fontColor32: + serializedVersion: 2 + rgba: 4278190080 + m_fontColor: {r: 0, g: 0, b: 0, a: 1} + m_enableVertexGradient: 0 + m_colorMode: 3 + m_fontColorGradient: + topLeft: {r: 1, g: 1, b: 1, a: 1} + topRight: {r: 1, g: 1, b: 1, a: 1} + bottomLeft: {r: 1, g: 1, b: 1, a: 1} + bottomRight: {r: 1, g: 1, b: 1, a: 1} + m_fontColorGradientPreset: {fileID: 0} + m_spriteAsset: {fileID: 0} + m_tintAllSprites: 0 + m_StyleSheet: {fileID: 0} + m_TextStyleHashCode: -1183493901 + m_overrideHtmlColors: 0 + m_faceColor: + serializedVersion: 2 + rgba: 4294967295 + m_fontSize: 0.79 + m_fontSizeBase: 0.79 + m_fontWeight: 400 + m_enableAutoSizing: 0 + m_fontSizeMin: 18 + m_fontSizeMax: 72 + m_fontStyle: 0 + m_HorizontalAlignment: 2 + m_VerticalAlignment: 512 + m_textAlignment: 65535 + m_characterSpacing: 0 + m_wordSpacing: 0 + m_lineSpacing: 0 + m_lineSpacingMax: 0 + m_paragraphSpacing: 0 + m_charWidthMaxAdj: 0 + m_enableWordWrapping: 1 + m_wordWrappingRatios: 0.4 + m_overflowMode: 0 + m_linkedTextComponent: {fileID: 0} + parentLinkedComponent: {fileID: 0} + m_enableKerning: 1 + m_enableExtraPadding: 0 + checkPaddingRequired: 0 + m_isRichText: 1 + m_parseCtrlCharacters: 1 + m_isOrthographic: 0 + m_isCullingEnabled: 0 + m_horizontalMapping: 0 + m_verticalMapping: 0 + m_uvLineOffset: 0 + m_geometrySortingOrder: 0 + m_IsTextObjectScaleStatic: 0 + m_VertexBufferAutoSizeReduction: 0 + m_useMaxVisibleDescender: 1 + m_pageToDisplay: 1 + m_margin: {x: 0, y: 0, z: 0, w: 0} + m_isUsingLegacyAnimationComponent: 0 + m_isVolumetricText: 0 + _SortingLayer: 0 + _SortingLayerID: 0 + _SortingOrder: 0 + m_hasFontAssetChanged: 0 + m_renderer: {fileID: 787750087721286071} + m_maskType: 0 +--- !u!114 &6105232356552213013 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 420082715076421484} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 69beb381e244f92449b8c4cf954630e9, type: 3} + m_Name: + m_EditorClassIdentifier: + m_TrackedObjects: + - rid: 3215294302546034780 + references: + version: 2 + RefIds: + - rid: 3215294302546034780 + type: {class: TrackedUGuiGraphic, ns: UnityEngine.Localization.PropertyVariants.TrackedObjects, + asm: Unity.Localization} + data: + m_Target: {fileID: 3172630236204918582} + m_TrackedProperties: + items: + - rid: 3215294302546034781 + m_UpdateType: 0 + - rid: 3215294302546034781 + type: {class: LocalizedStringProperty, ns: UnityEngine.Localization.PropertyVariants.TrackedProperties, + asm: Unity.Localization} + data: + m_Localized: + m_TableReference: + m_TableCollectionName: GUID:c84355079ab3f3e4f8f3812258805f86 + m_TableEntryReference: + m_KeyId: 76121021711179776 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_PropertyPath: m_text +--- !u!1 &696592855043656688 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 9087129829786851406} + - component: {fileID: 3346539883246239398} + - component: {fileID: 7781543330541298351} + m_Layer: 16 + m_Name: Background + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &9087129829786851406 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 696592855043656688} + serializedVersion: 2 + m_LocalRotation: {x: -0.7071068, y: -0, z: -0, w: 0.7071067} + m_LocalPosition: {x: 0, y: 0.0041000843, z: 0.002} + m_LocalScale: {x: 10, y: 6, z: 6} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 3728028955869875737} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &3346539883246239398 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 696592855043656688} + m_Mesh: {fileID: 4300002, guid: 3efe5b86b55995545a231ce66a53b402, type: 3} +--- !u!23 &7781543330541298351 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 696592855043656688} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 28d0bd4d20e3f8143994275754956c5e, 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: 0 + 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!1 &1032351299171828485 GameObject: m_ObjectHideFlags: 0 @@ -1133,6 +1428,40 @@ MonoBehaviour: references: version: 2 RefIds: [] +--- !u!1 &1288145675384974715 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3728028955869875737} + m_Layer: 0 + m_Name: Beta Tag + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &3728028955869875737 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1288145675384974715} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: -0.38268343, w: 0.92387956} + m_LocalPosition: {x: 0.823, y: 1.249, z: -0.02} + m_LocalScale: {x: 2, y: 2, z: 2} + m_ConstrainProportionsScale: 1 + m_Children: + - {fileID: 7629051910496749139} + - {fileID: 9087129829786851406} + - {fileID: 1641104114940865418} + m_Father: {fileID: 499980} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: -45} --- !u!1 &1499154774102387286 GameObject: m_ObjectHideFlags: 0 @@ -2211,6 +2540,89 @@ MonoBehaviour: m_hasFontAssetChanged: 0 m_renderer: {fileID: 6931700069523288738} m_maskType: 0 +--- !u!1 &3214184211823652001 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1641104114940865418} + - component: {fileID: 6601465140255059263} + - component: {fileID: 4866733623279814698} + m_Layer: 16 + m_Name: Border + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1641104114940865418 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3214184211823652001} + serializedVersion: 2 + m_LocalRotation: {x: -0.7071068, y: -0, z: -0, w: 0.7071067} + m_LocalPosition: {x: 0, y: 0.0041000843, z: 0.004} + m_LocalScale: {x: 10.75, y: 10.75, z: 7} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 3728028955869875737} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &6601465140255059263 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3214184211823652001} + m_Mesh: {fileID: 4300002, guid: 3efe5b86b55995545a231ce66a53b402, type: 3} +--- !u!23 &4866733623279814698 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3214184211823652001} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: d153b453067a0724889fb677fef801a1, 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: 0 + 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!1 &3644177191085397888 GameObject: m_ObjectHideFlags: 0 @@ -5411,115 +5823,6 @@ Transform: m_Children: [] m_Father: {fileID: 415082} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!1001 &1116501068073845947 -PrefabInstance: - m_ObjectHideFlags: 0 - serializedVersion: 2 - m_Modification: - serializedVersion: 3 - m_TransformParent: {fileID: 499980} - m_Modifications: - - target: {fileID: 2206467382688623040, guid: 2538d4a3972b37c4c9e8649481dd9a2b, - type: 3} - propertyPath: m_Name - value: Beta Tag - objectReference: {fileID: 0} - - target: {fileID: 2556331195588322189, guid: 2538d4a3972b37c4c9e8649481dd9a2b, - type: 3} - propertyPath: m_text - value: BETA - objectReference: {fileID: 0} - - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, - type: 3} - propertyPath: m_RootOrder - value: 2 - objectReference: {fileID: 0} - - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, - type: 3} - propertyPath: m_LocalScale.x - value: 2 - objectReference: {fileID: 0} - - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, - type: 3} - propertyPath: m_LocalScale.y - value: 2 - objectReference: {fileID: 0} - - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, - type: 3} - propertyPath: m_LocalScale.z - value: 2 - objectReference: {fileID: 0} - - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, - type: 3} - propertyPath: m_LocalPosition.x - value: 0.823 - objectReference: {fileID: 0} - - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, - type: 3} - propertyPath: m_LocalPosition.y - value: 1.249 - objectReference: {fileID: 0} - - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, - type: 3} - propertyPath: m_LocalPosition.z - value: -0.02 - objectReference: {fileID: 0} - - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, - type: 3} - propertyPath: m_LocalRotation.w - value: 0.92387956 - objectReference: {fileID: 0} - - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, - type: 3} - propertyPath: m_LocalRotation.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, - type: 3} - propertyPath: m_LocalRotation.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, - type: 3} - propertyPath: m_LocalRotation.z - value: -0.38268343 - objectReference: {fileID: 0} - - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, - type: 3} - propertyPath: m_LocalEulerAnglesHint.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, - type: 3} - propertyPath: m_LocalEulerAnglesHint.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, - type: 3} - propertyPath: m_LocalEulerAnglesHint.z - value: -45 - objectReference: {fileID: 0} - - target: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, - type: 3} - propertyPath: m_ConstrainProportionsScale - value: 1 - objectReference: {fileID: 0} - - target: {fileID: 6612620938781836974, guid: 2538d4a3972b37c4c9e8649481dd9a2b, - type: 3} - propertyPath: managedReferences[3215294302546034781].m_Localized.m_TableEntryReference.m_KeyId - value: 176097607781543936 - objectReference: {fileID: 0} - m_RemovedComponents: [] - m_RemovedGameObjects: [] - m_AddedGameObjects: [] - m_AddedComponents: [] - m_SourcePrefab: {fileID: 100100000, guid: 2538d4a3972b37c4c9e8649481dd9a2b, type: 3} ---- !u!4 &3728028955869875737 stripped -Transform: - m_CorrespondingSourceObject: {fileID: 4378067676344109730, guid: 2538d4a3972b37c4c9e8649481dd9a2b, - type: 3} - m_PrefabInstance: {fileID: 1116501068073845947} - m_PrefabAsset: {fileID: 0} --- !u!1001 &7380239823636675985 PrefabInstance: m_ObjectHideFlags: 0 From ba44b0f96c8f8e329fea6d9adab677ff82caf5bb Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Thu, 19 Dec 2024 07:23:51 +0000 Subject: [PATCH 157/174] Localization Added for Multiplayer Panel --- Assets/Prefabs/Panels/MultiplayerPanel.prefab | 65 ++++++++++++++++++- Assets/Scripts/GUI/MultiplayerPanel.cs | 29 ++++++--- .../Strings/Strings Shared Data.asset | 44 +++++++++++++ .../Localization/Strings/Strings_de.asset | 29 +++++++++ .../Localization/Strings/Strings_en.asset | 32 +++++++++ .../Localization/Strings/Strings_es.asset | 28 ++++++++ .../Localization/Strings/Strings_fr.asset | 28 ++++++++ .../Localization/Strings/Strings_ja.asset | 28 ++++++++ .../Localization/Strings/Strings_ko.asset | 30 +++++++++ .../Localization/Strings/Strings_zh.asset | 28 ++++++++ 10 files changed, 330 insertions(+), 11 deletions(-) diff --git a/Assets/Prefabs/Panels/MultiplayerPanel.prefab b/Assets/Prefabs/Panels/MultiplayerPanel.prefab index 242124bdb5..be876a0284 100644 --- a/Assets/Prefabs/Panels/MultiplayerPanel.prefab +++ b/Assets/Prefabs/Panels/MultiplayerPanel.prefab @@ -112,10 +112,73 @@ MonoBehaviour: m_PopUpGazeDuration: 0.2 m_PromoBorders: [] m_State: {fileID: 352620620088219705} + m_StatString: + m_TableReference: + m_TableCollectionName: GUID:c84355079ab3f3e4f8f3812258805f86 + m_TableEntryReference: + m_KeyId: 312783011751469056 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] m_RoomNumber: {fileID: 9160963822650286723} + m_RoomNumberString: + m_TableReference: + m_TableCollectionName: GUID:c84355079ab3f3e4f8f3812258805f86 + m_TableEntryReference: + m_KeyId: 312783116814589952 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] m_Nickname: {fileID: 4888819335275065630} - m_AlertsErrors: {fileID: 6437857310660163665} + m_NicknameString: + m_TableReference: + m_TableCollectionName: GUID:c84355079ab3f3e4f8f3812258805f86 + m_TableEntryReference: + m_KeyId: 312783250461892608 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] m_RoomOwnership: {fileID: 1415528830258304631} + m_RoomOwnerString: + m_TableReference: + m_TableCollectionName: GUID:c84355079ab3f3e4f8f3812258805f86 + m_TableEntryReference: + m_KeyId: 312963339069759488 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_NotRoomOwnerString: + m_TableReference: + m_TableCollectionName: GUID:c84355079ab3f3e4f8f3812258805f86 + m_TableEntryReference: + m_KeyId: 312963447907753984 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_AlertsErrors: {fileID: 6437857310660163665} + m_AlertsErrorBeginnerModeActive: + m_TableReference: + m_TableCollectionName: GUID:c84355079ab3f3e4f8f3812258805f86 + m_TableEntryReference: + m_KeyId: 312967307862810624 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_AlertsRoomAlreadyExistent: + m_TableReference: + m_TableCollectionName: GUID:c84355079ab3f3e4f8f3812258805f86 + m_TableEntryReference: + m_KeyId: 312967604588847104 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] references: version: 2 RefIds: [] diff --git a/Assets/Scripts/GUI/MultiplayerPanel.cs b/Assets/Scripts/GUI/MultiplayerPanel.cs index 069a56291d..9138d341df 100644 --- a/Assets/Scripts/GUI/MultiplayerPanel.cs +++ b/Assets/Scripts/GUI/MultiplayerPanel.cs @@ -12,13 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -using Google.Apis.PeopleService.v1.Data; using OpenBrush.Multiplayer; using System; using System.Collections.Generic; -using System.Threading.Tasks; using TMPro; using UnityEngine; +using UnityEngine.Localization; namespace TiltBrush { @@ -26,10 +25,18 @@ public class MultiplayerPanel : BasePanel { [SerializeField] private TextMeshPro m_State; + [SerializeField] private LocalizedString m_StatString; [SerializeField] private TextMeshPro m_RoomNumber; + [SerializeField] private LocalizedString m_RoomNumberString; [SerializeField] private TextMeshPro m_Nickname; - [SerializeField] private TextMeshPro m_AlertsErrors; + [SerializeField] private LocalizedString m_NicknameString; [SerializeField] private TextMeshPro m_RoomOwnership; + [SerializeField] private LocalizedString m_RoomOwnerString; + [SerializeField] private LocalizedString m_NotRoomOwnerString; + [SerializeField] private TextMeshPro m_AlertsErrors; + [SerializeField] private LocalizedString m_AlertsErrorBeginnerModeActive; + [SerializeField] private LocalizedString m_AlertsRoomAlreadyExistent; + private PlayerPrefsDataStore m_multiplayer; @@ -98,7 +105,6 @@ public async void RetrieveUsername() { var storedNickname = await m_multiplayer.GetAsync("nickname"); NickName = storedNickname ?? "UnamedUser"; - Debug.Log($"Nickname after assignment: {NickName}"); } @@ -162,8 +168,8 @@ private static string GenerateRandomRoomName() private void UpdateDisplay() { - if (m_RoomNumber) m_RoomNumber.text = "RoomName: " + data.roomName; - if (m_Nickname) m_Nickname.text = "Nickname: " + NickName; + if (m_RoomNumber) m_RoomNumber.text = m_RoomNumberString.GetLocalizedString() + data.roomName; + if (m_Nickname) m_Nickname.text = m_NicknameString.GetLocalizedString() + NickName; Alerts(); } @@ -203,7 +209,7 @@ private async void Disconnect() private void OnStateUpdated(ConnectionState newState) { if (!m_State) return; - m_State.text = "State: " + StateToString(newState); + m_State.text = m_StatString.GetLocalizedString() + StateToString(newState); UpdateDisplay(); } @@ -237,7 +243,10 @@ private string StateToString(ConnectionState newState) private void OnRoomOwnershipUpdated(bool isRoomOwner) { if (!m_RoomOwnership) return; - m_RoomOwnership.text = isRoomOwner ? "You are the Room Owner" : "You are not the Room Owner"; + + var localizedString = isRoomOwner ? m_RoomOwnerString : m_NotRoomOwnerString; + localizedString.GetLocalizedStringAsync().Completed += handle => + { m_RoomOwnership.text = handle.Result; }; } private Tuple CheckAdvancedModeActive() @@ -245,7 +254,7 @@ private Tuple CheckAdvancedModeActive() if (PanelManager.m_Instance != null) { bool isAdvancedModeActive = PanelManager.m_Instance.AdvancedModeActive(); - return Tuple.Create(isAdvancedModeActive, "Switch to beginner mode use Multiplayer"); + return Tuple.Create(isAdvancedModeActive, m_AlertsErrorBeginnerModeActive.GetLocalizedString()); } return Tuple.Create(false, ""); } @@ -269,7 +278,7 @@ private Tuple CheckIfRoomExist() if (MultiplayerManager.m_Instance != null && MultiplayerManager.m_Instance.State == ConnectionState.IN_LOBBY) { if (MultiplayerManager.m_Instance.DoesRoomNameExist(data.roomName)) - return Tuple.Create(true, $"Room {data.roomName} already exists. You will be joining an existing session."); + return Tuple.Create(true, m_AlertsRoomAlreadyExistent.GetLocalizedString()); } return Tuple.Create(false, ""); diff --git a/Assets/Settings/Localization/Strings/Strings Shared Data.asset b/Assets/Settings/Localization/Strings/Strings Shared Data.asset index 37faa4e532..71c4d8d15b 100644 --- a/Assets/Settings/Localization/Strings/Strings Shared Data.asset +++ b/Assets/Settings/Localization/Strings/Strings Shared Data.asset @@ -3383,6 +3383,50 @@ MonoBehaviour: m_Key: ADMIN_PANEL_SAVE_SKETCH_BUTTON_DESCRIPTION m_Metadata: m_Items: [] + - m_Id: 312781662766833664 + m_Key: MULTIPLAYER_STATUS + m_Metadata: + m_Items: [] + - m_Id: 312783011751469056 + m_Key: MULTIPLAYER__PANEL_STATE + m_Metadata: + m_Items: [] + - m_Id: 312783116814589952 + m_Key: MULTIPLAYER_PANEL_ROOM_NAME + m_Metadata: + m_Items: [] + - m_Id: 312783250461892608 + m_Key: MULTIPLAYER_PANEL_NICKNAME + m_Metadata: + m_Items: [] + - m_Id: 312783415369342976 + m_Key: New Entry 2 + m_Metadata: + m_Items: [] + - m_Id: 312962917202468864 + m_Key: MULTIPLAYER_PANEL_ROOM_OWNER + m_Metadata: + m_Items: [] + - m_Id: 312963339069759488 + m_Key: MULTIPLAYER_PANEL_ROOM_OWNE + m_Metadata: + m_Items: [] + - m_Id: 312963447907753984 + m_Key: MULTIPLAYER_PANEL_NOT_ROOM_OWNER + m_Metadata: + m_Items: [] + - m_Id: 312967307862810624 + m_Key: MULTIPLAYER_PANEL_ALERT_BEGINNER_MODE + m_Metadata: + m_Items: [] + - m_Id: 312967595889860608 + m_Key: New Entry 3 + m_Metadata: + m_Items: [] + - m_Id: 312967604588847104 + m_Key: MULTIPLAYER_PANEL_ALERT_ROOM_EXIST + m_Metadata: + m_Items: [] m_Metadata: m_Items: [] m_KeyGenerator: diff --git a/Assets/Settings/Localization/Strings/Strings_de.asset b/Assets/Settings/Localization/Strings/Strings_de.asset index 69d0c16865..21b1c28880 100644 --- a/Assets/Settings/Localization/Strings/Strings_de.asset +++ b/Assets/Settings/Localization/Strings/Strings_de.asset @@ -3600,6 +3600,35 @@ MonoBehaviour: m_Localized: Skizze speichern m_Metadata: m_Items: [] + - m_Id: 312783011751469056 + m_Localized: 'Status: ' + m_Metadata: + m_Items: [] + - m_Id: 312783116814589952 + m_Localized: 'Raumname: ' + m_Metadata: + m_Items: [] + - m_Id: 312783250461892608 + m_Localized: 'Spitzname: ' + m_Metadata: + m_Items: [] + - m_Id: 312963339069759488 + m_Localized: Sie sind der Raumbesitzer + m_Metadata: + m_Items: [] + - m_Id: 312963447907753984 + m_Localized: Sie sind nicht der Raumbesitzer + m_Metadata: + m_Items: [] + - m_Id: 312967307862810624 + m_Localized: "Wechseln Sie in den Anf\xE4nger-Modus, um Multiplayer zu verwenden" + m_Metadata: + m_Items: [] + - m_Id: 312967604588847104 + m_Localized: Der Raum existiert bereits. Sie werden einer bestehenden Sitzung + beitreten. + m_Metadata: + m_Items: [] references: version: 2 RefIds: [] diff --git a/Assets/Settings/Localization/Strings/Strings_en.asset b/Assets/Settings/Localization/Strings/Strings_en.asset index 608a133428..3ac06298b7 100644 --- a/Assets/Settings/Localization/Strings/Strings_en.asset +++ b/Assets/Settings/Localization/Strings/Strings_en.asset @@ -3572,6 +3572,38 @@ MonoBehaviour: m_Localized: Save Sketch m_Metadata: m_Items: [] + - m_Id: 312781662766833664 + m_Localized: 'Status:' + m_Metadata: + m_Items: [] + - m_Id: 312783011751469056 + m_Localized: 'Status: ' + m_Metadata: + m_Items: [] + - m_Id: 312963339069759488 + m_Localized: You are the Room Owner + m_Metadata: + m_Items: [] + - m_Id: 312963447907753984 + m_Localized: You are not the Room Owner + m_Metadata: + m_Items: [] + - m_Id: 312783116814589952 + m_Localized: 'RoomName: ' + m_Metadata: + m_Items: [] + - m_Id: 312783250461892608 + m_Localized: 'Nickname:' + m_Metadata: + m_Items: [] + - m_Id: 312967307862810624 + m_Localized: Switch to beginner mode use Multiplayer + m_Metadata: + m_Items: [] + - m_Id: 312967604588847104 + m_Localized: Room already exists. You will be joining an existing session. + m_Metadata: + m_Items: [] references: version: 2 RefIds: [] diff --git a/Assets/Settings/Localization/Strings/Strings_es.asset b/Assets/Settings/Localization/Strings/Strings_es.asset index 84af1e952b..9b5de33b4f 100644 --- a/Assets/Settings/Localization/Strings/Strings_es.asset +++ b/Assets/Settings/Localization/Strings/Strings_es.asset @@ -3572,6 +3572,34 @@ MonoBehaviour: m_Localized: Qu\xE9 hay de nuevo m_Metadata: m_Items: [] + - m_Id: 312783011751469056 + m_Localized: 'Estado: ' + m_Metadata: + m_Items: [] + - m_Id: 312783116814589952 + m_Localized: 'Nombre de la sala: ' + m_Metadata: + m_Items: [] + - m_Id: 312783250461892608 + m_Localized: 'Apodo: ' + m_Metadata: + m_Items: [] + - m_Id: 312963339069759488 + m_Localized: Eres el propietario de la sala + m_Metadata: + m_Items: [] + - m_Id: 312963447907753984 + m_Localized: No eres el propietario de la sala + m_Metadata: + m_Items: [] + - m_Id: 312967307862810624 + m_Localized: Cambia al modo principiante para usar el multijugador + m_Metadata: + m_Items: [] + - m_Id: 312967604588847104 + m_Localized: "La sala ya existe. Te unir\xE1s a una sesi\xF3n existente." + m_Metadata: + m_Items: [] references: version: 2 RefIds: [] diff --git a/Assets/Settings/Localization/Strings/Strings_fr.asset b/Assets/Settings/Localization/Strings/Strings_fr.asset index 4aa137bc25..8b7b0e83a2 100644 --- a/Assets/Settings/Localization/Strings/Strings_fr.asset +++ b/Assets/Settings/Localization/Strings/Strings_fr.asset @@ -3541,6 +3541,34 @@ MonoBehaviour: m_Localized: Quoi de neuf m_Metadata: m_Items: [] + - m_Id: 312783011751469056 + m_Localized: 'Statut: ' + m_Metadata: + m_Items: [] + - m_Id: 312783116814589952 + m_Localized: "Nom de la salle\_: " + m_Metadata: + m_Items: [] + - m_Id: 312783250461892608 + m_Localized: "Surnom\_: " + m_Metadata: + m_Items: [] + - m_Id: 312963339069759488 + m_Localized: "Vous \xEAtes le propri\xE9taire de la salle" + m_Metadata: + m_Items: [] + - m_Id: 312963447907753984 + m_Localized: "Vous n'\xEAtes pas le propri\xE9taire de la salle" + m_Metadata: + m_Items: [] + - m_Id: 312967307862810624 + m_Localized: "Passez en mode d\xE9butant pour utiliser le multijoueur" + m_Metadata: + m_Items: [] + - m_Id: 312967604588847104 + m_Localized: "La salle existe d\xE9j\xE0. Vous allez rejoindre une session existante." + m_Metadata: + m_Items: [] references: version: 2 RefIds: [] diff --git a/Assets/Settings/Localization/Strings/Strings_ja.asset b/Assets/Settings/Localization/Strings/Strings_ja.asset index 4d369184db..55af60b17b 100644 --- a/Assets/Settings/Localization/Strings/Strings_ja.asset +++ b/Assets/Settings/Localization/Strings/Strings_ja.asset @@ -3494,6 +3494,34 @@ MonoBehaviour: m_Localized: "\u65B0\u7740\u60C5\u5831" m_Metadata: m_Items: [] + - m_Id: 312783011751469056 + m_Localized: "\u30B9\u30C6\u30FC\u30BF\u30B9\uFF1A" + m_Metadata: + m_Items: [] + - m_Id: 312783116814589952 + m_Localized: "\u30EB\u30FC\u30E0\u540D\uFF1A" + m_Metadata: + m_Items: [] + - m_Id: 312783250461892608 + m_Localized: " \u30CB\u30C3\u30AF\u30CD\u30FC\u30E0\uFF1A" + m_Metadata: + m_Items: [] + - m_Id: 312963339069759488 + m_Localized: "\u3042\u306A\u305F\u306F\u30EB\u30FC\u30E0\u306E\u6240\u6709\u8005\u3067\u3059" + m_Metadata: + m_Items: [] + - m_Id: 312963447907753984 + m_Localized: "\u3042\u306A\u305F\u306F\u30EB\u30FC\u30E0\u306E\u6240\u6709\u8005\u3067\u306F\u3042\u308A\u307E\u305B\u3093" + m_Metadata: + m_Items: [] + - m_Id: 312967307862810624 + m_Localized: "\u30DE\u30EB\u30C1\u30D7\u30EC\u30A4\u30E4\u30FC\u3092\u4F7F\u7528\u3059\u308B\u306B\u306F\u3001\u30D3\u30AE\u30CA\u30FC\u30E2\u30FC\u30C9\u306B\u5207\u308A\u66FF\u3048\u3066\u304F\u3060\u3055\u3044" + m_Metadata: + m_Items: [] + - m_Id: 312967604588847104 + m_Localized: "\u30EB\u30FC\u30E0\u306F\u65E2\u306B\u5B58\u5728\u3057\u307E\u3059\u3002\u65E2\u5B58\u306E\u30BB\u30C3\u30B7\u30E7\u30F3\u306B\u53C2\u52A0\u3057\u307E\u3059\u3002" + m_Metadata: + m_Items: [] references: version: 2 RefIds: [] diff --git a/Assets/Settings/Localization/Strings/Strings_ko.asset b/Assets/Settings/Localization/Strings/Strings_ko.asset index 6692744251..74fbf82d2e 100644 --- a/Assets/Settings/Localization/Strings/Strings_ko.asset +++ b/Assets/Settings/Localization/Strings/Strings_ko.asset @@ -3560,6 +3560,36 @@ MonoBehaviour: m_Localized: "\uC0C8\uB85C\uC6B4 \uC18C\uC2DD" m_Metadata: m_Items: [] + - m_Id: 312783011751469056 + m_Localized: "\uC0C1\uD0DC: " + m_Metadata: + m_Items: [] + - m_Id: 312783116814589952 + m_Localized: "\uBC29 \uC774\uB984: " + m_Metadata: + m_Items: [] + - m_Id: 312783250461892608 + m_Localized: "\uB2C9\uB124\uC784: " + m_Metadata: + m_Items: [] + - m_Id: 312963339069759488 + m_Localized: "\uB2F9\uC2E0\uC740 \uBC29\uC758 \uC18C\uC720\uC790\uC785\uB2C8\uB2E4" + m_Metadata: + m_Items: [] + - m_Id: 312963447907753984 + m_Localized: "\uB2F9\uC2E0\uC740 \uBC29\uC758 \uC18C\uC720\uC790\uAC00 \uC544\uB2D9\uB2C8\uB2E4" + m_Metadata: + m_Items: [] + - m_Id: 312967307862810624 + m_Localized: "\uBA40\uD2F0\uD50C\uB808\uC774\uC5B4\uB97C \uC0AC\uC6A9\uD558\uB824\uBA74 + \uCD08\uBCF4\uC790 \uBAA8\uB4DC\uB85C \uC804\uD658\uD558\uC138\uC694" + m_Metadata: + m_Items: [] + - m_Id: 312967604588847104 + m_Localized: "\uBC29\uC774 \uC774\uBBF8 \uC874\uC7AC\uD569\uB2C8\uB2E4. \uAE30\uC874 + \uC138\uC158\uC5D0 \uCC38\uC5EC\uD569\uB2C8\uB2E4." + m_Metadata: + m_Items: [] references: version: 2 RefIds: [] diff --git a/Assets/Settings/Localization/Strings/Strings_zh.asset b/Assets/Settings/Localization/Strings/Strings_zh.asset index ffe2d58b33..adc6b43c9c 100644 --- a/Assets/Settings/Localization/Strings/Strings_zh.asset +++ b/Assets/Settings/Localization/Strings/Strings_zh.asset @@ -3490,6 +3490,34 @@ MonoBehaviour: m_Localized: "\u6709\u4EC0\u4E48\u65B0\u5185\u5BB9" m_Metadata: m_Items: [] + - m_Id: 312783011751469056 + m_Localized: " \u72B6\u6001\uFF1A" + m_Metadata: + m_Items: [] + - m_Id: 312783116814589952 + m_Localized: "\u623F\u95F4\u540D\u79F0\uFF1A" + m_Metadata: + m_Items: [] + - m_Id: 312783250461892608 + m_Localized: "\u6635\u79F0\uFF1A" + m_Metadata: + m_Items: [] + - m_Id: 312963339069759488 + m_Localized: "\u60A8\u662F\u623F\u95F4\u6240\u6709\u8005" + m_Metadata: + m_Items: [] + - m_Id: 312963447907753984 + m_Localized: "\u60A8\u4E0D\u662F\u623F\u95F4\u6240\u6709\u8005" + m_Metadata: + m_Items: [] + - m_Id: 312967307862810624 + m_Localized: "\u5207\u6362\u5230\u521D\u5B66\u8005\u6A21\u5F0F\u4EE5\u4F7F\u7528\u591A\u4EBA\u6E38\u620F" + m_Metadata: + m_Items: [] + - m_Id: 312967604588847104 + m_Localized: "\u623F\u95F4\u5DF2\u5B58\u5728\u3002\u60A8\u5C06\u52A0\u5165\u4E00\u4E2A\u73B0\u6709\u7684\u4F1A\u8BDD\u3002" + m_Metadata: + m_Items: [] references: version: 2 RefIds: [] From f7ea16e3af50e3a526bc0f123cad436c83b46dc6 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Thu, 19 Dec 2024 07:32:29 +0000 Subject: [PATCH 158/174] InitialiSing -> InitialiZing --- Assets/Scripts/GUI/MultiplayerPanel.cs | 4 ++-- Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs | 2 +- Assets/Scripts/Multiplayer/MultiplayerManager.cs | 2 +- Assets/Scripts/Multiplayer/Photon/PhotonManager.cs | 2 +- Assets/Scripts/Multiplayer/Photon/PhotonVoiceManager.cs | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Assets/Scripts/GUI/MultiplayerPanel.cs b/Assets/Scripts/GUI/MultiplayerPanel.cs index 9138d341df..92194b4c57 100644 --- a/Assets/Scripts/GUI/MultiplayerPanel.cs +++ b/Assets/Scripts/GUI/MultiplayerPanel.cs @@ -217,8 +217,8 @@ private string StateToString(ConnectionState newState) { switch (newState) { - case ConnectionState.INITIALISING: - return "Initialising"; + case ConnectionState.INITIALIZING: + return "Initializing"; case ConnectionState.INITIALIZED: return "Initialized"; case ConnectionState.DISCONNECTED: diff --git a/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs b/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs index eef8dd7666..3edab1b39f 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs @@ -63,7 +63,7 @@ public interface IVoiceConnectionHandler : IConnectionHandler public enum ConnectionState { - INITIALISING = 0, + INITIALIZING = 0, INITIALIZED = 1, DISCONNECTED = 2, DISCONNECTING = 3, diff --git a/Assets/Scripts/Multiplayer/MultiplayerManager.cs b/Assets/Scripts/Multiplayer/MultiplayerManager.cs index 9b40f24143..2293913e0a 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerManager.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerManager.cs @@ -132,7 +132,7 @@ void Start() }); #endif - State = ConnectionState.INITIALISING; + State = ConnectionState.INITIALIZING; switch (m_MultiplayerType) { case MultiplayerType.Photon: diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs index d1db580652..c4af325129 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs @@ -62,7 +62,7 @@ public async Task Init() { try { - State = ConnectionState.INITIALISING; + State = ConnectionState.INITIALIZING; var runnerGO = new GameObject("Photon Network Components"); m_Runner = runnerGO.AddComponent(); m_Runner.gameObject.AddComponent(); diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonVoiceManager.cs b/Assets/Scripts/Multiplayer/Photon/PhotonVoiceManager.cs index 1d18e6ab65..d6d4df3827 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonVoiceManager.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonVoiceManager.cs @@ -52,7 +52,7 @@ public async Task Init() try { - State = ConnectionState.INITIALISING; + State = ConnectionState.INITIALIZING; m_VoiceConnection = GameObject.FindFirstObjectByType(); if (m_VoiceConnection == null) throw new Exception("[PhotonVoiceManager] VoiceConnection component not found in scene"); PhotonNetwork.LogLevel = PunLogLevel.ErrorsOnly; From 5988e9c13588d41b633b862343def04f74555880 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Thu, 19 Dec 2024 07:34:59 +0000 Subject: [PATCH 159/174] UnamedUser -> UnnamedUser --- Assets/Scripts/GUI/MultiplayerPanel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Assets/Scripts/GUI/MultiplayerPanel.cs b/Assets/Scripts/GUI/MultiplayerPanel.cs index 92194b4c57..bfe03ed2bf 100644 --- a/Assets/Scripts/GUI/MultiplayerPanel.cs +++ b/Assets/Scripts/GUI/MultiplayerPanel.cs @@ -104,7 +104,7 @@ public void Awake() public async void RetrieveUsername() { var storedNickname = await m_multiplayer.GetAsync("nickname"); - NickName = storedNickname ?? "UnamedUser"; + NickName = storedNickname ?? "UnnamedUser"; } From 66de97c24a86ee5fefe07a14f550dac21d5094af Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Thu, 19 Dec 2024 07:52:58 +0000 Subject: [PATCH 160/174] Update README.md fixes to readme --- README.md | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 4f13b48ac3..cfabe33896 100644 --- a/README.md +++ b/README.md @@ -230,20 +230,18 @@ Follow these steps to build your app for Oculus Quest: `../Builds/OculusMobile_Release_OpenBrush/`. 1. Run `adb install com.Icosa.OpenBrush.apk`. -### Enabling Multiplayer Photon Fusion +### Enabling Multiplayer Photon Fusion and Photon Voice -1. Download and install both [Photon Fusion 2.0.3 SDK](https://doc.photonengine.com/fusion/current/getting-started/sdk-download) and [Photon Voice 2](https://assetstore.unity.com/packages/tools/audio/photon-voice-2-130518?srsltid=AfmBOoqJifR_h-nIp73IL5F83GSvOmk3WqKytS1YsxrEUuZqVfAv5kQ9) Or alternatively Download [Photon Fusion 2.0.3 SDK + Photon Voice 2] (https://github.com/icosa-mirror/photon-fusion/releases/tag/Fusion_v2_Voice_2) +1. Download and install both [Photon Fusion 2.0.3 SDK](https://doc.photonengine.com/fusion/current/getting-started/sdk-download) and [Photon Voice 2](https://assetstore.unity.com/packages/tools/audio/photon-voice-2-130518?srsltid=AfmBOoqJifR_h-nIp73IL5F83GSvOmk3WqKytS1YsxrEUuZqVfAv5kQ9) +1. Alternatively Download [Photon Fusion 2.0.3 SDK + Photon Voice 2] (https://github.com/icosa-mirror/photon-fusion/releases/tag/Fusion_v2_Voice_2) copy it's content to the Assets Folder. 1. Restart Unity to trigger the creation of the Photon Fusion and Photon Voice specific defines. 1. In the **Standalone** and **Android** tabs of the Player settings, go to **Other Settings** > **Scripting Define Symbols**. 1. Click the + button to create a new entry. 1. Add `MP_PHOTON` and press **Apply**. 1. Follow the steps to [create your secrets file](#-Generating-Secrets-file). - Add 2 new items to the **Secrets** field. -1. Add these IDs to the `Secrets` file. Both `Photon Fusion` and `Photon Voice` should - have their own entries. + Add 2 new items to the **Secrets** field. Both `Photon Fusion` and `Photon Voice` should have their own entries. 1. Put the app IDs in the `Client ID` field for each. - ### Publishing to Oculus stores Note: _Tilt Brush_ is a Google trademark. If you intend to publish a cloned From 997dae2f0ad4e9bdc00bcacc7fd3706a53275a14 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Thu, 19 Dec 2024 09:38:04 +0000 Subject: [PATCH 161/174] Disable passthrough in multiplayer --- Assets/Scripts/GUI/LightingPopUpWindow.cs | 31 +++++++++++++++-------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/Assets/Scripts/GUI/LightingPopUpWindow.cs b/Assets/Scripts/GUI/LightingPopUpWindow.cs index 61be8830df..b05d5c9324 100644 --- a/Assets/Scripts/GUI/LightingPopUpWindow.cs +++ b/Assets/Scripts/GUI/LightingPopUpWindow.cs @@ -23,6 +23,7 @@ using UnityEngine; using System.Collections.Generic; using System.Linq; +using OpenBrush.Multiplayer; namespace TiltBrush { @@ -64,17 +65,11 @@ override public void Init(GameObject rParent, string sText) // Remove passthrough scene for devices that don't support it #if !PASSTHROUGH_SUPPORTED - foreach (var env in m_Environments) - { - // Passthrough - if (env.m_Guid.ToString() == PASSTHROUGH_GUID) - { - m_Environments.Remove(env); - break; - } - } -#endif // PASSTHROUGH_SUPPORTED - + RemovePassthrough(); +#else // PASSTHROUGH_SUPPORTED + // Remove passthrough if multiplayer connected + if (MultiplayerManager.m_Instance.State == ConnectionState.IN_ROOM) RemovePassthrough(); +#endif //find the active lighting preset TiltBrush.Environment rCurrentPreset = SceneSettings.m_Instance.GetDesiredPreset(); if (rCurrentPreset != null) @@ -106,6 +101,19 @@ override public void Init(GameObject rParent, string sText) base.Init(rParent, sText); } + public void RemovePassthrough() + { + foreach (var env in m_Environments) + { + // Passthrough + if (env.m_Guid.ToString() == PASSTHROUGH_GUID) + { + m_Environments.Remove(env); + break; + } + } + } + public void HandleCanvasLockToggle() { SketchControlsScript.m_Instance.m_DisableWorldGrabbing = m_WorldLockToggle.IsToggledOn; @@ -157,5 +165,6 @@ void OnDestroy() { SceneSettings.m_Instance.FadingToDesiredEnvironment -= OnFadingToDesiredEnvironment; } + } } // namespace TiltBrush From 38a2c8844fa029ce71334375830d7c33cedb99a8 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Thu, 19 Dec 2024 11:17:17 +0000 Subject: [PATCH 162/174] Runtime switching for experimental brushes --- Assets/Editor/BuildTiltBrush.cs | 4 +- Assets/Scenes/Main.unity | 2 +- Assets/Scripts/App.cs | 37 ++++++++------ Assets/Scripts/BrushCatalog.cs | 15 +++--- Assets/Scripts/Config.cs | 19 ++------ Assets/Scripts/EnvironmentCatalog.cs | 2 +- Assets/Scripts/GUI/AppSettingsPanel.cs | 3 +- Assets/Scripts/GUI/BrushTypeButton.cs | 2 +- Assets/Scripts/GUI/LocalePopUpWindow.cs | 2 +- Assets/Scripts/Rendering/IconTextureAtlas.cs | 2 +- Assets/Scripts/UserConfig.cs | 48 ++++++++----------- .../Localization/Strings/Strings_en.asset | 2 +- 12 files changed, 62 insertions(+), 76 deletions(-) diff --git a/Assets/Editor/BuildTiltBrush.cs b/Assets/Editor/BuildTiltBrush.cs index a8af77a93d..a63f6ed928 100644 --- a/Assets/Editor/BuildTiltBrush.cs +++ b/Assets/Editor/BuildTiltBrush.cs @@ -1435,7 +1435,7 @@ static void ShowBrushExportTextures() using (var unused = new TempHookUpSingletons()) { // Set consultUserConfig = false to keep user config from affecting the build output. - TiltBrushManifest manifest = App.Instance.GetMergedManifest(forceExperimental: true); + TiltBrushManifest manifest = App.Instance.ManifestFull; StringBuilder s = new StringBuilder(); foreach (BrushDescriptor desc in manifest.UniqueBrushes()) @@ -1547,7 +1547,7 @@ public static void DoBuild(TiltBuildOptions tiltOptions) // to be run at build-time (ie when nobody has called Start(), Awake()). // TempHookupSingletons() has done just enough initialization to make it happy. // Also set consultUserConfig = false to keep user config from affecting the build output. - TiltBrushManifest manifest = App.Instance.GetMergedManifest(forceExperimental: true); + TiltBrushManifest manifest = App.Instance.ManifestFull; // Some sanity checks { diff --git a/Assets/Scenes/Main.unity b/Assets/Scenes/Main.unity index 55c188aed8..0fad55d57e 100644 --- a/Assets/Scenes/Main.unity +++ b/Assets/Scenes/Main.unity @@ -10198,7 +10198,7 @@ MonoBehaviour: m_FadeFromBlackDuration: 3 m_QuickLoadHintDelay: 2 m_GpuIntersector: {fileID: 165291219} - m_Manifest: {fileID: 11400000, guid: 0be87170c871bfc4f91119634daa9c79, type: 2} + m_ManifestStandard: {fileID: 11400000, guid: 0be87170c871bfc4f91119634daa9c79, type: 2} m_ManifestExperimental: {fileID: 11400000, guid: 1121701af0c4d7145af70356f0ac2a83, type: 2} m_ZapboxManifest: {fileID: 11400000, guid: 7be45b23483e18347a0170a596bf2867, type: 2} diff --git a/Assets/Scripts/App.cs b/Assets/Scripts/App.cs index c26e70c583..d2bf9ba7bc 100644 --- a/Assets/Scripts/App.cs +++ b/Assets/Scripts/App.cs @@ -23,6 +23,7 @@ using UnityEngine; using Newtonsoft.Json; using TMPro; +using UnityEngine.Serialization; #if USD_SUPPORTED using Unity.Formats.USD; #endif @@ -191,11 +192,22 @@ public static void Log(string msg) [SerializeField] GpuIntersector m_GpuIntersector; - public TiltBrushManifest m_Manifest; - - // Previously Experimental-Mode only + [SerializeField] private TiltBrushManifest m_ManifestStandard; [SerializeField] private TiltBrushManifest m_ManifestExperimental; [SerializeField] private TiltBrushManifest m_ZapboxManifest; + private TiltBrushManifest m_ManifestFull; + + public TiltBrushManifest ManifestFull + { + get + { + if (m_ManifestFull == null) + { + m_ManifestFull = MergeManifests(); + } + return m_ManifestFull; + } + } [SerializeField] private SelectionEffect m_SelectionEffect; @@ -552,8 +564,6 @@ void Awake() gameObject.AddComponent(); } - m_Manifest = GetMergedManifest(); - m_HttpServer = GetComponentInChildren(); if (!Config.IsMobileHardware) { @@ -2201,19 +2211,16 @@ void OnPlaybackComplete() } } - public TiltBrushManifest GetMergedManifest(bool forceExperimental = false) + private TiltBrushManifest MergeManifests() { - var manifest = m_Manifest; - if (Config.IsExperimental || forceExperimental) +#if ZAPBOX_SUPPORTED + var manifest = m_ZapboxManifest; +#else + var manifest = Instantiate(m_ManifestStandard); + if (m_ManifestExperimental != null) { - if (m_ManifestExperimental != null) - { - manifest = Instantiate(m_Manifest); - manifest.AppendFrom(m_ManifestExperimental); - } + manifest.AppendFrom(m_ManifestExperimental); } -#if ZAPBOX_SUPPORTED - manifest = m_ZapboxManifest; #endif return manifest; } diff --git a/Assets/Scripts/BrushCatalog.cs b/Assets/Scripts/BrushCatalog.cs index 86a44c4a2e..13bdfe354a 100644 --- a/Assets/Scripts/BrushCatalog.cs +++ b/Assets/Scripts/BrushCatalog.cs @@ -189,19 +189,22 @@ public void BeginReload() m_GuiBrushList.Clear(); foreach (var brush in m_GuidToBrush.Values) { - if (brush.m_HiddenInGui) + // Some brushes are hardcoded as hidden + if (brush.m_HiddenInGui) continue; + // Always include if experimental mode is on + if (Config.IsExperimental || !App.Instance.IsBrushExperimental(brush)) { - continue; + m_GuiBrushList.Add(brush); } - m_GuiBrushList.Add(brush); } + BrushCatalogChanged?.Invoke(); } public Brush[] GetTagFilteredBrushList() { - List includeTags = App.UserConfig.Brushes.IncludeTags.ToList(); - List excludeTags = App.UserConfig.Brushes.ExcludeTags.ToList(); + List includeTags = App.UserConfig.Brushes.IncludeTags?.ToList(); + List excludeTags = App.UserConfig.Brushes.ExcludeTags?.ToList(); if (includeTags == null || includeTags.Count == 0) { @@ -288,7 +291,7 @@ Brush _FindBrushByDescription(string brushDescription) static private List LoadBrushesInManifest() { List output = new List(); - var manifest = App.Instance.m_Manifest; + var manifest = App.Instance.ManifestFull; foreach (var desc in manifest.Brushes) { if (desc != null) diff --git a/Assets/Scripts/Config.cs b/Assets/Scripts/Config.cs index efadeeb3db..8b63d49b81 100644 --- a/Assets/Scripts/Config.cs +++ b/Assets/Scripts/Config.cs @@ -113,9 +113,6 @@ private class UserConfigChange // The sdk mode indicates which SDK that we're using to drive the display. public SdkMode m_SdkMode; - // Stores the value of IsExperimental at startup time - [NonSerialized] public bool m_WasExperimentalAtStartup; - // Whether or not to just do an automatic profile and then exit. public bool m_AutoProfile; // How long to wait before starting to profile. @@ -126,7 +123,7 @@ private class UserConfigChange public string[] m_SketchFiles = new string[0]; [NonSerialized] public bool m_QuickLoad = true; - public SecretsConfig.ServiceAuthData GoogleSecrets => Secrets[SecretsConfig.Service.Google]; + public SecretsConfig.ServiceAuthData GoogleSecrets => Secrets?[SecretsConfig.Service.Google]; public SecretsConfig.ServiceAuthData SketchfabSecrets => Secrets[SecretsConfig.Service.Sketchfab]; public SecretsConfig.ServiceAuthData OculusSecrets => Secrets[SecretsConfig.Service.Oculus]; public SecretsConfig.ServiceAuthData OculusMobileSecrets => Secrets[SecretsConfig.Service.OculusMobile]; @@ -530,12 +527,6 @@ public bool GeometryShaderSuppported } } - // Non-Static version of above - public bool GetIsExperimental() - { - return PlayerPrefs.HasKey("ExperimentalMode") && PlayerPrefs.GetInt("ExperimentalMode") == 1; - } - public void SetIsExperimental(bool active) { PlayerPrefs.SetInt("ExperimentalMode", active ? 1 : 0); @@ -546,7 +537,6 @@ public void SetIsExperimental(bool active) void Awake() { m_SingletonState = this; - m_WasExperimentalAtStartup = GetIsExperimental(); #if UNITY_EDITOR if (!string.IsNullOrEmpty(m_FakeCommandLineArgsInEditor)) @@ -580,12 +570,9 @@ void Awake() #endif m_BrushReplacement = new Dictionary(); - if (IsExperimental) + foreach (var brush in m_BrushReplacementMap) { - foreach (var brush in m_BrushReplacementMap) - { - m_BrushReplacement.Add(new Guid(brush.FromGuid), new Guid(brush.ToGuid)); - } + m_BrushReplacement.Add(new Guid(brush.FromGuid), new Guid(brush.ToGuid)); } } diff --git a/Assets/Scripts/EnvironmentCatalog.cs b/Assets/Scripts/EnvironmentCatalog.cs index ffe69c8924..827a4d194a 100644 --- a/Assets/Scripts/EnvironmentCatalog.cs +++ b/Assets/Scripts/EnvironmentCatalog.cs @@ -107,7 +107,7 @@ void Update() static void LoadEnvironmentsInManifest(List output) { - var manifest = App.Instance.m_Manifest; + var manifest = App.Instance.ManifestFull; foreach (var asset in manifest.Environments) { if (asset != null) diff --git a/Assets/Scripts/GUI/AppSettingsPanel.cs b/Assets/Scripts/GUI/AppSettingsPanel.cs index ffe5450a9a..d26c00abcf 100644 --- a/Assets/Scripts/GUI/AppSettingsPanel.cs +++ b/Assets/Scripts/GUI/AppSettingsPanel.cs @@ -24,7 +24,7 @@ public class AppSettingsPanel : BasePanel public override void InitPanel() { base.InitPanel(); - m_ExperimentalModeToggle.IsToggledOn = App.Config.GetIsExperimental(); + m_ExperimentalModeToggle.IsToggledOn = Config.IsExperimental; } public void HandleToggleHandedness() @@ -43,7 +43,6 @@ public void HandleResetFirstUse() public void HandleToggleExperimentalMode(ToggleButton btn) { App.Config.SetIsExperimental(btn.IsToggledOn); - RestartNotification(); } private void RestartNotification() diff --git a/Assets/Scripts/GUI/BrushTypeButton.cs b/Assets/Scripts/GUI/BrushTypeButton.cs index 359b404a0e..37abc24f32 100644 --- a/Assets/Scripts/GUI/BrushTypeButton.cs +++ b/Assets/Scripts/GUI/BrushTypeButton.cs @@ -114,7 +114,7 @@ public void SetButtonProperties(BrushDescriptor rBrush) VisualizerManager.m_Instance.VisualsRequested); // Play standard click sound if brush doesn't have a custom button sound m_ButtonHasPressedAudio = (rBrush.m_ButtonAudio == null); - if (App.Config.m_WasExperimentalAtStartup) + if (Config.IsExperimental) { m_ExperimentalIcon.SetActive(App.Instance.IsBrushExperimental(rBrush)); } diff --git a/Assets/Scripts/GUI/LocalePopUpWindow.cs b/Assets/Scripts/GUI/LocalePopUpWindow.cs index f4344858cb..0a907810f1 100644 --- a/Assets/Scripts/GUI/LocalePopUpWindow.cs +++ b/Assets/Scripts/GUI/LocalePopUpWindow.cs @@ -47,7 +47,7 @@ override public void Init(GameObject rParent, string sText) //build list of locale presets we're going to show Locale currentSelectedLocale = LocalizationSettings.SelectedLocale; - m_Locales = App.Instance.m_Manifest.Locales; + m_Locales = App.Instance.ManifestFull.Locales; int iPresetIndex = -1; m_CurrentPresetIdCode = currentSelectedLocale.Identifier.Code; diff --git a/Assets/Scripts/Rendering/IconTextureAtlas.cs b/Assets/Scripts/Rendering/IconTextureAtlas.cs index f0fb2db86b..01520f088f 100644 --- a/Assets/Scripts/Rendering/IconTextureAtlas.cs +++ b/Assets/Scripts/Rendering/IconTextureAtlas.cs @@ -56,7 +56,7 @@ public Material GetAppropriateMaterial(bool activated, bool focus) void AtlasIconTextures() { // Load the appropriate catalog from Resources. - string catalogPath = App.Config.GetIsExperimental() ? m_ExperimentalCatalogPath : m_CatalogPath; + string catalogPath = m_ExperimentalCatalogPath; m_Catalog = Resources.Load(catalogPath); Debug.Assert(m_Catalog != null); diff --git a/Assets/Scripts/UserConfig.cs b/Assets/Scripts/UserConfig.cs index 313c82fdc6..1a811652b8 100644 --- a/Assets/Scripts/UserConfig.cs +++ b/Assets/Scripts/UserConfig.cs @@ -232,14 +232,7 @@ public string[] IncludeTags { if (m_IncludeTags == null) { - if (App.Config.GetIsExperimental()) - { - m_IncludeTags = new[] { "default", "experimental" }; - } - else - { - m_IncludeTags = new[] { "default" }; - } + m_IncludeTags = new[] { "default", "experimental" }; } return m_IncludeTags; } @@ -550,37 +543,34 @@ public Dictionary BrushReplacementMap get { Dictionary results = new Dictionary(); - if (Config.IsExperimental) + if (string.IsNullOrEmpty(BrushReplacements)) { - if (string.IsNullOrEmpty(BrushReplacements)) - { - return results; - } - var replacements = BrushReplacements.Split(','); - foreach (string replacement in replacements) + return results; + } + var replacements = BrushReplacements.Split(','); + foreach (string replacement in replacements) + { + string[] pair = replacement.Split('='); + if (pair.Length == 2) { - string[] pair = replacement.Split('='); - if (pair.Length == 2) + if (pair[0] == "*") { - if (pair[0] == "*") - { - Guid guid = new Guid(pair[1]); - foreach (var brush in App.Instance.m_Manifest.Brushes) - { - results.Add(brush.m_Guid, guid); - } - } - else + Guid guid = new Guid(pair[1]); + foreach (var brush in App.Instance.ManifestFull.Brushes) { - results.Add(new Guid(pair[0]), new Guid(pair[1])); + results.Add(brush.m_Guid, guid); } } else { - OutputWindowScript.Error("BrushReplacement should be of the form:\n" + - "brushguidA=brushguidB,brushguidC=brushguidD"); + results.Add(new Guid(pair[0]), new Guid(pair[1])); } } + else + { + OutputWindowScript.Error("BrushReplacement should be of the form:\n" + + "brushguidA=brushguidB,brushguidC=brushguidD"); + } } return results; } diff --git a/Assets/Settings/Localization/Strings/Strings_en.asset b/Assets/Settings/Localization/Strings/Strings_en.asset index 0c50b585da..d5841d6ce2 100644 --- a/Assets/Settings/Localization/Strings/Strings_en.asset +++ b/Assets/Settings/Localization/Strings/Strings_en.asset @@ -3081,7 +3081,7 @@ MonoBehaviour: m_Metadata: m_Items: [] - m_Id: 89093153698373632 - m_Localized: (Restart required) + m_Localized: Less compatibility when exported. See our docs. m_Metadata: m_Items: [] - m_Id: 89093320602312704 From c05a7b6e917e5641a54c1ed5187e49f5b5e2c742 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Thu, 19 Dec 2024 11:22:46 +0000 Subject: [PATCH 163/174] Photon does not join (room full) Realtime Voice joins anyway Photon does not join the room (room full) while Realtime Voice joins anyway --- Assets/Scripts/Multiplayer/Photon/PhotonManager.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs index c4af325129..63fa260340 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs @@ -153,6 +153,19 @@ public async Task JoinRoom(RoomCreateData roomCreateData) if (result.Ok) { + // Verify if the room is actually full + int currentPlayerCount = m_Runner.SessionInfo.PlayerCount; + int? maxPlayerCount = m_Runner.SessionInfo.MaxPlayers; + maxPlayerCount = maxPlayerCount == null ? int.MaxValue : maxPlayerCount; + + if (currentPlayerCount >= maxPlayerCount) + { + State = ConnectionState.ERROR; + LastError = "[PhotonManager] Room is full."; + ControllerConsoleScript.m_Instance.AddNewLine(LastError); + return false; + } + State = ConnectionState.IN_ROOM; ControllerConsoleScript.m_Instance.AddNewLine("[PhotonManager] Joined Room"); UserInfo = new ConnectionUserInfo { From d20bc4c92adbd8fc639712e2d0d961eb0c15c88a Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Thu, 19 Dec 2024 11:32:43 +0000 Subject: [PATCH 164/174] Localization fix --- Assets/Scripts/GUI/MultiplayerPanel.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Assets/Scripts/GUI/MultiplayerPanel.cs b/Assets/Scripts/GUI/MultiplayerPanel.cs index bfe03ed2bf..a0a92157f5 100644 --- a/Assets/Scripts/GUI/MultiplayerPanel.cs +++ b/Assets/Scripts/GUI/MultiplayerPanel.cs @@ -18,6 +18,7 @@ using TMPro; using UnityEngine; using UnityEngine.Localization; +using UnityEngine.Localization.Settings; namespace TiltBrush { @@ -39,6 +40,7 @@ public class MultiplayerPanel : BasePanel private PlayerPrefsDataStore m_multiplayer; + private bool updateDisplay = false; public string RoomName { @@ -99,6 +101,13 @@ public void Awake() MultiplayerManager.m_Instance.RoomOwnershipUpdated += OnRoomOwnershipUpdated; } + LocalizationSettings.SelectedLocaleChanged += OnLanguageChanged; + + } + + private void OnLanguageChanged(Locale newLocale) + { + updateDisplay = true; } public async void RetrieveUsername() @@ -125,6 +134,8 @@ protected override void OnEnablePanel() { MultiplayerManager.m_Instance.Connect(); } + + if (updateDisplay) UpdateDisplay(); } protected override void OnDisablePanel() @@ -171,6 +182,7 @@ private void UpdateDisplay() if (m_RoomNumber) m_RoomNumber.text = m_RoomNumberString.GetLocalizedString() + data.roomName; if (m_Nickname) m_Nickname.text = m_NicknameString.GetLocalizedString() + NickName; Alerts(); + updateDisplay = false; } private async void Connect() From f6bbc948b9094cc70e35d0551a752643760fbb29 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Thu, 19 Dec 2024 12:06:14 +0000 Subject: [PATCH 165/174] Icon experimental badges need to update all the time --- Assets/Scripts/GUI/BrushTypeButton.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Assets/Scripts/GUI/BrushTypeButton.cs b/Assets/Scripts/GUI/BrushTypeButton.cs index 37abc24f32..dd63934f8b 100644 --- a/Assets/Scripts/GUI/BrushTypeButton.cs +++ b/Assets/Scripts/GUI/BrushTypeButton.cs @@ -114,10 +114,7 @@ public void SetButtonProperties(BrushDescriptor rBrush) VisualizerManager.m_Instance.VisualsRequested); // Play standard click sound if brush doesn't have a custom button sound m_ButtonHasPressedAudio = (rBrush.m_ButtonAudio == null); - if (Config.IsExperimental) - { - m_ExperimentalIcon.SetActive(App.Instance.IsBrushExperimental(rBrush)); - } + m_ExperimentalIcon.SetActive(App.Instance.IsBrushExperimental(rBrush)); } override protected void OnDescriptionActivated() From 7b9f6a1ca069f90ce49ac6a778f3d03c79c3bad2 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Thu, 19 Dec 2024 14:29:01 +0000 Subject: [PATCH 166/174] Add alert to check if passthrough environment is active --- Assets/Prefabs/Panels/MultiplayerPanel.prefab | 9 +++++++++ Assets/Scripts/GUI/MultiplayerPanel.cs | 18 +++++++++++++++++- .../Multiplayer/Photon/PhotonManager.cs | 2 +- .../Strings/Strings Shared Data.asset | 6 +++++- .../Localization/Strings/Strings_de.asset | 5 +++++ .../Localization/Strings/Strings_en.asset | 2 +- .../Localization/Strings/Strings_es.asset | 4 ++++ .../Localization/Strings/Strings_fr.asset | 5 +++++ .../Localization/Strings/Strings_ja.asset | 4 ++++ .../Localization/Strings/Strings_ko.asset | 5 +++++ .../Localization/Strings/Strings_zh.asset | 4 ++++ 11 files changed, 60 insertions(+), 4 deletions(-) diff --git a/Assets/Prefabs/Panels/MultiplayerPanel.prefab b/Assets/Prefabs/Panels/MultiplayerPanel.prefab index be876a0284..8757271258 100644 --- a/Assets/Prefabs/Panels/MultiplayerPanel.prefab +++ b/Assets/Prefabs/Panels/MultiplayerPanel.prefab @@ -179,6 +179,15 @@ MonoBehaviour: m_FallbackState: 0 m_WaitForCompletion: 0 m_LocalVariables: [] + m_AlertsPassThroughAcive: + m_TableReference: + m_TableCollectionName: GUID:c84355079ab3f3e4f8f3812258805f86 + m_TableEntryReference: + m_KeyId: 156198200358006784 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] references: version: 2 RefIds: [] diff --git a/Assets/Scripts/GUI/MultiplayerPanel.cs b/Assets/Scripts/GUI/MultiplayerPanel.cs index a0a92157f5..2a0d8ec81b 100644 --- a/Assets/Scripts/GUI/MultiplayerPanel.cs +++ b/Assets/Scripts/GUI/MultiplayerPanel.cs @@ -37,7 +37,7 @@ public class MultiplayerPanel : BasePanel [SerializeField] private TextMeshPro m_AlertsErrors; [SerializeField] private LocalizedString m_AlertsErrorBeginnerModeActive; [SerializeField] private LocalizedString m_AlertsRoomAlreadyExistent; - + [SerializeField] private LocalizedString m_AlertsPassThroughAcive; private PlayerPrefsDataStore m_multiplayer; private bool updateDisplay = false; @@ -91,6 +91,7 @@ public void Awake() alertChecks = new List>> { CheckAdvancedModeActive, + CheckIfPassThroughEnvironment, CheckMultiplayerManagerErrors, CheckIfRoomExist, }; @@ -297,6 +298,21 @@ private Tuple CheckIfRoomExist() } + // hardoded copied from LightingPopUpWindow + private const string PASSTHROUGH_GUID = "e38af599-4575-46ff-a040-459703dbcd36"; + private Tuple CheckIfPassThroughEnvironment() + { + + if (MultiplayerManager.m_Instance != null && MultiplayerManager.m_Instance.State == ConnectionState.IN_LOBBY) + { + TiltBrush.Environment targetEnvironment = SceneSettings.m_Instance.GetDesiredPreset(); + if (targetEnvironment.m_Guid.ToString() == PASSTHROUGH_GUID) + return Tuple.Create(true, m_AlertsPassThroughAcive.GetLocalizedString()); + } + + return Tuple.Create(false, ""); + } + private void Alerts() { if (m_AlertsErrors) diff --git a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs index 63fa260340..dbcea03c33 100644 --- a/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs +++ b/Assets/Scripts/Multiplayer/Photon/PhotonManager.cs @@ -150,7 +150,6 @@ public async Task JoinRoom(RoomCreateData roomCreateData) //m_Runner.ReliableDataSendRate = 60; //m_Runner.Config.Network.ReliableDataTransferModes = NetworkConfiguration.ReliableDataTransfers.ClientToClientWithServerProxy; - if (result.Ok) { // Verify if the room is actually full @@ -163,6 +162,7 @@ public async Task JoinRoom(RoomCreateData roomCreateData) State = ConnectionState.ERROR; LastError = "[PhotonManager] Room is full."; ControllerConsoleScript.m_Instance.AddNewLine(LastError); + Disconnect(); return false; } diff --git a/Assets/Settings/Localization/Strings/Strings Shared Data.asset b/Assets/Settings/Localization/Strings/Strings Shared Data.asset index 71c4d8d15b..19d6b358a2 100644 --- a/Assets/Settings/Localization/Strings/Strings Shared Data.asset +++ b/Assets/Settings/Localization/Strings/Strings Shared Data.asset @@ -3300,7 +3300,7 @@ MonoBehaviour: m_Metadata: m_Items: [] - m_Id: 156198200358006784 - m_Key: ADMIN_PANEL_MULTIPLAYER_BUTTON_DESCRIPTION + m_Key: MULTIPLAYER_PANEL_ALERT_PASSTHROUGH_ACTIVE m_Metadata: m_Items: [] - m_Id: 157582826200739840 @@ -3427,6 +3427,10 @@ MonoBehaviour: m_Key: MULTIPLAYER_PANEL_ALERT_ROOM_EXIST m_Metadata: m_Items: [] + - m_Id: 313081115495178240 + m_Key: MULTIPLAYER_PANEL_ALERT_PASSTHROUGH_EXIST + m_Metadata: + m_Items: [] m_Metadata: m_Items: [] m_KeyGenerator: diff --git a/Assets/Settings/Localization/Strings/Strings_de.asset b/Assets/Settings/Localization/Strings/Strings_de.asset index 21b1c28880..aa9610940f 100644 --- a/Assets/Settings/Localization/Strings/Strings_de.asset +++ b/Assets/Settings/Localization/Strings/Strings_de.asset @@ -3629,6 +3629,11 @@ MonoBehaviour: beitreten. m_Metadata: m_Items: [] + - m_Id: 156198200358006784 + m_Localized: Schalten Sie den Passthrough-Modus aus, um den Mehrspielermodus + zu nutzen. + m_Metadata: + m_Items: [] references: version: 2 RefIds: [] diff --git a/Assets/Settings/Localization/Strings/Strings_en.asset b/Assets/Settings/Localization/Strings/Strings_en.asset index 3ac06298b7..a6d099e885 100644 --- a/Assets/Settings/Localization/Strings/Strings_en.asset +++ b/Assets/Settings/Localization/Strings/Strings_en.asset @@ -3521,7 +3521,7 @@ MonoBehaviour: m_Metadata: m_Items: [] - m_Id: 156198200358006784 - m_Localized: Multiplayer (Alpha) + m_Localized: Switch off passthrough environment mode to use Multiplayer m_Metadata: m_Items: [] - m_Id: 238556149774557184 diff --git a/Assets/Settings/Localization/Strings/Strings_es.asset b/Assets/Settings/Localization/Strings/Strings_es.asset index 9b5de33b4f..5c8126a0a2 100644 --- a/Assets/Settings/Localization/Strings/Strings_es.asset +++ b/Assets/Settings/Localization/Strings/Strings_es.asset @@ -3600,6 +3600,10 @@ MonoBehaviour: m_Localized: "La sala ya existe. Te unir\xE1s a una sesi\xF3n existente." m_Metadata: m_Items: [] + - m_Id: 156198200358006784 + m_Localized: Desactiva el modo de entorno de passthrough para usar el multijugador. + m_Metadata: + m_Items: [] references: version: 2 RefIds: [] diff --git a/Assets/Settings/Localization/Strings/Strings_fr.asset b/Assets/Settings/Localization/Strings/Strings_fr.asset index 8b7b0e83a2..251fb8c8dc 100644 --- a/Assets/Settings/Localization/Strings/Strings_fr.asset +++ b/Assets/Settings/Localization/Strings/Strings_fr.asset @@ -3569,6 +3569,11 @@ MonoBehaviour: m_Localized: "La salle existe d\xE9j\xE0. Vous allez rejoindre une session existante." m_Metadata: m_Items: [] + - m_Id: 156198200358006784 + m_Localized: "D\xE9sactivez le mode environnement de transparence pour utiliser + le multijoueur." + m_Metadata: + m_Items: [] references: version: 2 RefIds: [] diff --git a/Assets/Settings/Localization/Strings/Strings_ja.asset b/Assets/Settings/Localization/Strings/Strings_ja.asset index 55af60b17b..6d61846432 100644 --- a/Assets/Settings/Localization/Strings/Strings_ja.asset +++ b/Assets/Settings/Localization/Strings/Strings_ja.asset @@ -3522,6 +3522,10 @@ MonoBehaviour: m_Localized: "\u30EB\u30FC\u30E0\u306F\u65E2\u306B\u5B58\u5728\u3057\u307E\u3059\u3002\u65E2\u5B58\u306E\u30BB\u30C3\u30B7\u30E7\u30F3\u306B\u53C2\u52A0\u3057\u307E\u3059\u3002" m_Metadata: m_Items: [] + - m_Id: 156198200358006784 + m_Localized: "\u30DE\u30EB\u30C1\u30D7\u30EC\u30A4\u30E4\u30FC\u3092\u4F7F\u7528\u3059\u308B\u306B\u306F\u3001\u900F\u904E\u74B0\u5883\u30E2\u30FC\u30C9\u3092\u30AA\u30D5\u306B\u3057\u3066\u304F\u3060\u3055\u3044\u3002" + m_Metadata: + m_Items: [] references: version: 2 RefIds: [] diff --git a/Assets/Settings/Localization/Strings/Strings_ko.asset b/Assets/Settings/Localization/Strings/Strings_ko.asset index 74fbf82d2e..d363f4ed09 100644 --- a/Assets/Settings/Localization/Strings/Strings_ko.asset +++ b/Assets/Settings/Localization/Strings/Strings_ko.asset @@ -3590,6 +3590,11 @@ MonoBehaviour: \uC138\uC158\uC5D0 \uCC38\uC5EC\uD569\uB2C8\uB2E4." m_Metadata: m_Items: [] + - m_Id: 156198200358006784 + m_Localized: "\uBA40\uD2F0\uD50C\uB808\uC774\uC5B4\uB97C \uC0AC\uC6A9\uD558\uB824\uBA74 + \uD328\uC2A4\uC2A4\uB8E8 \uD658\uACBD \uBAA8\uB4DC\uB97C \uB044\uC138\uC694." + m_Metadata: + m_Items: [] references: version: 2 RefIds: [] diff --git a/Assets/Settings/Localization/Strings/Strings_zh.asset b/Assets/Settings/Localization/Strings/Strings_zh.asset index adc6b43c9c..64c144843d 100644 --- a/Assets/Settings/Localization/Strings/Strings_zh.asset +++ b/Assets/Settings/Localization/Strings/Strings_zh.asset @@ -3518,6 +3518,10 @@ MonoBehaviour: m_Localized: "\u623F\u95F4\u5DF2\u5B58\u5728\u3002\u60A8\u5C06\u52A0\u5165\u4E00\u4E2A\u73B0\u6709\u7684\u4F1A\u8BDD\u3002" m_Metadata: m_Items: [] + - m_Id: 156198200358006784 + m_Localized: "\u5173\u95ED\u900F\u89C6\u73AF\u5883\u6A21\u5F0F\u4EE5\u4F7F\u7528\u591A\u4EBA\u6E38\u620F\u3002" + m_Metadata: + m_Items: [] references: version: 2 RefIds: [] From 4712b7923aec1215afcd94a7e6440a62058bc2ae Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Thu, 19 Dec 2024 15:10:34 +0000 Subject: [PATCH 167/174] Remove hardcoded passthrough GUID Replaced PASSTHROUGH_GUID = "e38af599-4575-46ff-a040-459703dbcd36"; with a new property isPassthrough in the environment scriptable object. --- .../Environments/Passthrough/Passthrough.asset | 2 +- Assets/Scripts/Environment.cs | 1 + Assets/Scripts/GUI/LightingPopUpWindow.cs | 15 ++++++++------- Assets/Scripts/GUI/MultiplayerPanel.cs | 4 +--- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Assets/Resources/Environments/Passthrough/Passthrough.asset b/Assets/Resources/Environments/Passthrough/Passthrough.asset index 3e70c8f39b..d44d6a7ace 100644 --- a/Assets/Resources/Environments/Passthrough/Passthrough.asset +++ b/Assets/Resources/Environments/Passthrough/Passthrough.asset @@ -14,7 +14,6 @@ MonoBehaviour: m_EditorClassIdentifier: m_Guid: m_storage: e38af599-4575-46ff-a040-459703dbcd36 - m_Description: ENVIRONMENT_PASSTHROUGH m_EnvironmentDescription: m_TableReference: m_TableCollectionName: GUID:c84355079ab3f3e4f8f3812258805f86 @@ -63,6 +62,7 @@ MonoBehaviour: m_WidgetHome: {x: 0, y: 0, z: 0} m_SkyboxColorA: {r: 0, g: 0, b: 0, a: 0} m_SkyboxColorB: {r: 0, g: 0, b: 0, a: 0} + isPassthrough: 1 references: version: 2 RefIds: [] diff --git a/Assets/Scripts/Environment.cs b/Assets/Scripts/Environment.cs index fae21811ef..5b57756971 100644 --- a/Assets/Scripts/Environment.cs +++ b/Assets/Scripts/Environment.cs @@ -175,6 +175,7 @@ public Material m_SkyboxMaterial public Color m_SkyboxColorA; public Color m_SkyboxColorB; + public bool isPassthrough; } } // namespace TiltBrush diff --git a/Assets/Scripts/GUI/LightingPopUpWindow.cs b/Assets/Scripts/GUI/LightingPopUpWindow.cs index b05d5c9324..28abbc2d4f 100644 --- a/Assets/Scripts/GUI/LightingPopUpWindow.cs +++ b/Assets/Scripts/GUI/LightingPopUpWindow.cs @@ -30,7 +30,6 @@ namespace TiltBrush public class LightingPopUpWindow : PagingPopUpWindow { - private const string PASSTHROUGH_GUID = "e38af599-4575-46ff-a040-459703dbcd36"; private string m_CurrentPresetGuid; [SerializeField] private Transform m_PassthroughControls; @@ -105,8 +104,7 @@ public void RemovePassthrough() { foreach (var env in m_Environments) { - // Passthrough - if (env.m_Guid.ToString() == PASSTHROUGH_GUID) + if (env.isPassthrough) { m_Environments.Remove(env); break; @@ -127,8 +125,7 @@ public void HandleCanvasReset(ActionButton btn) override protected void RefreshPage() { base.RefreshPage(); - bool passthroughActive = m_CurrentPresetGuid == PASSTHROUGH_GUID; - if (passthroughActive) + if (isPassThroughActive()) { m_PassthroughControls.gameObject.SetActive(true); } @@ -144,8 +141,7 @@ protected void OnFadingToDesiredEnvironment() if (rCurrentPreset != null) { m_CurrentPresetGuid = rCurrentPreset.m_Guid.ToString(); - bool passthroughActive = m_CurrentPresetGuid == PASSTHROUGH_GUID; - if (passthroughActive) + if (isPassThroughActive()) { m_PassthroughControls.gameObject.SetActive(true); m_WorldLockToggle.IsToggledOn = true; @@ -161,6 +157,11 @@ protected void OnFadingToDesiredEnvironment() RefreshPage(); } + private bool isPassThroughActive() + { + return m_Environments.Any(env => env.isPassthrough && env.m_Guid.ToString() == m_CurrentPresetGuid); + } + void OnDestroy() { SceneSettings.m_Instance.FadingToDesiredEnvironment -= OnFadingToDesiredEnvironment; diff --git a/Assets/Scripts/GUI/MultiplayerPanel.cs b/Assets/Scripts/GUI/MultiplayerPanel.cs index 2a0d8ec81b..6c22ce4ca2 100644 --- a/Assets/Scripts/GUI/MultiplayerPanel.cs +++ b/Assets/Scripts/GUI/MultiplayerPanel.cs @@ -298,15 +298,13 @@ private Tuple CheckIfRoomExist() } - // hardoded copied from LightingPopUpWindow - private const string PASSTHROUGH_GUID = "e38af599-4575-46ff-a040-459703dbcd36"; private Tuple CheckIfPassThroughEnvironment() { if (MultiplayerManager.m_Instance != null && MultiplayerManager.m_Instance.State == ConnectionState.IN_LOBBY) { TiltBrush.Environment targetEnvironment = SceneSettings.m_Instance.GetDesiredPreset(); - if (targetEnvironment.m_Guid.ToString() == PASSTHROUGH_GUID) + if (targetEnvironment.isPassthrough) return Tuple.Create(true, m_AlertsPassThroughAcive.GetLocalizedString()); } From 0967bd5b0c69bbc7bade26d711a814d81a5bbe58 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Thu, 19 Dec 2024 16:29:03 +0000 Subject: [PATCH 168/174] Multiplayer admin menu string fix --- Assets/Prefabs/Panels/AdminPanel.prefab | 2 +- .../Localization/Strings/Strings Shared Data.asset | 8 ++++++++ Assets/Settings/Localization/Strings/Strings_de.asset | 4 ++++ Assets/Settings/Localization/Strings/Strings_en.asset | 4 ++++ Assets/Settings/Localization/Strings/Strings_es.asset | 4 ++++ Assets/Settings/Localization/Strings/Strings_fr.asset | 4 ++++ Assets/Settings/Localization/Strings/Strings_ja.asset | 4 ++++ Assets/Settings/Localization/Strings/Strings_ko.asset | 4 ++++ Assets/Settings/Localization/Strings/Strings_zh.asset | 4 ++++ 9 files changed, 37 insertions(+), 1 deletion(-) diff --git a/Assets/Prefabs/Panels/AdminPanel.prefab b/Assets/Prefabs/Panels/AdminPanel.prefab index 9ef1cc81b7..bb9232afe2 100644 --- a/Assets/Prefabs/Panels/AdminPanel.prefab +++ b/Assets/Prefabs/Panels/AdminPanel.prefab @@ -3523,7 +3523,7 @@ MonoBehaviour: m_TableReference: m_TableCollectionName: GUID:c84355079ab3f3e4f8f3812258805f86 m_TableEntryReference: - m_KeyId: 156198200358006784 + m_KeyId: 313111533204348928 m_Key: m_FallbackState: 0 m_WaitForCompletion: 0 diff --git a/Assets/Settings/Localization/Strings/Strings Shared Data.asset b/Assets/Settings/Localization/Strings/Strings Shared Data.asset index 19d6b358a2..5bc5787fd3 100644 --- a/Assets/Settings/Localization/Strings/Strings Shared Data.asset +++ b/Assets/Settings/Localization/Strings/Strings Shared Data.asset @@ -3431,6 +3431,14 @@ MonoBehaviour: m_Key: MULTIPLAYER_PANEL_ALERT_PASSTHROUGH_EXIST m_Metadata: m_Items: [] + - m_Id: 313111533204348928 + m_Key: ADMIN_PANEL_MULTIPLAYER_BUTTON_DESCRIPTION + m_Metadata: + m_Items: [] + - m_Id: 313111642914758656 + m_Key: ADMIN_PANEL_MULTIPLAYER_DESCRIPTION + m_Metadata: + m_Items: [] m_Metadata: m_Items: [] m_KeyGenerator: diff --git a/Assets/Settings/Localization/Strings/Strings_de.asset b/Assets/Settings/Localization/Strings/Strings_de.asset index aa9610940f..acda5ad1d3 100644 --- a/Assets/Settings/Localization/Strings/Strings_de.asset +++ b/Assets/Settings/Localization/Strings/Strings_de.asset @@ -3634,6 +3634,10 @@ MonoBehaviour: zu nutzen. m_Metadata: m_Items: [] + - m_Id: 313111533204348928 + m_Localized: "Mehrspieler-Men\xFC" + m_Metadata: + m_Items: [] references: version: 2 RefIds: [] diff --git a/Assets/Settings/Localization/Strings/Strings_en.asset b/Assets/Settings/Localization/Strings/Strings_en.asset index a6d099e885..41b78f32b2 100644 --- a/Assets/Settings/Localization/Strings/Strings_en.asset +++ b/Assets/Settings/Localization/Strings/Strings_en.asset @@ -3604,6 +3604,10 @@ MonoBehaviour: m_Localized: Room already exists. You will be joining an existing session. m_Metadata: m_Items: [] + - m_Id: 313111533204348928 + m_Localized: 'Multiplayer Menu ' + m_Metadata: + m_Items: [] references: version: 2 RefIds: [] diff --git a/Assets/Settings/Localization/Strings/Strings_es.asset b/Assets/Settings/Localization/Strings/Strings_es.asset index 5c8126a0a2..6af4360f3d 100644 --- a/Assets/Settings/Localization/Strings/Strings_es.asset +++ b/Assets/Settings/Localization/Strings/Strings_es.asset @@ -3604,6 +3604,10 @@ MonoBehaviour: m_Localized: Desactiva el modo de entorno de passthrough para usar el multijugador. m_Metadata: m_Items: [] + - m_Id: 313111533204348928 + m_Localized: "Men\xFA Multijugador" + m_Metadata: + m_Items: [] references: version: 2 RefIds: [] diff --git a/Assets/Settings/Localization/Strings/Strings_fr.asset b/Assets/Settings/Localization/Strings/Strings_fr.asset index 251fb8c8dc..528dd733e2 100644 --- a/Assets/Settings/Localization/Strings/Strings_fr.asset +++ b/Assets/Settings/Localization/Strings/Strings_fr.asset @@ -3574,6 +3574,10 @@ MonoBehaviour: le multijoueur." m_Metadata: m_Items: [] + - m_Id: 313111533204348928 + m_Localized: Menu Multijoueur + m_Metadata: + m_Items: [] references: version: 2 RefIds: [] diff --git a/Assets/Settings/Localization/Strings/Strings_ja.asset b/Assets/Settings/Localization/Strings/Strings_ja.asset index 6d61846432..4379c4df58 100644 --- a/Assets/Settings/Localization/Strings/Strings_ja.asset +++ b/Assets/Settings/Localization/Strings/Strings_ja.asset @@ -3526,6 +3526,10 @@ MonoBehaviour: m_Localized: "\u30DE\u30EB\u30C1\u30D7\u30EC\u30A4\u30E4\u30FC\u3092\u4F7F\u7528\u3059\u308B\u306B\u306F\u3001\u900F\u904E\u74B0\u5883\u30E2\u30FC\u30C9\u3092\u30AA\u30D5\u306B\u3057\u3066\u304F\u3060\u3055\u3044\u3002" m_Metadata: m_Items: [] + - m_Id: 313111533204348928 + m_Localized: "\u30DE\u30EB\u30C1\u30D7\u30EC\u30A4\u30E4\u30FC\u30E1\u30CB\u30E5\u30FC" + m_Metadata: + m_Items: [] references: version: 2 RefIds: [] diff --git a/Assets/Settings/Localization/Strings/Strings_ko.asset b/Assets/Settings/Localization/Strings/Strings_ko.asset index d363f4ed09..47251b9d18 100644 --- a/Assets/Settings/Localization/Strings/Strings_ko.asset +++ b/Assets/Settings/Localization/Strings/Strings_ko.asset @@ -3595,6 +3595,10 @@ MonoBehaviour: \uD328\uC2A4\uC2A4\uB8E8 \uD658\uACBD \uBAA8\uB4DC\uB97C \uB044\uC138\uC694." m_Metadata: m_Items: [] + - m_Id: 313111533204348928 + m_Localized: "\uBA40\uD2F0\uD50C\uB808\uC774 \uBA54\uB274" + m_Metadata: + m_Items: [] references: version: 2 RefIds: [] diff --git a/Assets/Settings/Localization/Strings/Strings_zh.asset b/Assets/Settings/Localization/Strings/Strings_zh.asset index 64c144843d..578696a8c6 100644 --- a/Assets/Settings/Localization/Strings/Strings_zh.asset +++ b/Assets/Settings/Localization/Strings/Strings_zh.asset @@ -3522,6 +3522,10 @@ MonoBehaviour: m_Localized: "\u5173\u95ED\u900F\u89C6\u73AF\u5883\u6A21\u5F0F\u4EE5\u4F7F\u7528\u591A\u4EBA\u6E38\u620F\u3002" m_Metadata: m_Items: [] + - m_Id: 313111533204348928 + m_Localized: "\u591A\u4EBA\u83DC\u5355" + m_Metadata: + m_Items: [] references: version: 2 RefIds: [] From a3185a036b81db8565d0e8a925519487fa606825 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Thu, 19 Dec 2024 16:32:37 +0000 Subject: [PATCH 169/174] Disable Join Button if in PassThrough mode --- Assets/Scripts/SketchControlsScript.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Assets/Scripts/SketchControlsScript.cs b/Assets/Scripts/SketchControlsScript.cs index 9f25b03344..8bfb01b557 100644 --- a/Assets/Scripts/SketchControlsScript.cs +++ b/Assets/Scripts/SketchControlsScript.cs @@ -5097,7 +5097,7 @@ public bool IsCommandAvailable(GlobalCommands rEnum, int iParam = -1) case GlobalCommands.MultiplayerDisconnect: return MultiplayerManager.m_Instance.IsDisconnectable(); case GlobalCommands.MultiplayerJoinRoom: - return !PanelManager.m_Instance.AdvancedModeActive() && MultiplayerManager.m_Instance.CanJoinRoom(); + return !PanelManager.m_Instance.AdvancedModeActive() && MultiplayerManager.m_Instance.CanJoinRoom() && !SceneSettings.m_Instance.GetDesiredPreset().isPassthrough; case GlobalCommands.MultiplayerLeaveRoom: return MultiplayerManager.m_Instance.CanLeaveRoom(); case GlobalCommands.Sketchbook: From de95baa8055efa0a720d045c562216844111e5e6 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Fri, 20 Dec 2024 09:49:11 +0000 Subject: [PATCH 170/174] PhotonPlayerRig -> TextMeshPro -> autosize Nickname to fit in the headset --- .../Multiplayer/Photon/PhotonPlayerRig.prefab | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Assets/Resources/Multiplayer/Photon/PhotonPlayerRig.prefab b/Assets/Resources/Multiplayer/Photon/PhotonPlayerRig.prefab index 4b507b3ed6..9c60844319 100644 --- a/Assets/Resources/Multiplayer/Photon/PhotonPlayerRig.prefab +++ b/Assets/Resources/Multiplayer/Photon/PhotonPlayerRig.prefab @@ -2600,11 +2600,11 @@ MonoBehaviour: m_faceColor: serializedVersion: 2 rgba: 4294967295 - m_fontSize: 36 + m_fontSize: 36.75 m_fontSizeBase: 36 m_fontWeight: 400 - m_enableAutoSizing: 0 - m_fontSizeMin: 18 + m_enableAutoSizing: 1 + m_fontSizeMin: 24 m_fontSizeMax: 72 m_fontStyle: 0 m_HorizontalAlignment: 2 @@ -2613,12 +2613,12 @@ MonoBehaviour: m_characterSpacing: 0 m_wordSpacing: 0 m_lineSpacing: 0 - m_lineSpacingMax: 0 + m_lineSpacingMax: -3.07 m_paragraphSpacing: 0 m_charWidthMaxAdj: 0 m_enableWordWrapping: 1 m_wordWrappingRatios: 0.4 - m_overflowMode: 0 + m_overflowMode: 1 m_linkedTextComponent: {fileID: 0} parentLinkedComponent: {fileID: 0} m_enableKerning: 1 From 491519d190ea8a3cfd1b83a3c6e92c9307f6a327 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Fri, 20 Dec 2024 09:57:11 +0000 Subject: [PATCH 171/174] UnnamedUser -> Unnamed --- Assets/Scripts/GUI/MultiplayerPanel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Assets/Scripts/GUI/MultiplayerPanel.cs b/Assets/Scripts/GUI/MultiplayerPanel.cs index 6c22ce4ca2..6e70a0d5c5 100644 --- a/Assets/Scripts/GUI/MultiplayerPanel.cs +++ b/Assets/Scripts/GUI/MultiplayerPanel.cs @@ -114,7 +114,7 @@ private void OnLanguageChanged(Locale newLocale) public async void RetrieveUsername() { var storedNickname = await m_multiplayer.GetAsync("nickname"); - NickName = storedNickname ?? "UnnamedUser"; + NickName = storedNickname ?? "Unnamed"; } From 98fd79f19f8431e88e0911b9a5f9402266d8a527 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Fri, 20 Dec 2024 10:45:38 +0000 Subject: [PATCH 172/174] Sync reaching 100% and complete --- .../Multiplayer/MultiplayerSceneSync.cs | 27 ++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/Assets/Scripts/Multiplayer/MultiplayerSceneSync.cs b/Assets/Scripts/Multiplayer/MultiplayerSceneSync.cs index f6215b69ba..e4a9c4ea7b 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerSceneSync.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerSceneSync.cs @@ -55,8 +55,6 @@ void OnDestroy() public void StartSyncronizationForUser(int id) { - StartSynchHistory(id); - SendCurrentTargetEnvironmentCommand(); // TODO serialize the environmentand send it via large reliable data switch (m_SyncType) { @@ -74,6 +72,10 @@ public void StartSyncronizationForUser(int id) async void SendStrokesToPlayer(int id) { LinkedList strokes = SketchMemoryScript.m_Instance.GetMemoryList; + + if (strokes.Count == 0) return; + + SendCurrentTargetEnvironmentCommand(); StartSyncProgressDisplayForSrokes(id, strokes); const int chunkSize = 5; List strokeList = strokes.ToList(); @@ -152,6 +154,10 @@ public IEnumerator SendCommandHistory(int id) CreateBrushStrokeCommands(strokesWithoutCommand, firstCommandTimestamp); // this add the strokes without commands to the IEnumerable commands + if (commands.Count() == 0) yield break; + + SendCurrentTargetEnvironmentCommand(); + StartSyncProgressDisplayForCommands(id, commands.ToList()); foreach (BaseCommand command in commands) MultiplayerManager.m_Instance.OnCommandPerformed(command); @@ -197,12 +203,27 @@ public async void StartSyncProgressDisplayForSrokes(int TargetPlayerId, LinkedLi StartSynchHistory(TargetPlayerId); int sentStrokes = 0; + foreach (var stroke in strokes) { - while (await MultiplayerManager.m_Instance.CheckStrokeReception(stroke, TargetPlayerId)) + int retryCount = 0; + const int maxRetries = 3; + bool acknowledged = false; + + while (retryCount < maxRetries) { + if (await MultiplayerManager.m_Instance.CheckStrokeReception(stroke, TargetPlayerId)) + { + acknowledged = true; + break; + } + retryCount++; await Task.Delay(200); } + + if (!acknowledged) + ControllerConsoleScript.m_Instance.AddNewLine($"Stroke {stroke.m_Guid} failed to synchronize after {maxRetries} retries."); + sentStrokes++; SynchHistoryPercentage(TargetPlayerId, strokes.Count, sentStrokes); } From a8f606d592855b595242639d31af315798e4782e Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Fri, 20 Dec 2024 11:14:09 +0000 Subject: [PATCH 173/174] Grey out the 'What's New' button on the admin panel. --- Assets/Prefabs/Panels/AdminPanel.prefab | 2 +- Assets/Scripts/SketchControlsScript.cs | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Assets/Prefabs/Panels/AdminPanel.prefab b/Assets/Prefabs/Panels/AdminPanel.prefab index bb9232afe2..b3c8e4d9f6 100644 --- a/Assets/Prefabs/Panels/AdminPanel.prefab +++ b/Assets/Prefabs/Panels/AdminPanel.prefab @@ -3366,7 +3366,7 @@ MonoBehaviour: m_HoverScale: 1.1 m_HoverBoxColliderGrow: 0.2 m_AddOverlay: 0 - m_Command: 0 + m_Command: 101 m_CommandParam: -1 m_CommandParam2: -1 m_RequiresPopup: 0 diff --git a/Assets/Scripts/SketchControlsScript.cs b/Assets/Scripts/SketchControlsScript.cs index 8bfb01b557..0def9b8fb1 100644 --- a/Assets/Scripts/SketchControlsScript.cs +++ b/Assets/Scripts/SketchControlsScript.cs @@ -144,6 +144,7 @@ public enum GlobalCommands SignOutConfirm, ReadOnlyNotice, ShowContribution, + WhatIsNew, // Open Brush Reserved Enums 1000-1999 LanguagePopup = 1000, @@ -4876,6 +4877,7 @@ public void IssueGlobalCommand(GlobalCommands rEnum, int iParam1 = -1, case GlobalCommands.MultiplayerLeaveRoom: break; // Intentionally blank. case GlobalCommands.MultiplayerConnect: break; // Intentionally blank. case GlobalCommands.MultiplayerDisconnect: break; // Intentionally blank. + case GlobalCommands.WhatIsNew: break;// Intentionally blank. default: Debug.LogError($"Unrecognized command {rEnum}"); break; @@ -5105,6 +5107,8 @@ public bool IsCommandAvailable(GlobalCommands rEnum, int iParam = -1) case GlobalCommands.EditMultiplayerNickName: case GlobalCommands.EditMultiplayerRoomName: return !(MultiplayerManager.m_Instance.State == ConnectionState.IN_ROOM); + case GlobalCommands.WhatIsNew: + return false; } return true; } From fe7e9edc30f32f7c0ed40b6d9425111862f06220 Mon Sep 17 00:00:00 2001 From: Riccardo Bovo Date: Fri, 20 Dec 2024 12:32:56 +0000 Subject: [PATCH 174/174] Sync reaching 100% and complete --- .../Multiplayer/MultiplayerSceneSync.cs | 101 +++++++++--------- Assets/Scripts/SketchControlsScript.cs | 2 +- 2 files changed, 52 insertions(+), 51 deletions(-) diff --git a/Assets/Scripts/Multiplayer/MultiplayerSceneSync.cs b/Assets/Scripts/Multiplayer/MultiplayerSceneSync.cs index e4a9c4ea7b..9c45185562 100644 --- a/Assets/Scripts/Multiplayer/MultiplayerSceneSync.cs +++ b/Assets/Scripts/Multiplayer/MultiplayerSceneSync.cs @@ -14,6 +14,7 @@ using System; using System.Collections; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; @@ -48,6 +49,11 @@ void Start() onLargeDataReceived += OnLargeDataReceived; } + private void Update() + { + ProcessQueue(); + } + void OnDestroy() { onLargeDataReceived -= OnLargeDataReceived; @@ -206,24 +212,12 @@ public async void StartSyncProgressDisplayForSrokes(int TargetPlayerId, LinkedLi foreach (var stroke in strokes) { - int retryCount = 0; - const int maxRetries = 3; - bool acknowledged = false; - while (retryCount < maxRetries) + while (await MultiplayerManager.m_Instance.CheckStrokeReception(stroke, TargetPlayerId)) { - if (await MultiplayerManager.m_Instance.CheckStrokeReception(stroke, TargetPlayerId)) - { - acknowledged = true; - break; - } - retryCount++; await Task.Delay(200); } - if (!acknowledged) - ControllerConsoleScript.m_Instance.AddNewLine($"Stroke {stroke.m_Guid} failed to synchronize after {maxRetries} retries."); - sentStrokes++; SynchHistoryPercentage(TargetPlayerId, strokes.Count, sentStrokes); } @@ -269,27 +263,54 @@ private void SynchHistoryComplete(int id) #region Local infoCard commands - private InfoCardAnimation infoCard; private readonly object infoCardLock = new object(); + private ConcurrentQueue messageQueue = new ConcurrentQueue(); + private InfoCardAnimation infoCard; + private void EnqueueMessage(string message) + { + messageQueue.Enqueue(message); + } - public void DisplaySynchInfo(string text = "Sync Started!") + private void ProcessQueue() //once per frame { - lock (infoCardLock) + if (messageQueue.TryDequeue(out string message)) { if (infoCard == null) { - OutputWindowScript.m_Instance.CreateInfoCardAtController( - InputManager.ControllerName.Brush, - text, - fPopScalar: 1.0f - ); - infoCard = RetrieveInfoCard(); + DisplaySynchInfo(message); + } + else + { + UpdateInfoCard(message); } } } - public InfoCardAnimation RetrieveInfoCard() + private void DisplaySynchInfo(string text) + { + if (infoCard == null) + { + OutputWindowScript.m_Instance.CreateInfoCardAtController( + InputManager.ControllerName.Brush, + text, + fPopScalar: 1.0f + ); + infoCard = RetrieveInfoCard(); + } + else + { + UpdateInfoCard(text); + } + } + + private void UpdateInfoCard(string text) + { + infoCard.GetComponentInChildren().text = text; + infoCard.UpdateHoldingDuration(5f); + } + + private InfoCardAnimation RetrieveInfoCard() { InfoCardAnimation[] allInfoCards = FindObjectsOfType(); foreach (var card in allInfoCards) @@ -303,41 +324,21 @@ public InfoCardAnimation RetrieveInfoCard() return null; } + public void StartSynchInfo() + { + EnqueueMessage("Sync Started!"); + } public void SynchInfoPercentageUpdate() { - lock (infoCardLock) - { - int percentage = (int)((float)SketchMemoryScript.AllStrokesCount() / numberOfCommandsExpected * 100); - string text = $"Sync {percentage}%"; - - if (infoCard == null) - { - DisplaySynchInfo(text); - return; - } - - infoCard.GetComponentInChildren().text = text; - infoCard.UpdateHoldingDuration(5f); - } + int percentage = (int)((float)SketchMemoryScript.AllStrokesCount() / numberOfCommandsExpected * 100); + EnqueueMessage($"Sync {percentage}%"); } public void HideSynchInfo() { - lock (infoCardLock) - { - if (infoCard == null) - { - DisplaySynchInfo("Sync Ended!"); - return; - } - - infoCard.GetComponentInChildren().text = "Sync Ended!"; - infoCard.UpdateHoldingDuration(3.0f); - ControllerConsoleScript.m_Instance.AddNewLine("Sync Ended!"); - } + EnqueueMessage("Sync Ended!"); } - #endregion } diff --git a/Assets/Scripts/SketchControlsScript.cs b/Assets/Scripts/SketchControlsScript.cs index 0def9b8fb1..cfdab9e636 100644 --- a/Assets/Scripts/SketchControlsScript.cs +++ b/Assets/Scripts/SketchControlsScript.cs @@ -4862,7 +4862,7 @@ public void IssueGlobalCommand(GlobalCommands rEnum, int iParam1 = -1, SketchSurfacePanel.m_Instance.EatToolsInput(); break; case GlobalCommands.DisplaySynchInfo: - MultiplayerSceneSync.m_Instance.DisplaySynchInfo(); + MultiplayerSceneSync.m_Instance.StartSynchInfo(); break; case GlobalCommands.SynchInfoPercentageUpdate: MultiplayerSceneSync.m_Instance.SynchInfoPercentageUpdate();