Skip to content

Commit 55a5426

Browse files
committed
added GlobalToggleAdditionalAvatarDataLock aswell as most of the way there on local avatar preview camera rendererr
1 parent 6d1ee2f commit 55a5426

18 files changed

Lines changed: 56890 additions & 5696 deletions

File tree

Basis Server/BasisNetworkCore/BasisServerConfiguration.cs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,17 @@ public class Configuration
7878
/// </summary>
7979
public bool ThirdPersonDisabled = false;
8080
/// <summary>
81-
/// Read config from file. If no file is found create a default config file at filePath
81+
/// When true, the server strips AdditionalAvatarDatas (blendshapes, custom-behaviour
82+
/// params) from every inbound avatar sync message before propagating to other peers.
83+
/// Muscle/position/rotation still sync normally; only the additional-data payload is
84+
/// dropped. Toggled live via the admin panel and persisted alongside the other
85+
/// content lockouts. Default off.
86+
/// </summary>
87+
public bool AdditionalAvatarDataLock = false;
88+
/// <summary>
89+
/// Read config from file. If no file is found create a default config file at filePath.
90+
/// Also loads per-transport config sidecars from <c>{configDir}/transports/{stackId}.xml</c>.
8291
/// </summary>
83-
/// <param name="filePath">Path to config file</param>
8492
public static Configuration LoadFromXml(string filePath)
8593
{
8694
RuntimeHelpers.RunClassConstructor(typeof(BasisNetworkStackRegistry).TypeHandle);

Basis Server/BasisNetworkCore/Serializable/AdminRequest.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,12 @@ public enum AdminRequestMode : byte
9292
// Payload: [string url]
9393
// Removes every defaultlibrary/ XML whose URL matches and rebroadcasts.
9494
RemoveDefaultLibraryItem,
95+
96+
// admin: toggle the global strip of AdditionalAvatarDatas (blendshapes,
97+
// custom-behaviour params) on inbound avatar sync messages. Muscle/position/
98+
// rotation still propagate normally. State is appended as the 6th bool in
99+
// GlobalGetLockState.
100+
GlobalToggleAdditionalAvatarDataLock,
95101
}
96102
}
97103
}

Basis Server/BasisNetworkServer/BasisNetworkingReductionSystem/BasisServerReductionSystemEvents.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1061,6 +1061,12 @@ private static void ProcessMessage(QueuedMessage message)
10611061
return;
10621062
}
10631063

1064+
if (BasisNetworkServer.Security.BasisGlobalLockManager.AdditionalAvatarDataLock)
1065+
{
1066+
poolMsg.AdditionalAvatarDatas = null;
1067+
poolMsg.AdditionalAvatarDataSize = 0;
1068+
}
1069+
10641070
var incomingQuality = (BitQuality)poolMsg.DataQualityLevel;
10651071
bool isHighQuality = incomingQuality == BitQuality.High;
10661072

Basis Server/BasisNetworkServer/Security/BasisGlobalLockManager.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,14 @@ public static class BasisGlobalLockManager
1717
private static int _worldsLocked;
1818
private static int _serversLocked;
1919
private static int _thirdPersonDisabled;
20+
private static int _additionalAvatarDataLock;
2021

2122
public static bool AvatarsLocked => Interlocked.CompareExchange(ref _avatarsLocked, 0, 0) == 1;
2223
public static bool PropsLocked => Interlocked.CompareExchange(ref _propsLocked, 0, 0) == 1;
2324
public static bool WorldsLocked => Interlocked.CompareExchange(ref _worldsLocked, 0, 0) == 1;
2425
public static bool ServersLocked => Interlocked.CompareExchange(ref _serversLocked, 0, 0) == 1;
2526
public static bool ThirdPersonDisabled => Interlocked.CompareExchange(ref _thirdPersonDisabled, 0, 0) == 1;
27+
public static bool AdditionalAvatarDataLock => Interlocked.CompareExchange(ref _additionalAvatarDataLock, 0, 0) == 1;
2628

2729
/// <summary>
2830
/// Seed the initial lock state from the server configuration.
@@ -35,6 +37,7 @@ public static void InitializeFromConfig(Configuration config)
3537
Interlocked.Exchange(ref _worldsLocked, config.WorldsLocked ? 1 : 0);
3638
Interlocked.Exchange(ref _serversLocked, config.ServersLocked ? 1 : 0);
3739
Interlocked.Exchange(ref _thirdPersonDisabled, config.ThirdPersonDisabled ? 1 : 0);
40+
Interlocked.Exchange(ref _additionalAvatarDataLock, config.AdditionalAvatarDataLock ? 1 : 0);
3841
}
3942

4043
/// <summary>
@@ -62,6 +65,12 @@ public static void InitializeFromConfig(Configuration config)
6265
/// </summary>
6366
public static bool ToggleThirdPerson() => Toggle(ref _thirdPersonDisabled);
6467

68+
/// <summary>
69+
/// Toggle the network-side strip of AdditionalAvatarDatas on inbound avatar
70+
/// sync messages. Returns the new state (true = additional data stripped).
71+
/// </summary>
72+
public static bool ToggleAdditionalAvatarDataLock() => Toggle(ref _additionalAvatarDataLock);
73+
6574
private static bool Toggle(ref int field)
6675
{
6776
int prev, next;
@@ -88,6 +97,8 @@ public static void SendLockStateToPeer(NetPeer peer)
8897
writer.Put(ServersLocked);
8998
// Appended after ServersLocked so older clients reading 4 bools still parse cleanly.
9099
writer.Put(ThirdPersonDisabled);
100+
// Appended after ThirdPersonDisabled — older clients parsing 5 bools still work.
101+
writer.Put(AdditionalAvatarDataLock);
91102
NetworkServer.TrySend(peer, writer, BasisNetworkCommons.AdminChannel, DeliveryMethod.ReliableOrdered);
92103
NetworkServer.ReturnWriter(writer);
93104
}
@@ -105,6 +116,8 @@ public static void BroadcastLockState()
105116
writer.Put(ServersLocked);
106117
// Appended after ServersLocked so older clients reading 4 bools still parse cleanly.
107118
writer.Put(ThirdPersonDisabled);
119+
// Appended after ThirdPersonDisabled — older clients parsing 5 bools still work.
120+
writer.Put(AdditionalAvatarDataLock);
108121
NetworkServer.BroadcastMessageToClients(
109122
writer,
110123
BasisNetworkCommons.AdminChannel,

Basis Server/BasisNetworkServer/Security/BasisPlayerModeration.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,11 @@ public static void OnAdminMessage(NetPeer peer, NetPacketReader reader)
344344
HandleGlobalToggle(peer, "Third-person camera", BasisGlobalLockManager.ToggleThirdPerson()));
345345
break;
346346

347+
case AdminRequestMode.GlobalToggleAdditionalAvatarDataLock:
348+
Require(peer, PermNodes.ModerationGlobalLock, () =>
349+
HandleGlobalToggle(peer, "Additional avatar data lock", BasisGlobalLockManager.ToggleAdditionalAvatarDataLock()));
350+
break;
351+
347352
case AdminRequestMode.SetGlobalHeadlessAudio:
348353
Require(peer, PermNodes.ModerationHeadlessAudio, () =>
349354
HandleHeadlessAudioSet(peer, reader));

Basis/Packages/com.basis.framework/BasisUI/Localization/Languages/en.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,8 @@
393393
{ "key": "settings.general.hud.title", "value": "HUD" },
394394
{ "key": "settings.general.desktopReticle", "value": "Desktop Reticle" },
395395
{ "key": "settings.general.desktopReticle.description", "value": "Show a small aim reticle at screen center while in desktop mode." },
396+
{ "key": "settings.general.avatarPreview", "value": "Avatar Preview" },
397+
{ "key": "settings.general.avatarPreview.description", "value": "Show a small preview of your avatar near the microphone icon." },
396398
{ "key": "settings.general.camera.title", "value": "Camera" },
397399
{ "key": "settings.general.thirdPerson", "value": "Third-Person Camera" },
398400
{ "key": "settings.general.thirdPerson.description", "value": "Allow the desktop camera to switch to a third-person view." },

Basis/Packages/com.basis.framework/BasisUI/Menus/Main Menu Providers/SettingsProvider.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,11 @@ public static PanelTabPage GeneralTab(PanelTabGroup tabGroup)
352352
toggleDesktopReticle.Descriptor.SetTitle(BasisLocalization.Get("settings.general.desktopReticle"));
353353
toggleDesktopReticle.Descriptor.SetDescription(BasisLocalization.Get("settings.general.desktopReticle.description"));
354354

355+
PanelToggle toggleAvatarPreview = PanelToggle.CreateNewEntry(hudGroup);
356+
toggleAvatarPreview.AssignBinding(BasisSettingsDefaults.AvatarPreview);
357+
toggleAvatarPreview.Descriptor.SetTitle(BasisLocalization.Get("settings.general.avatarPreview"));
358+
toggleAvatarPreview.Descriptor.SetDescription(BasisLocalization.Get("settings.general.avatarPreview.description"));
359+
355360
// Third-person camera is desktop-only; hide the entire group in VR/XR.
356361
if (BasisDeviceManagement.IsUserInDesktop())
357362
{

Basis/Packages/com.basis.framework/BasisUI/Menus/Main Menu Providers/SettingsProviderParts/SettingsProviderAdminTab.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,12 @@ public static PanelTabPage AdminTab(PanelTabGroup tabGroup)
8686
thirdPersonLock.SetValueWithoutNotify(BasisNetworkModeration.GlobalThirdPersonDisabled);
8787
thirdPersonLock.OnValueChanged += _ => BasisNetworkModeration.GlobalToggleThirdPerson();
8888

89+
PanelToggle additionalAvatarDataLock = PanelToggle.CreateNewEntry(lockGroup.ContentParent);
90+
additionalAvatarDataLock.Descriptor.SetTitle("Strip Additional Avatar Data");
91+
additionalAvatarDataLock.Descriptor.SetDescription("Strips additional avatar data (blendshapes, custom-behaviour params) from every player's network broadcast. Muscle, position, and rotation still sync normally.");
92+
additionalAvatarDataLock.SetValueWithoutNotify(BasisNetworkModeration.GlobalAdditionalAvatarDataLock);
93+
additionalAvatarDataLock.OnValueChanged += _ => BasisNetworkModeration.GlobalToggleAdditionalAvatarDataLock();
94+
8995
PanelSlider opusPacketLossSlider = PanelSlider.CreateNew(PanelSlider.SliderStyles.Entry, lockGroup.ContentParent);
9096
opusPacketLossSlider.SetSliderSettings(PanelSlider.SliderSettings.Percentage("Opus FEC loss %"));
9197
opusPacketLossSlider.Descriptor.SetDescription("Sets OPUS_SET_PACKET_LOSS_PERC on every client's voice encoder. Higher = more bitrate spent on redundant FEC data, better recovery under packet loss.");
@@ -97,6 +103,7 @@ public static PanelTabPage AdminTab(PanelTabGroup tabGroup)
97103
controller.WorldLockToggle = worldLock;
98104
controller.ServerShareLockToggle = serverShareLock;
99105
controller.ThirdPersonLockToggle = thirdPersonLock;
106+
controller.AdditionalAvatarDataLockToggle = additionalAvatarDataLock;
100107
controller.HeadlessAudioToggle = headlessAudioToggle;
101108
controller.HeadlessDisallowToggle = disallowHeadlessToggle;
102109
controller.OpusPacketLossSlider = opusPacketLossSlider;
@@ -352,6 +359,7 @@ private sealed class AdminTabController : MonoBehaviour
352359
public PanelToggle WorldLockToggle;
353360
public PanelToggle ServerShareLockToggle;
354361
public PanelToggle ThirdPersonLockToggle;
362+
public PanelToggle AdditionalAvatarDataLockToggle;
355363
public PanelToggle HeadlessAudioToggle;
356364
public PanelToggle HeadlessDisallowToggle;
357365
public PanelSlider OpusPacketLossSlider;
@@ -362,6 +370,8 @@ private void OnEnable()
362370
BasisNetworkModeration.OnGlobalLockStateChanged += OnGlobalLockStateChanged;
363371
BasisNetworkModeration.OnGlobalThirdPersonDisabledChanged -= OnGlobalThirdPersonDisabledChanged;
364372
BasisNetworkModeration.OnGlobalThirdPersonDisabledChanged += OnGlobalThirdPersonDisabledChanged;
373+
BasisNetworkModeration.OnGlobalAdditionalAvatarDataLockChanged -= OnGlobalAdditionalAvatarDataLockChanged;
374+
BasisNetworkModeration.OnGlobalAdditionalAvatarDataLockChanged += OnGlobalAdditionalAvatarDataLockChanged;
365375
BasisNetworkModeration.OnGlobalHeadlessAudioStateChanged -= OnGlobalHeadlessAudioStateChanged;
366376
BasisNetworkModeration.OnGlobalHeadlessAudioStateChanged += OnGlobalHeadlessAudioStateChanged;
367377
BasisNetworkModeration.OnGlobalHeadlessDisallowStateChanged -= OnGlobalHeadlessDisallowStateChanged;
@@ -374,6 +384,7 @@ private void OnDisable()
374384
{
375385
BasisNetworkModeration.OnGlobalLockStateChanged -= OnGlobalLockStateChanged;
376386
BasisNetworkModeration.OnGlobalThirdPersonDisabledChanged -= OnGlobalThirdPersonDisabledChanged;
387+
BasisNetworkModeration.OnGlobalAdditionalAvatarDataLockChanged -= OnGlobalAdditionalAvatarDataLockChanged;
377388
BasisNetworkModeration.OnGlobalHeadlessAudioStateChanged -= OnGlobalHeadlessAudioStateChanged;
378389
BasisNetworkModeration.OnGlobalHeadlessDisallowStateChanged -= OnGlobalHeadlessDisallowStateChanged;
379390
BasisNetworkModeration.OnGlobalOpusPacketLossChanged -= OnGlobalOpusPacketLossChanged;
@@ -383,6 +394,7 @@ private void OnDestroy()
383394
{
384395
BasisNetworkModeration.OnGlobalLockStateChanged -= OnGlobalLockStateChanged;
385396
BasisNetworkModeration.OnGlobalThirdPersonDisabledChanged -= OnGlobalThirdPersonDisabledChanged;
397+
BasisNetworkModeration.OnGlobalAdditionalAvatarDataLockChanged -= OnGlobalAdditionalAvatarDataLockChanged;
386398
BasisNetworkModeration.OnGlobalHeadlessAudioStateChanged -= OnGlobalHeadlessAudioStateChanged;
387399
BasisNetworkModeration.OnGlobalHeadlessDisallowStateChanged -= OnGlobalHeadlessDisallowStateChanged;
388400
BasisNetworkModeration.OnGlobalOpusPacketLossChanged -= OnGlobalOpusPacketLossChanged;
@@ -401,6 +413,11 @@ private void OnGlobalThirdPersonDisabledChanged(bool disabled)
401413
if (ThirdPersonLockToggle != null) ThirdPersonLockToggle.SetValueWithoutNotify(disabled);
402414
}
403415

416+
private void OnGlobalAdditionalAvatarDataLockChanged(bool locked)
417+
{
418+
if (AdditionalAvatarDataLockToggle != null) AdditionalAvatarDataLockToggle.SetValueWithoutNotify(locked);
419+
}
420+
404421
private void OnGlobalHeadlessAudioStateChanged(bool headlessAudioOff)
405422
{
406423
if (HeadlessAudioToggle != null) HeadlessAudioToggle.SetValueWithoutNotify(headlessAudioOff);

Basis/Packages/com.basis.framework/Drivers/Local/BasisLocalAvatarPreviewDriver.cs

Lines changed: 32 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using Basis.BasisUI;
22
using Basis.Scripts.BasisSdk.Players;
3+
using Basis.Scripts.Device_Management;
34
using Basis.Scripts.TransformBinders;
45
using UnityEngine;
56
using UnityEngine.Rendering.Universal;
@@ -8,7 +9,7 @@ namespace Basis.Scripts.Drivers
89
{
910
/// <summary>
1011
/// Creates a secondary camera that renders only the local avatar layer (layer 6)
11-
/// and displays the result on a HUD sprite near the microphone icon.
12+
/// and displays the result on a HUD sprite spanning the bottom-right half of the screen.
1213
/// Off by default; toggled via the AvatarPreview setting.
1314
/// All objects are created at runtime and cleaned up on disable/destroy.
1415
/// </summary>
@@ -21,27 +22,15 @@ public class BasisLocalAvatarPreviewDriver
2122

2223
[Header("Preview Camera")]
2324
/// <summary>Distance in front of the player's face the camera is placed.</summary>
24-
public static float CameraDistance = 3f;
25-
/// <summary>Where on the body the camera looks at, as a fraction of player height (0 = feet, 1 = head).</summary>
26-
public static float BodyFocusHeight = 1f;
25+
public static float CameraDistance = 1.6f;
2726
public float CameraFieldOfView = 40f;
2827

29-
[Header("HUD Display")]
30-
/// <summary>
31-
/// Normalized offset from the mic icon in frustum-relative units (fraction of half-frustum).
32-
/// X moves right, Y moves up. Stays at the same relative screen position on resize.
33-
/// </summary>
34-
public static Vector2 DisplayNormalizedOffset = new Vector2(0.15f, 0.03f);
35-
/// <summary>
36-
/// Local scale of the display sprite within ParentOfUI.
37-
/// </summary>
38-
public static float DisplayScale = 50f;
39-
4028
[System.NonSerialized] public Camera PreviewCamera;
4129
[System.NonSerialized] public RenderTexture PreviewRT;
4230

4331
private BasisLocalCameraDriver cachedDriver;
4432
private GameObject cameraGO;
33+
private GameObject parentOfUIGO;
4534
private GameObject displayGO;
4635
private SpriteRenderer displaySpriteRenderer;
4736
private MaterialPropertyBlock propertyBlock;
@@ -56,8 +45,6 @@ public class BasisLocalAvatarPreviewDriver
5645
/// </summary>
5746
public void Initialize(BasisLocalCameraDriver cameraDriver)
5847
{
59-
// TODO: re-enable when avatar preview is finished
60-
return;
6148
cachedDriver = cameraDriver;
6249

6350
// Apply the persisted setting
@@ -121,11 +108,13 @@ private void CreateObjects()
121108
urpData.allowXRRendering = false;
122109
}
123110

124-
// --- Display Sprite (child of ParentOfUI, next to the mic icon) ---
111+
parentOfUIGO = new GameObject("AvatarPreviewParentOfUI");
112+
parentOfUIGO.layer = LayerMask.NameToLayer("UI");
113+
parentOfUIGO.transform.SetParent(cachedDriver.transform, false);
114+
125115
displayGO = new GameObject("AvatarPreviewDisplay");
126116
displayGO.layer = LayerMask.NameToLayer("UI");
127-
displayGO.transform.SetParent(cachedDriver.ParentOfUI, false);
128-
displayGO.transform.localScale = new Vector3(DisplayScale, DisplayScale, 1f);
117+
displayGO.transform.SetParent(parentOfUIGO.transform, false);
129118
displayGO.transform.localRotation = Quaternion.identity;
130119

131120
displaySpriteRenderer = displayGO.AddComponent<SpriteRenderer>();
@@ -153,6 +142,7 @@ private void DestroyObjects()
153142

154143
if (cameraGO != null) { Object.Destroy(cameraGO); cameraGO = null; }
155144
if (displayGO != null) { Object.Destroy(displayGO); displayGO = null; }
145+
if (parentOfUIGO != null) { Object.Destroy(parentOfUIGO); parentOfUIGO = null; }
156146

157147
if (PreviewRT != null)
158148
{
@@ -185,33 +175,39 @@ public void Simulate()
185175

186176
Transform head = BasisLocalAvatarDriver.Mapping.head;
187177

188-
// Flatten head forward to horizontal (ignore pitch)
189-
Vector3 flatForward = head.forward;
190-
flatForward.y = 0f;
191-
flatForward.Normalize();
192-
193-
// Place camera in front of the avatar at body center height
194178
Vector3 feetPos = BasisLocalPlayer.Instance.transform.position;
195-
float bodyCenter = BasisHeightDriver.SelectedScaledPlayerHeight * BodyFocusHeight;
196-
Vector3 bodyTarget = feetPos + Vector3.up * bodyCenter;
179+
float playerHeight = BasisHeightDriver.SelectedScaledPlayerHeight;
180+
Vector3 bodyCenter = feetPos + Vector3.up * (playerHeight * 0.75f);
197181

198-
Vector3 cameraPos = bodyTarget + flatForward * CameraDistance;
182+
Vector3 cameraPos = bodyCenter + head.forward * CameraDistance;
199183

200184
PreviewCamera.transform.SetPositionAndRotation(
201185
cameraPos,
202-
Quaternion.LookRotation(bodyTarget - cameraPos));
186+
Quaternion.LookRotation(bodyCenter - cameraPos));
203187

204-
// Reposition display relative to frustum so it stays in the same screen-relative spot
205-
if (displayGO != null && cachedDriver != null && cachedDriver.Camera != null)
188+
if (displayGO != null && parentOfUIGO != null && cachedDriver != null && cachedDriver.Camera != null)
206189
{
207190
Camera cam = cachedDriver.Camera;
208191
cam.CalculateFrustumCorners(new Rect(0, 0, 1, 1), 1f, Camera.MonoOrStereoscopicEye.Left, frustumCorners);
209192
float halfW = (frustumCorners[2] - frustumCorners[1]).magnitude * 0.5f;
210193
float halfH = (frustumCorners[1] - frustumCorners[0]).magnitude * 0.5f;
211-
displayGO.transform.localPosition = new Vector3(
212-
DisplayNormalizedOffset.x * halfW,
213-
DisplayNormalizedOffset.y * halfH,
214-
0f);
194+
195+
Vector3 viewportPos = BasisDeviceManagement.IsMobileHardware()
196+
? cachedDriver.MobileMicrophoneViewportPosition
197+
: cachedDriver.DesktopMicrophoneViewportPosition;
198+
viewportPos.x = 1f - viewportPos.x;
199+
Vector3 parentWorld = cam.ViewportToWorldPoint(viewportPos);
200+
parentOfUIGO.transform.localPosition = cachedDriver.transform.InverseTransformPoint(parentWorld);
201+
202+
float aspect = (float)TextureWidth / (float)TextureHeight;
203+
float displayHeight = halfH;
204+
float displayWidth = displayHeight * aspect;
205+
displayGO.transform.localScale = new Vector3(displayWidth, displayHeight, 1f);
206+
207+
Vector3 displayCameraLocal = new Vector3(halfW - displayWidth * 0.5f, -halfH * 0.5f, 1f);
208+
Vector3 displayWorld = cam.transform.TransformPoint(displayCameraLocal);
209+
Vector3 displayLocal = cachedDriver.transform.InverseTransformPoint(displayWorld);
210+
displayGO.transform.localPosition = displayLocal - parentOfUIGO.transform.localPosition;
215211
}
216212
}
217213

0 commit comments

Comments
 (0)