Added Unity project files

This commit is contained in:
2026-06-07 16:58:24 +01:00
parent 3cc05d260b
commit 23bbcab156
3942 changed files with 453676 additions and 0 deletions

View File

@ -0,0 +1,218 @@

using UnityEngine;
using VRC.SDK3.ClientSim.PlayerTracking;
using VRC.Udon.Common;
namespace VRC.SDK3.ClientSim
{
// Listens to Events:
// - ClientSimMouseReleasedEvent
// - ClientSimOnPlayerEnteredStationEvent
// - ClientSimOnPlayerExitedStationEvent
// Listens to Input Events:
// - ToggleCrouch
// - ToggleProne
[AddComponentMenu("")]
public class ClientSimDesktopTrackingProvider : ClientSimTrackingProviderBase
{
[SerializeField]
private Transform playerXRotationBase;
[SerializeField]
private Transform playerYRotationBase;
private bool _mouseReleased = false;
private ClientSimDesktopTrackingRotator _desktopRotator;
private IClientSimStation _currentStation;
public override void Initialize(
IClientSimEventDispatcher eventDispatcher,
IClientSimInput input,
ClientSimSettings settings,
IClientSimPlayerHeightManager heightManager)
{
base.Initialize(eventDispatcher, input, settings, heightManager);
SetTrackingItemPositions();
_desktopRotator = new ClientSimDesktopTrackingRotator(playerXRotationBase, playerYRotationBase);
}
private void SetTrackingItemPositions()
{
head.localPosition = new Vector3(0, STANDING_HEIGHT, .1f);
head.localRotation = Quaternion.identity;
rightHand.localPosition = new Vector3(0.15f, -0.13f, 0.4f);
rightHand.localRotation = Quaternion.Euler(-35, 0, -90);
leftHand.localPosition = new Vector3(-0.15f, -0.13f, 0.4f);
leftHand.localRotation = Quaternion.Euler(-35, 0, -90);
}
#region IClientSimInputEventSubscribable
public override void SubscribeInputEvents()
{
base.SubscribeInputEvents();
input.SubscribeToggleCrouch(ToggleCrouchInput);
input.SubscribeToggleProne(ToggleProneInput);
}
public override void UnsubscribeInputEvents()
{
base.UnsubscribeInputEvents();
input.UnsubscribeToggleCrouch(ToggleCrouchInput);
input.UnsubscribeToggleProne(ToggleProneInput);
}
#endregion
#region IClientSimInputEventSubscribable
public override void SubscribeEvents()
{
base.SubscribeEvents();
eventDispatcher.Subscribe<ClientSimMouseReleasedEvent>(MouseReleasedEvent);
eventDispatcher.Subscribe<ClientSimOnPlayerEnteredStationEvent>(PlayerEnteredStation);
eventDispatcher.Subscribe<ClientSimOnPlayerExitedStationEvent>(PlayerExitedStation);
}
public override void UnsubscribeEvents()
{
base.UnsubscribeEvents();
eventDispatcher.Unsubscribe<ClientSimMouseReleasedEvent>(MouseReleasedEvent);
eventDispatcher.Unsubscribe<ClientSimOnPlayerEnteredStationEvent>(PlayerEnteredStation);
eventDispatcher.Unsubscribe<ClientSimOnPlayerExitedStationEvent>(PlayerExitedStation);
}
#endregion
#region ClientSim Events
private void MouseReleasedEvent(ClientSimMouseReleasedEvent mouseReleasedEvent)
{
_mouseReleased = mouseReleasedEvent.isReleased;
}
private void PlayerEnteredStation(ClientSimOnPlayerEnteredStationEvent stationEvent)
{
_currentStation = stationEvent.station;
_desktopRotator.SetStation(_currentStation);
if (_currentStation.IsSeated())
{
SetStance(ClientSimPlayerStanceEnum.SITTING);
}
}
private void PlayerExitedStation(ClientSimOnPlayerExitedStationEvent stationEvent)
{
_currentStation = null;
_desktopRotator.SetStation(null);
SetStance(ClientSimPlayerStanceEnum.STANDING);
}
#endregion
#region ClientSim Input
private void ToggleCrouchInput(bool value)
{
// Only handle on down, and not on release.
if (!value)
{
return;
}
if (GetPlayerStance() == ClientSimPlayerStanceEnum.CROUCHING)
{
SetStance(ClientSimPlayerStanceEnum.STANDING);
}
else
{
SetStance(ClientSimPlayerStanceEnum.CROUCHING);
}
}
private void ToggleProneInput(bool value)
{
// Only handle on down, and not on release.
if (!value)
{
return;
}
if (GetPlayerStance() == ClientSimPlayerStanceEnum.PRONE)
{
SetStance(ClientSimPlayerStanceEnum.STANDING);
}
else
{
SetStance(ClientSimPlayerStanceEnum.PRONE);
}
}
#endregion
private void Update()
{
// If mouse is released, do not update rotation.
if (!_mouseReleased)
{
_desktopRotator.HandleRotation(input.GetLookHorizontal(), input.GetLookVertical());
}
}
private void SetStance(ClientSimPlayerStanceEnum stance)
{
// If in a station, ignore all non sitting stances.
if (_currentStation != null && _currentStation.IsLockedInStation() && stance != ClientSimPlayerStanceEnum.SITTING)
{
return;
}
Vector3 cameraPosition = head.localPosition;
switch (stance)
{
case ClientSimPlayerStanceEnum.PRONE:
cameraPosition.y = PRONE_HEIGHT;
break;
case ClientSimPlayerStanceEnum.CROUCHING:
cameraPosition.y = CROUCHING_HEIGHT;
break;
case ClientSimPlayerStanceEnum.SITTING:
cameraPosition.y = SITTING_HEIGHT;
break;
case ClientSimPlayerStanceEnum.STANDING:
cameraPosition.y = STANDING_HEIGHT;
break;
}
head.localPosition = cameraPosition;
}
public override Transform GetHandRaycastTransform(HandType handType)
{
throw new ClientSimException("Desktop tracking does not support arm based raycasting");
}
public override bool IsVR()
{
return false;
}
public override bool SupportsPickupAutoHold()
{
return true;
}
public override void LookTowardsPoint(Vector3 point)
{
_desktopRotator.LookAtPoint(point);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5cdb19253bd15ee4296b716139111626
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,83 @@
using UnityEngine;
namespace VRC.SDK3.ClientSim.PlayerTracking
{
public class ClientSimDesktopTrackingRotator
{
private const float MINIMUM_X_ANGLE = -80f;
private const float MAXIMUM_X_ANGLE = 70f;
private const float MINIMUM_Y_ANGLE = -90F;
private const float MAXIMUM_Y_ANGLE = 90F;
private Quaternion _targetBaseYRot;
private Quaternion _targetHeadXRot;
private readonly Transform _headXTransform;
private readonly Transform _headYTransform;
private IClientSimStation _currentStation;
public ClientSimDesktopTrackingRotator(Transform headXTransform, Transform headYTransform)
{
_headXTransform = headXTransform;
_headYTransform = headYTransform;
_targetBaseYRot = _headYTransform.localRotation;
_targetHeadXRot = _headXTransform.localRotation;
}
public void SetStation(IClientSimStation station)
{
_currentStation = station;
_targetBaseYRot = Quaternion.identity;
}
public void HandleRotation(float xDelta, float yDelta)
{
_targetHeadXRot *= Quaternion.Euler(-yDelta, 0f, 0f);
_targetHeadXRot = ClampRotationAroundAxis(_targetHeadXRot, 0, MINIMUM_X_ANGLE, MAXIMUM_X_ANGLE);
// Player in a station, allow limited rotation on Y axis.
if (_currentStation != null && _currentStation.IsLockedInStation())
{
_targetBaseYRot *= Quaternion.Euler(0f, xDelta, 0f);
_targetBaseYRot = ClampRotationAroundAxis(_targetBaseYRot, 1, MINIMUM_Y_ANGLE, MAXIMUM_Y_ANGLE);
}
_headXTransform.localRotation = _targetHeadXRot;
_headYTransform.localRotation = _targetBaseYRot;
}
// Used in tests to force rotate the player to look at an object.
public void LookAtPoint(Vector3 point)
{
// Get the amount the player needs to rotate on Y
Vector3 localizedYPoint = _headYTransform.InverseTransformPoint(point);
localizedYPoint.y = 0;
float yAngle = Vector3.SignedAngle(Vector3.forward, localizedYPoint, Vector3.up);
// Get the amount the player needs to rotate on X
Vector3 localizedXPoint = _headXTransform.InverseTransformPoint(point);
localizedXPoint.x = 0;
float xAngle = Vector3.SignedAngle(Vector3.forward, localizedXPoint, Vector3.left);
HandleRotation(yAngle, xAngle);
}
private static Quaternion ClampRotationAroundAxis(Quaternion q, int index, float minAngle, float maxAngle)
{
q.x /= q.w;
q.y /= q.w;
q.z /= q.w;
q.w = 1.0f;
float angle = 2.0f * Mathf.Rad2Deg * Mathf.Atan(q[index]);
angle = Mathf.Clamp(angle, minAngle, maxAngle);
q[index] = Mathf.Tan(0.5f * Mathf.Deg2Rad * angle);
return q;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4416d1ef28048a94683820481afedf33
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,10 @@
namespace VRC.SDK3.ClientSim
{
public enum ClientSimPlayerStanceEnum
{
STANDING,
CROUCHING,
PRONE,
SITTING
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 062e0aeaac1ea1e4b9c22f6b612b3163
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,270 @@

using System;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon.Common;
namespace VRC.SDK3.ClientSim
{
/// <summary>
/// System responsible for providing tracking data to the rest of ClientSim.
/// </summary>
/// <remarks>
/// Currently only used for DesktopTracking, but can be extended for VR and even fake VR implementations.
/// Sends Events:
/// - ClientSimOnPlayerHeightUpdateEvent
/// - ClientSimOnTrackingScaleUpdateEvent
/// Listens to Events:
/// - ClientSimOnPlayerHeightUpdateEvent
/// </remarks>
[DefaultExecutionOrder(-3000)] // Update before player raycasting
public abstract class ClientSimTrackingProviderBase : ClientSimBehaviour, IClientSimTrackingProvider, IDisposable
{
private const float MINIMUM_TRACKING_SCALE = 0.1f;
private const float MAXIMUM_TRACKING_SCALE = 50f;
// TODO calculate this value based on the avatar instead of hard coding it.
public const float AVATAR_HEIGHT = 1.9f;
protected const float STANDING_HEIGHT = 1.75f;
protected const float CROUCHING_HEIGHT = 1.0f;
protected const float PRONE_HEIGHT = 0.5f;
protected const float SITTING_HEIGHT = 1.2f;
[SerializeField]
protected Transform head;
[SerializeField]
protected Transform leftHand;
[SerializeField]
protected Transform rightHand;
[SerializeField]
protected Transform playspace;
[SerializeField]
protected Transform playerAudioListener;
[SerializeField]
protected Camera playerCamera;
protected IClientSimEventDispatcher eventDispatcher;
protected IClientSimInput input;
protected ClientSimSettings settings;
protected IClientSimPlayerHeightManager heightManager;
private float _trackingScale = 1;
public static float CalculateTrackingScaleFromPlayerHeight(float playerHeight)
{
return playerHeight / AVATAR_HEIGHT;
}
public static float CalculatePlayerHeightFromTrackingScale(float trackingScale)
{
return trackingScale * AVATAR_HEIGHT;
}
public virtual void Initialize(
IClientSimEventDispatcher eventDispatcher,
IClientSimInput input,
ClientSimSettings settings,
IClientSimPlayerHeightManager heightManager)
{
this.eventDispatcher = eventDispatcher;
this.input = input;
this.settings = settings;
this.heightManager = heightManager;
SubscribeEvents();
// Input will be null with incorrect Unity input project settings.
if (input != null)
{
SubscribeInputEvents();
}
}
protected virtual void Start()
{
// Send event for this to ensure everything that uses the player height is properly updated.
eventDispatcher.SendEvent(new ClientSimOnPlayerHeightUpdateEvent
{
playerHeight = heightManager.GetAvatarEyeHeightAsMeters()
});
// Only disable audio listeners and cameras if the player is spawned.
if (settings.spawnPlayer)
{
// Destroy other audio listeners
foreach (var listener in FindObjectsByType<AudioListener>(FindObjectsSortMode.None))
{
if (listener.transform == playerAudioListener)
{
continue;
}
DestroyImmediate(listener);
}
// Disable all cameras that do not render to a render texture.
foreach (var worldCamera in FindObjectsByType<Camera>(FindObjectsSortMode.None))
{
if (worldCamera == playerCamera)
{
continue;
}
if (worldCamera.targetTexture != null)
{
continue;
}
worldCamera.enabled = false;
}
}
}
protected virtual void OnDestroy()
{
Dispose();
}
public void Dispose()
{
UnsubscribeEvents();
// Input will be null with incorrect Unity input project settings.
if (input != null)
{
UnsubscribeInputEvents();
}
}
#region IClientSimTrackingProvider
public virtual VRCPlayerApi.TrackingData GetTrackingData(VRCPlayerApi.TrackingDataType trackingDataType)
{
VRCPlayerApi.TrackingData data = new VRCPlayerApi.TrackingData();
switch (trackingDataType)
{
case VRCPlayerApi.TrackingDataType.Head:
data.position = head.position;
data.rotation = head.rotation;
break;
case VRCPlayerApi.TrackingDataType.LeftHand:
data.position = leftHand.position;
data.rotation = leftHand.rotation;
break;
case VRCPlayerApi.TrackingDataType.RightHand:
data.position = rightHand.position;
data.rotation = rightHand.rotation;
break;
case VRCPlayerApi.TrackingDataType.Origin:
data.position = playspace.position;
data.rotation = playspace.rotation;
break;
}
return data;
}
public virtual Transform GetTrackingTransform(VRCPlayerApi.TrackingDataType trackingDataType)
{
switch (trackingDataType)
{
case VRCPlayerApi.TrackingDataType.Head:
return head;
case VRCPlayerApi.TrackingDataType.LeftHand:
return leftHand;
case VRCPlayerApi.TrackingDataType.RightHand:
return rightHand;
case VRCPlayerApi.TrackingDataType.Origin:
return playspace;
}
return null;
}
public float GetTrackingScale()
{
return _trackingScale;
}
public void SetTrackingScale(float scale)
{
scale = Mathf.Clamp(scale, MINIMUM_TRACKING_SCALE, MAXIMUM_TRACKING_SCALE);
_trackingScale = scale;
playspace.localScale = scale * Vector3.one;
// Audio listener must always be scale 1 to ensure ONSP sounds correctly.
playerAudioListener.localScale = 1.0f / scale * Vector3.one;
eventDispatcher.SendEvent(new ClientSimOnTrackingScaleUpdateEvent { trackingScale = scale });
}
public ClientSimPlayerStanceEnum GetPlayerStance()
{
// Check heights starting from shortest
float headHeight = head.localPosition.y;
if (headHeight <= PRONE_HEIGHT)
{
return ClientSimPlayerStanceEnum.PRONE;
}
if (headHeight <= CROUCHING_HEIGHT)
{
return ClientSimPlayerStanceEnum.CROUCHING;
}
return ClientSimPlayerStanceEnum.STANDING;
}
public abstract Transform GetHandRaycastTransform(HandType handType);
public abstract bool IsVR();
public abstract bool SupportsPickupAutoHold();
public abstract void LookTowardsPoint(Vector3 point);
#endregion
#region IClientSimPlayerCameraProvider
public Camera GetCamera()
{
return playerCamera;
}
public Camera GetCameraForObject(GameObject obj)
{
// TODO: Make this interact with camera stacking
return playerCamera;
}
#endregion
#region ClientSim Events
private void OnPlayerHeightUpdate(ClientSimOnPlayerHeightUpdateEvent heightEvent)
{
// Convert player height to tracking scale and set the new scale.
SetTrackingScale(CalculateTrackingScaleFromPlayerHeight(heightEvent.playerHeight));
}
#endregion
public virtual void SubscribeInputEvents() { }
public virtual void UnsubscribeInputEvents() { }
public virtual void SubscribeEvents()
{
eventDispatcher.Subscribe<ClientSimOnPlayerHeightUpdateEvent>(OnPlayerHeightUpdate);
}
public virtual void UnsubscribeEvents()
{
eventDispatcher.Unsubscribe<ClientSimOnPlayerHeightUpdateEvent>(OnPlayerHeightUpdate);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3d6fc3f30fc48c247855805690cd90cc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: