#if UNITY_EDITOR using UnityEngine; using VRC.Dynamics; namespace VRC.SDK3.Dynamics.PhysBone { /// /// Internal component that makes it possible to grab and pose PhysBones for testing in an SDK project while it's /// in play mode. /// // This component is intentionally duplicated across SDKs instead of existing in the base SDK only, to address // compatibility issues with community made tooling. [AddComponentMenu("")] public class PhysBoneGrabHelper : MonoBehaviour { Camera currentCamera; void Update() { currentCamera = FindCamera(); //Process mouse input SetMouseDown(Input.GetMouseButton(0)); if (mouseIsDown && Input.GetMouseButtonDown(1)) { if(grab != null) { PhysBoneManager.Inst.ReleaseGrab(grab, true); grab = null; } } UpdateGrab(); } Camera FindCamera() { //Get target display int targetDisplay = Display.activeEditorGameViewTarget; //Find camera with matching target Camera result = null; var cameras = Camera.allCameras; foreach(var camera in cameras) //Get the last active camera { if (camera.isActiveAndEnabled && camera.cameraType == CameraType.Game && camera.targetDisplay == targetDisplay && camera.targetTexture == null ) result = camera; } return result; } bool mouseIsDown = false; PhysBoneManager.Grab grab; Vector3 grabOriginLocal; void SetMouseDown(bool state) { if (state == mouseIsDown) return; mouseIsDown = state; if (mouseIsDown && currentCamera != null) { var localPlayer = VRC.SDKBase.Networking.LocalPlayer; var playerId = localPlayer != null && localPlayer.IsValid() ? localPlayer.playerId : -1; var ray = GetMouseRay(); grab = PhysBoneManager.Inst.AttemptGrab(playerId, ray, out Vector3 grabOrigin); if (grab != null) { grabOriginLocal = currentCamera.transform.InverseTransformPoint(grabOrigin); #if VERBOSE_LOGGING Debug.Log($"Grabbing - Chain:{grab.chainId} Bone:{grab.bone}"); #endif } } else { if (grab != null) { PhysBoneManager.Inst.ReleaseGrab(grab); grab = null; } } } Ray GetMouseRay() { return currentCamera.ScreenPointToRay(Input.mousePosition); } void UpdateGrab() { if(currentCamera == null) return; if (grab != null) { Vector3 curGrabOrigin = currentCamera.transform.TransformPoint(grabOriginLocal); var ray = GetMouseRay(); Vector3 hit; if (PlaneLineIntersection(curGrabOrigin, -currentCamera.transform.forward, ray.origin, ray.origin + ray.direction * 1000f, out hit)) { grab.GlobalPosition = hit + grab.LocalOffset; } } } public static bool PlaneLineIntersection(Vector3 planeOrigin, Vector3 planeNormal, Vector3 lineA, Vector3 lineB, out Vector3 hit) { float delta; //Make sure the line is not parallel delta = Vector3.Dot(planeNormal, (lineB - lineA) - planeOrigin); if (delta == 0.0f) { hit = Vector3.zero; return false; } //Find the delta delta = Vector3.Dot(planeNormal, lineA - planeOrigin) / delta; hit = lineA + ((lineB - lineA) * -delta); return true; } } } #endif