Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Basis/Packages/com.basis.eventdriver/BasisEventDriver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,9 @@ private void LateUpdateBody()

// ── Nameplate schedule ──
ProfileBegin(PROF_NAMEPLATE_SCHEDULE);
BasisNamePlateMeshBaker.ProcessBakeQueue();
BasisRemoteNamePlateDriver.ScheduleSimulate(TimeAsDouble);
BasisCameraNamePlateDriver.UpdateAllPlatePositions();
ProfileEnd(PROF_NAMEPLATE_SCHEDULE);
#if STEAMAUDIO_ENABLED
SteamAudioManager.Schedule();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ public static void ResetNamePlateDefaults()
public static void ApplyNamePlateSettings()
{
BasisRemoteNamePlateDriver.ApplyNamePlateSettingsFromUI();
BasisCameraNamePlateDriver.ApplyNamePlateSettingsFromUI();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -798,7 +798,10 @@ private void RunAfterInitialized()
{
if (FireOffNetwork)
{
BasisNamePlateAssets.Initialize();
BasisNamePlateMeshBaker.Initialize();
BasisRemoteNamePlateDriver.Initialize();
BasisCameraNamePlateDriver.Initialize();
BasisNetworkLifeCycle.Initialize();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Basis.Scripts.Networking.NetworkedAvatar;
using Basis.Scripts.Networking.Receivers;
using Basis.Scripts.UI;
using Basis.Scripts.UI.NamePlate;
using Basis.Network.Core;
using System.Threading;
using System.Threading.Tasks;
Expand Down Expand Up @@ -77,6 +78,10 @@ public static async Task RebootManagement(bool DisplayReason, NetPeer peer, Disc
}

BasisRemoteNetworkDriver.Apply();//complete in-flight jobs before clearing players
#if !UNITY_SERVER
BasisCameraNamePlateDriver.ClearAll();
BasisNetworkPIPCameraDriver.ClearRemotePIPs();
#endif
BasisNetworkPlayers.ClearAllRegistries();//remove players
Basis.Scripts.Networking.Receivers.BasisShoutAudioDriver.DeInitialize();//remove shout audio sources
await BasisNetworkSpawnItem.Reset();//remove items
Expand Down Expand Up @@ -150,6 +155,7 @@ public static async Task Destroy()
BasisNetworkHandleTempBlock.Shutdown();
BasisNetworkHandleChatTyping.Shutdown();
#if !UNITY_SERVER
BasisCameraNamePlateDriver.Shutdown();
BasisNetworkPIPCameraDriver.Shutdown();
#endif
BasisNetworkConnection.NetworkClient?.Disconnect();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,17 +115,7 @@ public static void Shutdown()

BasisLocalPlayer.AfterSimulateOnLate.RemoveAction(SimulatePriority, Simulate);

smoothHandle.Complete();

// Destroy all spawned PIP instances
foreach (var kvp in pipInstances)
{
if (kvp.Value != null)
{
UnityEngine.Object.Destroy(kvp.Value);
}
}
pipInstances.Clear();
ClearRemotePIPs();

// Release the loaded prefab
if (prefabHandle.IsValid())
Expand All @@ -140,14 +130,37 @@ public static void Shutdown()
if (targetRotations.IsCreated) targetRotations.Dispose();
if (activeFlags.IsCreated) activeFlags.Dispose();

initialized = false;
}

public static void ClearRemotePIPs()
{
if (!initialized) return;

smoothHandle.Complete();

foreach (var kvp in pipInstances)
{
if (kvp.Value != null)
{
UnityEngine.Object.Destroy(kvp.Value);
}
}
pipInstances.Clear();
pipTransforms.Clear();
playerIdToIndex.Clear();
indexToPlayerId.Clear();
activeRemotePIPs.Clear();
pipTransforms.Clear();
recycledIndices.Clear();
nextFreeIndex = 0;

initialized = false;
if (activeFlags.IsCreated)
{
for (int i = 0; i < activeFlags.Length; i++)
{
activeFlags[i] = 0;
}
}
}

/// <summary>
Expand Down Expand Up @@ -300,6 +313,23 @@ public static bool TryGetPIPPosition(ushort playerId, out Vector3 position)
return false;
}

/// <summary>
/// Try to get the world pose of a remote PIP camera by player ID.
/// </summary>
public static bool TryGetPIPPose(ushort playerId, out Vector3 position, out Quaternion rotation)
{
if (pipTransforms.TryGetValue(playerId, out Transform t) && t != null)
{
position = t.position;
rotation = t.rotation;
return true;
}

position = Vector3.zero;
rotation = Quaternion.identity;
return false;
}

/// <summary>
/// Send a camera shutter sound event to the server for broadcast to other clients.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
using Basis.Scripts.BasisSdk.Players;
using UnityEngine;

namespace Basis.Scripts.UI.NamePlate
{
/// <summary>
/// Nameplate displayed above a remote PIP camera feed.
/// Shows the player's display name using the same font and visual style as avatar nameplates.
/// </summary>
public class BasisCameraNamePlate : MonoBehaviour
{
/// <summary>
/// The MeshFilter for the baked nameplate mesh.
/// </summary>
public MeshFilter BackgroundFilter;

/// <summary>
/// The MeshRenderer for the baked nameplate mesh.
/// </summary>
public MeshRenderer BackgroundRenderer;

/// <summary>
/// The player ID this nameplate belongs to.
/// </summary>
public ushort PlayerID;

public BasisRemotePlayer RemotePlayer;

/// <summary>
/// Whether this nameplate is currently active and visible.
/// </summary>
// Camera nameplate lifecycle is driven from Unity's main thread.
public bool IsActive = true;
private bool _lastAppliedActive = true;

/// <summary>
/// The current display name being shown.
/// </summary>
public string DisplayName;

private BasisNamePlateVisual visual;

public void Initialize(ushort playerId, string displayName, Transform parentTransform, BasisRemotePlayer remotePlayer = null)
{
if (string.IsNullOrEmpty(displayName))
{
displayName = $"Player {playerId}";
}

PlayerID = playerId;
RemotePlayer = remotePlayer;
DisplayName = displayName;
gameObject.name = $"CameraNamePlate_{playerId}";
ApplyScale();

if (BackgroundFilter == null && !TryGetComponent(out BackgroundFilter))
{
BackgroundFilter = gameObject.AddComponent<MeshFilter>();
}

if (BackgroundRenderer == null && !TryGetComponent(out BackgroundRenderer))
{
BackgroundRenderer = gameObject.AddComponent<MeshRenderer>();
}

visual = new BasisNamePlateVisual("CameraNamePlateCombinedMesh");
visual.Attach(BackgroundFilter, BackgroundRenderer);
visual.SetDisplayName(displayName);

// Parent under BasisDeviceManagement for lifetime management
if (parentTransform != null)
{
transform.SetParent(parentTransform, false);
}

// Register with the driver
BasisCameraNamePlateDriver.Register(this);

QueueMeshRefreshIfNeeded();
RefreshActiveState();
}

public void UpdateDisplayName(string newName)
{
if (string.IsNullOrEmpty(newName))
{
newName = $"Player {PlayerID}";
}

if (DisplayName == newName) return;
DisplayName = newName;
visual?.SetDisplayName(newName);
}

public void OnPlayerLeft()
{
BasisCameraNamePlateDriver.Unregister(this);
gameObject.SetActive(false);
Destroy(gameObject);
}

public void QueueMeshRefreshIfNeeded()
{
visual?.QueueMeshRefreshIfNeeded();
}

public void SetActive(bool active)
{
IsActive = active;
RefreshActiveState();
}

public void ApplyScale()
{
transform.localScale = Vector3.one * 0.02f * BasisNamePlateSettings.NamePlateSize * BasisCameraNamePlateDriver.NamePlateScale;
visual?.ApplyMaterial();
}

public void RefreshActiveState()
{
bool active = BasisCameraNamePlateDriver.ShouldPlateBeActive(this);
if (_lastAppliedActive == active && gameObject.activeSelf == active)
{
return;
}

_lastAppliedActive = active;
gameObject.SetActive(active);
}

private void OnDestroy()
{
BasisCameraNamePlateDriver.Unregister(this);
visual?.Dispose();
visual = null;
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading