Skip to content

Commit f9b1b14

Browse files
committed
more work on dolly
1 parent 020b98d commit f9b1b14

11 files changed

Lines changed: 210 additions & 457 deletions

Basis/Assets/AddressableAssetsData/AddressableAssetSettings.asset

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ MonoBehaviour:
1515
m_DefaultGroup: 37c75a6cf72db324eb01200602299fc8
1616
m_currentHash:
1717
serializedVersion: 2
18-
Hash: 00000000000000000000000000000000
18+
Hash: 65ced2d2c4a4fdc005e9b6d7fa8fe640
1919
m_ExtractTypeTreeData: 0
2020
m_OptimizeCatalogSize: 0
2121
m_BuildRemoteCatalog: 0

Basis/Assets/AddressableAssetsData/link.xml

Lines changed: 0 additions & 417 deletions
This file was deleted.

Basis/Assets/AddressableAssetsData/link.xml.meta

Lines changed: 0 additions & 7 deletions
This file was deleted.

Basis/Packages/com.basis.camera/Runtime/BasisHandHeldCameraModifiers.cs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -81,10 +81,17 @@ internal void TickDollyTrack()
8181
DollyTrack.SyncMode = Modifiers.dolly.syncMode;
8282
DollyTrack.Refresh(scale, facing);
8383

84-
// Everyone else's tracks are drawn from the same frame as our own, so a shared track is
85-
// never a frame behind the one it is being laid out beside.
84+
// Claimed every frame rather than on the share dropdown, so a track that turns networked
85+
// through a preset or a restored mode is shared on the same terms as one switched by hand.
86+
if (Modifiers.dolly.syncMode == BasisCameraDollySync.LocalOnly)
87+
{
88+
BasisCameraDollyManager.ReleaseLocalTrack(DollyTrack);
89+
}
90+
else
91+
{
92+
BasisCameraDollyManager.ClaimLocalTrack(DollyTrack);
93+
}
8694
BasisCameraDollyManager.Tick(Time.time);
87-
BasisCameraDollyManager.TickMirrors(scale, facing);
8895
}
8996

9097
internal void DisposeModifiers()
@@ -93,7 +100,7 @@ internal void DisposeModifiers()
93100
// to update it.
94101
if (DollyTrack != null)
95102
{
96-
BasisCameraDollyManager.SetLocalTrack(null);
103+
BasisCameraDollyManager.ReleaseLocalTrack(DollyTrack);
97104
}
98105
DollyTrack?.Dispose();
99106
DollyTrack = null;

Basis/Packages/com.basis.camera/Runtime/BasisHandHeldCameraPanelProvider.Modifiers.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -513,7 +513,7 @@ private void BuildPositionGroup(RectTransform parent)
513513

514514
_dollySpeedSlider = PanelSlider.CreateNew(content);
515515
_dollySpeedSlider.SetSliderSettings(PanelSlider.SliderSettings.Advanced(
516-
BasisLocalization.Get("camera.dollySpeed"), -3f, 3f, false, 2, ValueDisplayMode.Raw));
516+
BasisLocalization.Get("camera.dollySpeed"), -100f, 100f, false, 2, ValueDisplayMode.Raw));
517517
_dollySpeedSlider.Descriptor.SetDescription(BasisLocalization.Get("camera.dollySpeed.description"));
518518
_dollySpeedSlider.OnValueChanged = v =>
519519
{
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using UnityEngine;
2+
3+
namespace Basis.Cinematics
4+
{
5+
/// <summary>
6+
/// Arms the shared dolly service for the session. It has to exist before any camera is taken out:
7+
/// a track arriving from somebody else is handled whether or not this client has ever opened one.
8+
/// </summary>
9+
public static class BasisCameraDollyBootstrap
10+
{
11+
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
12+
private static void Initialize()
13+
{
14+
BasisCameraDollyManager.Initialize();
15+
}
16+
}
17+
}

Basis/Packages/com.basis.camera/Runtime/Cinematic/BasisCameraDollyBootstrap.cs.meta

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Basis/Packages/com.basis.camera/Runtime/Cinematic/BasisCameraDollyManager.cs

Lines changed: 120 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
using System.Collections.Generic;
2+
using Basis.Scripts.BasisSdk.Players;
3+
using Basis.Scripts.Drivers;
24
using Basis.Scripts.Networking;
35
using Basis.Scripts.Networking.NetworkedAvatar;
46
using Basis.Network.Core;
@@ -54,6 +56,7 @@ public static class BasisCameraDollyManager
5456
private static float _nextKeyframe;
5557
private static int _lastSentCount = -1;
5658
private static BasisCameraDollySync _lastSentMode = BasisCameraDollySync.LocalOnly;
59+
private static bool _mirrorTickRequested;
5760

5861
// ---- Lifecycle -------------------------------------------------------------------
5962

@@ -66,14 +69,24 @@ public static void Initialize()
6669
BasisNetworkPlayer.OnPlayerLeft += HandlePlayerLeft;
6770
BasisNetworkPlayer.OnPlayerJoined -= HandlePlayerJoined;
6871
BasisNetworkPlayer.OnPlayerJoined += HandlePlayerJoined;
72+
BasisNetworkPlayer.OnLocalPlayerJoined -= HandleLocalPlayerJoined;
73+
BasisNetworkPlayer.OnLocalPlayerJoined += HandleLocalPlayerJoined;
74+
Application.quitting -= Shutdown;
75+
Application.quitting += Shutdown;
6976

70-
ResolveNetworkId();
77+
if (BasisNetworkConnection.LocalPlayerIsConnected)
78+
{
79+
HandleLocalPlayerJoined(null, null);
80+
}
7181
}
7282

7383
public static void Shutdown()
7484
{
7585
BasisNetworkPlayer.OnPlayerLeft -= HandlePlayerLeft;
7686
BasisNetworkPlayer.OnPlayerJoined -= HandlePlayerJoined;
87+
BasisNetworkPlayer.OnLocalPlayerJoined -= HandleLocalPlayerJoined;
88+
BasisNetworkPlayer.OnLocalPlayerLeft -= HandleLocalPlayerLeft;
89+
Application.quitting -= Shutdown;
7790

7891
if (HasNetworkID)
7992
{
@@ -87,9 +100,19 @@ public static void Shutdown()
87100
ClearAllMirrors();
88101
}
89102

90-
private static async void ResolveNetworkId()
103+
/// <summary>
104+
/// Resolving the shared identifier needs a live connection, so it waits for the local player
105+
/// to be approved rather than running at load. The handler is re-armed per join because the
106+
/// network lifecycle nulls the delegate on teardown.
107+
/// </summary>
108+
private static async void HandleLocalPlayerJoined(BasisNetworkPlayer networkPlayer, BasisLocalPlayer localPlayer)
91109
{
92110
if (HasNetworkID) return;
111+
if (!BasisNetworkConnection.LocalPlayerIsConnected)
112+
{
113+
BasisDebug.LogError("Dolly manager cannot start; the local player is not connected.", LogTag);
114+
return;
115+
}
93116

94117
BasisIdResolutionResult resolution = await BasisNetworkIdResolver.ResolveAsync(FixedNetworkIdentifier);
95118
if (!_initialized || HasNetworkID) return;
@@ -104,6 +127,27 @@ private static async void ResolveNetworkId()
104127
NetworkID = resolution.Id;
105128
HasNetworkID = true;
106129
BasisNetworkGenericMessages.RegisterDirectHandler(NetworkID, OnDirectNetworkMessage);
130+
131+
BasisNetworkPlayer.OnLocalPlayerLeft -= HandleLocalPlayerLeft;
132+
BasisNetworkPlayer.OnLocalPlayerLeft += HandleLocalPlayerLeft;
133+
134+
_lastSentCount = -1;
135+
_nextRoster = 0f;
136+
_nextKeyframe = 0f;
137+
BasisDebug.Log($"Dolly manager ready (network id {NetworkID}).", LogTag);
138+
}
139+
140+
private static void HandleLocalPlayerLeft(BasisNetworkPlayer networkPlayer, BasisLocalPlayer localPlayer)
141+
{
142+
if (HasNetworkID)
143+
{
144+
BasisNetworkGenericMessages.UnregisterDirectHandler(NetworkID);
145+
}
146+
HasNetworkID = false;
147+
NetworkID = 0;
148+
_lastSentCount = -1;
149+
150+
ClearAllMirrors();
107151
}
108152

109153
// ---- The local track --------------------------------------------------------------
@@ -126,6 +170,27 @@ public static void SetLocalTrack(BasisCameraDollyTrack track)
126170
_nextKeyframe = 0f;
127171
}
128172

173+
/// <summary>
174+
/// Offers a track for sharing. A roster is keyed by the player who sent it, so a client
175+
/// shares one track at a time; with several cameras out the first to ask keeps the slot
176+
/// until it stops sharing, rather than the two of them withdrawing each other every frame.
177+
/// </summary>
178+
public static void ClaimLocalTrack(BasisCameraDollyTrack track)
179+
{
180+
if (track == null || ReferenceEquals(_local, track)) return;
181+
if (_local != null && _local.SyncMode != BasisCameraDollySync.LocalOnly) return;
182+
183+
SetLocalTrack(track);
184+
}
185+
186+
/// <summary>Gives the slot up, but only for the track actually holding it.</summary>
187+
public static void ReleaseLocalTrack(BasisCameraDollyTrack track)
188+
{
189+
if (track == null || !ReferenceEquals(_local, track)) return;
190+
191+
SetLocalTrack(null);
192+
}
193+
129194
/// <summary>
130195
/// Sends the local track when it is due. Called from the camera's own frame, so a client
131196
/// with no camera out costs nothing at all.
@@ -145,7 +210,7 @@ public static void Tick(float time)
145210
return;
146211
}
147212

148-
bool changed = _local.Count != _lastSentCount || _local.SyncMode != _lastSentMode;
213+
bool changed = _local.Count != _lastSentCount || _local.SyncMode != _lastSentMode || LocalTrackIsHeld();
149214
bool due = changed ? time >= _nextRoster : time >= _nextKeyframe;
150215
if (!due) return;
151216

@@ -156,6 +221,20 @@ public static void Tick(float time)
156221
_lastSentMode = _local.SyncMode;
157222
}
158223

224+
/// <summary>
225+
/// Whether a point is in somebody's hand. A drag moves points without changing the count, so
226+
/// without this a track being laid out would only reach the others at the keyframe rate.
227+
/// </summary>
228+
private static bool LocalTrackIsHeld()
229+
{
230+
for (int Index = 0; Index < _local.Count; Index++)
231+
{
232+
BasisCameraDollyWaypoint waypoint = _local.GetWaypoint(Index);
233+
if (waypoint != null && waypoint.IsGrabbed) return true;
234+
}
235+
return false;
236+
}
237+
159238
private static void BroadcastRoster(ushort[] recipients)
160239
{
161240
int count = Mathf.Min(_local.Count, BasisCameraDollyPacket.MaxPoints);
@@ -172,7 +251,7 @@ private static void BroadcastRoster(ushort[] recipients)
172251
}
173252

174253
EnsureBuffer(BasisCameraDollyPacket.RosterSize(count));
175-
int written = BasisCameraDollyPacket.WriteRoster(_sendBuffer, _local.SyncMode, _scratch, count);
254+
int written = BasisCameraDollyPacket.WriteRoster(_sendBuffer, _local.SyncMode, _local.Looped, _scratch, count);
176255
if (written > 0) Send(written, DeliveryMethod.ReliableOrdered, recipients);
177256
}
178257

@@ -182,7 +261,7 @@ private static void BroadcastWithdrawal()
182261
if (!HasNetworkID) return;
183262

184263
EnsureBuffer(BasisCameraDollyPacket.RosterSize(0));
185-
int written = BasisCameraDollyPacket.WriteRoster(_sendBuffer, BasisCameraDollySync.LocalOnly, _scratch, 0);
264+
int written = BasisCameraDollyPacket.WriteRoster(_sendBuffer, BasisCameraDollySync.LocalOnly, false, _scratch, 0);
186265
if (written > 0) Send(written, DeliveryMethod.ReliableOrdered, null);
187266
}
188267

@@ -234,7 +313,7 @@ private static void OnDirectNetworkMessage(ushort playerId, byte[] buffer, Deliv
234313
private static void ApplyRoster(ushort playerId, byte[] buffer, int length)
235314
{
236315
if (!BasisCameraDollyPacket.TryReadRoster(buffer, length, _scratch,
237-
out BasisCameraDollySync mode, out int count))
316+
out BasisCameraDollySync mode, out bool looped, out int count))
238317
{
239318
return;
240319
}
@@ -249,8 +328,9 @@ private static void ApplyRoster(ushort playerId, byte[] buffer, int length)
249328
{
250329
mirror = new BasisCameraDollyMirror(playerId);
251330
_mirrors[playerId] = mirror;
331+
UpdateMirrorTickRequest();
252332
}
253-
mirror.Apply(mode, _scratch, count);
333+
mirror.Apply(mode, looped, _scratch, count);
254334
}
255335

256336
private static void ApplyPointMove(ushort playerId, byte[] buffer, int length)
@@ -318,6 +398,7 @@ private static void DropMirror(ushort playerId)
318398
{
319399
mirror.Dispose();
320400
_mirrors.Remove(playerId);
401+
UpdateMirrorTickRequest();
321402
}
322403
}
323404

@@ -328,11 +409,41 @@ private static void ClearAllMirrors()
328409
pair.Value.Dispose();
329410
}
330411
_mirrors.Clear();
412+
UpdateMirrorTickRequest();
331413
}
332414

333-
/// <summary>Refreshes every mirrored track's markers. Driven from the camera's frame.</summary>
334-
public static void TickMirrors(float scale, Quaternion labelFacing)
415+
/// <summary>
416+
/// Somebody else's track has to draw on a client that has no camera of its own out — seeing
417+
/// where a shot is being laid out is the point of sharing it — so the frame clock drives the
418+
/// mirrors rather than the handheld camera. The request is held only while there is a track
419+
/// to draw, so a client with none costs nothing.
420+
/// </summary>
421+
private static void UpdateMirrorTickRequest()
335422
{
423+
bool wanted = _mirrors.Count > 0;
424+
if (wanted == _mirrorTickRequested) return;
425+
426+
_mirrorTickRequested = wanted;
427+
if (wanted)
428+
{
429+
BasisFrameClock.OnTick += TickMirrors;
430+
BasisFrameClock.AddRequest();
431+
}
432+
else
433+
{
434+
BasisFrameClock.OnTick -= TickMirrors;
435+
BasisFrameClock.RemoveRequest();
436+
}
437+
}
438+
439+
/// <summary>Refreshes every mirrored track's markers.</summary>
440+
public static void TickMirrors()
441+
{
442+
float scale = BasisHeightDriver.AvatarToDefaultRatioScaledWithAvatarScale;
443+
Quaternion labelFacing = BasisLocalCameraDriver.HasInstance && BasisLocalCameraDriver.CameraInstance != null
444+
? BasisLocalCameraDriver.CameraInstance.transform.rotation
445+
: Quaternion.identity;
446+
336447
foreach (KeyValuePair<ushort, BasisCameraDollyMirror> pair in _mirrors)
337448
{
338449
pair.Value.Refresh(scale, labelFacing);

Basis/Packages/com.basis.camera/Runtime/Cinematic/BasisCameraDollyMirror.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,13 @@ public BasisCameraDollyMirror(ushort owner)
4040

4141
/// <summary>
4242
/// Brings the shown track in line with what the author sent: the same count, the same
43-
/// places, and the same rule about whether it can be touched.
43+
/// places, the same path through them, and the same rule about whether it can be touched.
4444
/// </summary>
45-
public void Apply(BasisCameraDollySync mode, BasisCameraDollyPacket.Point[] points, int count)
45+
public void Apply(BasisCameraDollySync mode, bool looped, BasisCameraDollyPacket.Point[] points, int count)
4646
{
4747
_mode = mode;
4848
_track.SyncMode = mode;
49+
_track.Looped = looped;
4950

5051
count = Mathf.Clamp(count, 0, BasisCameraDollyPacket.MaxPoints);
5152

Basis/Packages/com.basis.camera/Runtime/Cinematic/BasisCameraDollyPacket.cs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,15 @@ public static class BasisCameraDollyPacket
3737

3838
private const int HeaderSize = 1;
3939
private const int OwnerSize = 2;
40-
private const int PointSize = 28; // position (12) + rotation (16)
41-
private const int RosterHeader = HeaderSize + 1 + 1; // type, mode, count
40+
private const int PointSize = 28; // position (12) + rotation (16)
41+
private const int RosterHeader = HeaderSize + 1 + 1 + 1; // type, mode, count, flags
42+
43+
/// <summary>
44+
/// The track closes back on itself. It belongs with the points rather than beside them: it
45+
/// decides whether the last waypoint joins the first, so a mirror without it draws a
46+
/// different path from the one the author is looking at.
47+
/// </summary>
48+
private const byte RosterFlagLooped = 1 << 0;
4249
private const int MoveSize = HeaderSize + OwnerSize + 1 + PointSize;
4350
private const int ClaimSize = HeaderSize + OwnerSize + 1 + 1;
4451

@@ -62,7 +69,7 @@ public struct Point
6269
/// callers size with <see cref="RosterSize"/>, so 0 means a caller bug rather than a
6370
/// runtime condition to handle.
6471
/// </summary>
65-
public static int WriteRoster(byte[] buffer, BasisCameraDollySync mode, Point[] points, int count)
72+
public static int WriteRoster(byte[] buffer, BasisCameraDollySync mode, bool looped, Point[] points, int count)
6673
{
6774
count = Mathf.Clamp(count, 0, MaxPoints);
6875
if (points == null) count = 0;
@@ -73,6 +80,7 @@ public static int WriteRoster(byte[] buffer, BasisCameraDollySync mode, Point[]
7380
buffer[offset++] = (byte)BasisCameraDollyPacketType.Roster;
7481
buffer[offset++] = (byte)mode;
7582
buffer[offset++] = (byte)count;
83+
buffer[offset++] = looped ? RosterFlagLooped : (byte)0;
7684

7785
for (int Index = 0; Index < count; Index++)
7886
{
@@ -129,9 +137,10 @@ public static bool TryReadType(byte[] buffer, int length, out BasisCameraDollyPa
129137
/// False when the packet is truncated or names a mode this build does not have.
130138
/// </summary>
131139
public static bool TryReadRoster(byte[] buffer, int length, Point[] points,
132-
out BasisCameraDollySync mode, out int count)
140+
out BasisCameraDollySync mode, out bool looped, out int count)
133141
{
134142
mode = BasisCameraDollySync.LocalOnly;
143+
looped = false;
135144
count = 0;
136145

137146
if (!TryReadType(buffer, length, out BasisCameraDollyPacketType type) ||
@@ -147,6 +156,7 @@ public static bool TryReadRoster(byte[] buffer, int length, Point[] points,
147156
if (length < RosterSize(declared)) return false;
148157

149158
mode = (BasisCameraDollySync)buffer[1];
159+
looped = (buffer[3] & RosterFlagLooped) != 0;
150160
count = declared;
151161

152162
int offset = RosterHeader;

0 commit comments

Comments
 (0)