Files
2026-06-07 16:58:24 +01:00

125 lines
4.6 KiB
C#

using UnityEngine;
namespace VRC.SDK3.ClientSim
{
// This class is used for when an object has no usable mesh renderers to highlight. This will copy the collider on
// the interact instead of any mesh on or under the interact.
// An object will not have usable colliders if:
// - There are no mesh renderers on or as a child of the object.
// - The mesh renderers do not have a mesh
// - The mesh renderers are part of a combined or static batched mesh.
[AddComponentMenu("")]
public class ClientSimHighlightProxy : ClientSimBehaviour
{
public Renderer Renderer { get; private set; }
private Mesh _cubeMesh;
private Mesh _capsuleMesh;
private Mesh _sphereMesh;
private MeshFilter _filter;
private Transform _proxyObject;
private Collider _proxyCollider;
protected override void Awake()
{
base.Awake();
Renderer = GetComponent<Renderer>();
_filter = GetComponent<MeshFilter>();
}
public void Initialize(Mesh cubeMesh, Mesh capsuleMesh, Mesh sphereMesh)
{
_cubeMesh = cubeMesh;
_capsuleMesh = capsuleMesh;
_sphereMesh = sphereMesh;
}
private void OnWillRenderObject()
{
UpdatePosition();
}
private void UpdatePosition()
{
Vector3 position = _proxyObject.position;
Quaternion rotation = _proxyObject.rotation;
Vector3 scale = _proxyObject.lossyScale;
if (_proxyCollider is BoxCollider boxCollider)
{
position += rotation * Vector3.Scale(boxCollider.center, scale);
scale = Vector3.Scale(scale, boxCollider.size);
_filter.sharedMesh = _cubeMesh;
}
else if (_proxyCollider is SphereCollider sphereCollider)
{
position += rotation * Vector3.Scale(sphereCollider.center, scale);
// VRChatBug: Unity will always keep spheres round, but VRChat ignores this for the proxy rendering.
// Sphere colliders are always round. Get the max component as the sphere size.
float sphereScale = sphereCollider.radius * 2 * Mathf.Max(scale.x, scale.y, scale.z);
scale = sphereScale * Vector3.one;
_filter.sharedMesh = _sphereMesh;
}
else if (_proxyCollider is CapsuleCollider capsuleCollider)
{
position += rotation * Vector3.Scale(capsuleCollider.center, scale);
float height = capsuleCollider.height * 0.5f;
float radius = capsuleCollider.radius * 2;
float sphereScale = radius;
switch (capsuleCollider.direction)
{
case 0: // X
rotation *= Quaternion.Euler(0, 0, 90);
sphereScale = Mathf.Max(scale.y, scale.z) * radius;
height *= scale.x;
break;
case 1: // Y
// No need to rotate since mesh is already along y axis.
sphereScale = Mathf.Max(scale.x, scale.z) * radius;
height *= scale.y;
break;
case 2: // Z
rotation *= Quaternion.Euler(90, 0, 0);
sphereScale = Mathf.Max(scale.y, scale.x) * radius;
height *= scale.z;
break;
}
height = Mathf.Max(sphereScale * 0.5f, height);
scale = new Vector3(sphereScale, height, sphereScale);
_filter.sharedMesh = _capsuleMesh;
}
else if (_proxyCollider is MeshCollider meshCollider)
{
_filter.sharedMesh = meshCollider.sharedMesh;
}
transform.SetPositionAndRotation(position, rotation);
transform.localScale = scale;
}
public void EnableProxy(Transform obj, Collider proxyCollider)
{
gameObject.SetActive(true);
_filter.sharedMesh = null;
_proxyObject = obj;
_proxyCollider = proxyCollider;
UpdatePosition();
}
public void DisableProxy()
{
gameObject.SetActive(false);
_filter.sharedMesh = null;
_proxyObject = null;
_proxyCollider = null;
}
}
}