https://www.reddit.com/r/Vive/wiki/skins
//======= Copyright (c) Valve Corporation, All rights reserved. =============== // // Purpose: Render model of associated tracked object // //============================================================================= using UnityEngine; using System.Collections; using System.Runtime.InteropServices; using Valve.VR; [ExecuteInEditMode] public class SteamVR_RenderModel : MonoBehaviour { public SteamVR_TrackedObject.EIndex index = SteamVR_TrackedObject.EIndex.None; public string modelOverride; // Shader to apply to model. public Shader shader; // Enable to print out when render models are loaded. public bool verbose = false; // If available, break down into separate components instead of loading as a single mesh. public bool createComponents = true; // Update transforms of components at runtime to reflect user action. public bool updateDynamically = true; // Additional controller settings for showing scrollwheel, etc. public RenderModel_ControllerMode_State_t controllerModeState; // Name of the sub-object which represents the "local" coordinate space for each component. public const string k_localTransformName = "attach"; // Cached name of this render model for updating component transforms at runtime. public string renderModelName { get; private set; } // If someone knows how to keep these from getting cleaned up every time // you exit play mode, let me know. I've tried marking the RenderModel // class below as [System.Serializable] and switching to normal public // variables for mesh and material to get them to serialize properly, // as well as tried marking the mesh and material objects as // DontUnloadUnusedAsset, but Unity was still unloading them. // The hashtable is preserving its entries, but the mesh and material // variables are going null. public class RenderModel { public RenderModel(Mesh mesh, Material material) { this.mesh = mesh; this.material = material; } public Mesh mesh { get; private set; } public Material material { get; private set; } } public static Hashtable models = new Hashtable(); public static Hashtable materials = new Hashtable(); // Helper class to load render models interface on demand and clean up when done. public sealed class RenderModelInterfaceHolder : System.IDisposable { private bool needsShutdown, failedLoadInterface; private CVRRenderModels _instance; public CVRRenderModels instance { get { if (_instance == null && !failedLoadInterface) { if (!SteamVR.active && !SteamVR.usingNativeSupport) { var error = EVRInitError.None; OpenVR.Init(ref error, EVRApplicationType.VRApplication_Other); needsShutdown = true; } _instance = OpenVR.RenderModels; if (_instance == null) { Debug.LogError("Failed to load IVRRenderModels interface version " + OpenVR.IVRRenderModels_Version); failedLoadInterface = true; } } return _instance; } } public void Dispose() { if (needsShutdown) OpenVR.Shutdown(); } } private void OnModelSkinSettingsHaveChanged(params object[] args) { if (!string.IsNullOrEmpty(renderModelName)) { renderModelName = ""; UpdateModel(); } } private void OnHideRenderModels(params object[] args) { bool hidden = (bool)args[0]; var meshRenderer = GetComponent<MeshRenderer>(); if (meshRenderer != null) meshRenderer.enabled = !hidden; foreach (var child in transform.GetComponentsInChildren<MeshRenderer>()) child.enabled = !hidden; } private void OnDeviceConnected(params object[] args) { var i = (int)args[0]; if (i != (int)index) return; var connected = (bool)args[1]; if (connected) { UpdateModel(); } } public void UpdateModel() { var system = OpenVR.System; if (system == null) return; var error = ETrackedPropertyError.TrackedProp_Success; var capacity = system.GetStringTrackedDeviceProperty((uint)index, ETrackedDeviceProperty.Prop_RenderModelName_String, null, 0, ref error); if (capacity <= 1) { Debug.LogError("Failed to get render model name for tracked object " + index); return; } var buffer = new System.Text.StringBuilder((int)capacity); system.GetStringTrackedDeviceProperty((uint)index, ETrackedDeviceProperty.Prop_RenderModelName_String, buffer, capacity, ref error); var s = buffer.ToString(); if (renderModelName != s) { renderModelName = s; StartCoroutine(SetModelAsync(s)); } } IEnumerator SetModelAsync(string renderModelName) { if (string.IsNullOrEmpty(renderModelName)) yield break; // Preload all render models before asking for the data to create meshes. using (var holder = new RenderModelInterfaceHolder()) { var renderModels = holder.instance; if (renderModels == null) yield break; // Gather names of render models to preload. string[] renderModelNames; var count = renderModels.GetComponentCount(renderModelName); if (count > 0) { renderModelNames = new string[count]; for (int i = 0; i < count; i++) { var capacity = renderModels.GetComponentName(renderModelName, (uint)i, null, 0); if (capacity == 0) continue; var componentName = new System.Text.StringBuilder((int)capacity); if (renderModels.GetComponentName(renderModelName, (uint)i, componentName, capacity) == 0) continue; capacity = renderModels.GetComponentRenderModelName(renderModelName, componentName.ToString(), null, 0); if (capacity == 0) continue; var name = new System.Text.StringBuilder((int)capacity); if (renderModels.GetComponentRenderModelName(renderModelName, componentName.ToString(), name, capacity) == 0) continue; var s = name.ToString(); // Only need to preload if not already cached. var model = models[s] as RenderModel; if (model == null || model.mesh == null) { renderModelNames[i] = s; } } } else { // Only need to preload if not already cached. var model = models[renderModelName] as RenderModel; if (model == null || model.mesh == null) { renderModelNames = new string[] { renderModelName }; } else { renderModelNames = new string[0]; } } // Keep trying every 100ms until all components finish loading. while (true) { var loading = false; foreach (var name in renderModelNames) { if (string.IsNullOrEmpty(name)) continue; var pRenderModel = System.IntPtr.Zero; var error = renderModels.LoadRenderModel_Async(name, ref pRenderModel); if (error == EVRRenderModelError.Loading) { loading = true; } else if (error == EVRRenderModelError.None) { // Preload textures as well. var renderModel = (RenderModel_t)Marshal.PtrToStructure(pRenderModel, typeof(RenderModel_t)); // Check the cache first. var material = materials[renderModel.diffuseTextureId] as Material; if (material == null || material.mainTexture == null) { var pDiffuseTexture = System.IntPtr.Zero; error = renderModels.LoadTexture_Async(renderModel.diffuseTextureId, ref pDiffuseTexture); if (error == EVRRenderModelError.Loading) { loading = true; } } } } if (loading) { yield return new WaitForSeconds(0.1f); } else { break; } } } bool success = SetModel(renderModelName); SteamVR_Utils.Event.Send("render_model_loaded", this, success); } private bool SetModel(string renderModelName) { StripMesh(gameObject); using (var holder = new RenderModelInterfaceHolder()) { if (createComponents) { if (LoadComponents(holder, renderModelName)) { UpdateComponents(); return true; } Debug.Log("[" + gameObject.name + "] Render model does not support components, falling back to single mesh."); } if (!string.IsNullOrEmpty(renderModelName)) { var model = models[renderModelName] as RenderModel; if (model == null || model.mesh == null) { var renderModels = holder.instance; if (renderModels == null) return false; if (verbose) Debug.Log("Loading render model " + renderModelName); model = LoadRenderModel(renderModels, renderModelName, renderModelName); if (model == null) return false; models[renderModelName] = model; } gameObject.AddComponent<MeshFilter>().mesh = model.mesh; gameObject.AddComponent<MeshRenderer>().sharedMaterial = model.material; return true; } } return false; } RenderModel LoadRenderModel(CVRRenderModels renderModels, string renderModelName, string baseName) { var pRenderModel = System.IntPtr.Zero; EVRRenderModelError error; while ( true ) { error = renderModels.LoadRenderModel_Async(renderModelName, ref pRenderModel); if (error != EVRRenderModelError.Loading) break; System.Threading.Thread.Sleep(1); } if (error != EVRRenderModelError.None) { Debug.LogError(string.Format("Failed to load render model {0} - {1}", renderModelName, error.ToString())); return null; } var renderModel = (RenderModel_t)Marshal.PtrToStructure(pRenderModel, typeof(RenderModel_t)); var vertices = new Vector3[renderModel.unVertexCount]; var normals = new Vector3[renderModel.unVertexCount]; var uv = new Vector2[renderModel.unVertexCount]; var type = typeof(RenderModel_Vertex_t); for (int iVert = 0; iVert < renderModel.unVertexCount; iVert++) { var ptr = new System.IntPtr(renderModel.rVertexData.ToInt64() + iVert * Marshal.SizeOf(type)); var vert = (RenderModel_Vertex_t)Marshal.PtrToStructure(ptr, type); vertices[iVert] = new Vector3(vert.vPosition.v0, vert.vPosition.v1, -vert.vPosition.v2); normals[iVert] = new Vector3(vert.vNormal.v0, vert.vNormal.v1, -vert.vNormal.v2); uv[iVert] = new Vector2(vert.rfTextureCoord0, vert.rfTextureCoord1); } int indexCount = (int)renderModel.unTriangleCount * 3; var indices = new short[indexCount]; Marshal.Copy(renderModel.rIndexData, indices, 0, indices.Length); var triangles = new int[indexCount]; for (int iTri = 0; iTri < renderModel.unTriangleCount; iTri++) { triangles[iTri * 3 + 0] = (int)indices[iTri * 3 + 2]; triangles[iTri * 3 + 1] = (int)indices[iTri * 3 + 1]; triangles[iTri * 3 + 2] = (int)indices[iTri * 3 + 0]; } var mesh = new Mesh(); mesh.vertices = vertices; mesh.normals = normals; mesh.uv = uv; mesh.triangles = triangles; mesh.Optimize(); //mesh.hideFlags = HideFlags.DontUnloadUnusedAsset; // Check cache before loading texture. var material = materials[renderModel.diffuseTextureId] as Material; if (material == null || material.mainTexture == null) { var pDiffuseTexture = System.IntPtr.Zero; while (true) { error = renderModels.LoadTexture_Async(renderModel.diffuseTextureId, ref pDiffuseTexture); if (error != EVRRenderModelError.Loading) break; System.Threading.Thread.Sleep(1); } if (error == EVRRenderModelError.None) { var diffuseTexture = (RenderModel_TextureMap_t)Marshal.PtrToStructure(pDiffuseTexture, typeof(RenderModel_TextureMap_t)); var texture = new Texture2D(diffuseTexture.unWidth, diffuseTexture.unHeight, TextureFormat.ARGB32, false); if (SystemInfo.graphicsDeviceVersion.StartsWith("OpenGL")) { var textureMapData = new byte[diffuseTexture.unWidth * diffuseTexture.unHeight * 4]; // RGBA Marshal.Copy(diffuseTexture.rubTextureMapData, textureMapData, 0, textureMapData.Length); var colors = new Color32[diffuseTexture.unWidth * diffuseTexture.unHeight]; int iColor = 0; for (int iHeight = 0; iHeight < diffuseTexture.unHeight; iHeight++) { for (int iWidth = 0; iWidth < diffuseTexture.unWidth; iWidth++) { var r = textureMapData[iColor++]; var g = textureMapData[iColor++]; var b = textureMapData[iColor++]; var a = textureMapData[iColor++]; colors[iHeight * diffuseTexture.unWidth + iWidth] = new Color32(r, g, b, a); } } texture.SetPixels32(colors); texture.Apply(); } else { texture.Apply(); while (true) { error = renderModels.LoadIntoTextureD3D11_Async(renderModel.diffuseTextureId, texture.GetNativeTexturePtr()); if (error != EVRRenderModelError.Loading) break; System.Threading.Thread.Sleep(1); } } material = new Material(shader != null ? shader : Shader.Find("Standard")); material.mainTexture = texture; //material.hideFlags = HideFlags.DontUnloadUnusedAsset; materials[renderModel.diffuseTextureId] = material; renderModels.FreeTexture(pDiffuseTexture); } else { Debug.Log("Failed to load render model texture for render model " + renderModelName); } } // Delay freeing when we can since we'll often get multiple requests for the same model right // after another (e.g. two controllers or two basestations). #if UNITY_EDITOR if (!Application.isPlaying) renderModels.FreeRenderModel(pRenderModel); else #endif StartCoroutine(FreeRenderModel(pRenderModel)); return new RenderModel(mesh, material); } IEnumerator FreeRenderModel(System.IntPtr pRenderModel) { yield return new WaitForSeconds(1.0f); using (var holder = new RenderModelInterfaceHolder()) { var renderModels = holder.instance; renderModels.FreeRenderModel(pRenderModel); } } public Transform FindComponent(string componentName) { var t = transform; for (int i = 0; i < t.childCount; i++) { var child = t.GetChild(i); if (child.name == componentName) return child; } return null; } private void StripMesh(GameObject go) { var meshRenderer = go.GetComponent<MeshRenderer>(); if (meshRenderer != null) DestroyImmediate(meshRenderer); var meshFilter = go.GetComponent<MeshFilter>(); if (meshFilter != null) DestroyImmediate(meshFilter); } private bool LoadComponents(RenderModelInterfaceHolder holder, string renderModelName) { // Disable existing components (we will re-enable them if referenced by this new model). // Also strip mesh filter and renderer since these will get re-added if the new component needs them. var t = transform; for (int i = 0; i < t.childCount; i++) { var child = t.GetChild(i); child.gameObject.SetActive(false); StripMesh(child.gameObject); } // If no model specified, we're done; return success. if (string.IsNullOrEmpty(renderModelName)) return true; var renderModels = holder.instance; if (renderModels == null) return false; var count = renderModels.GetComponentCount(renderModelName); if (count == 0) return false; for (int i = 0; i < count; i++) { var capacity = renderModels.GetComponentName(renderModelName, (uint)i, null, 0); if (capacity == 0) continue; var componentName = new System.Text.StringBuilder((int)capacity); if (renderModels.GetComponentName(renderModelName, (uint)i, componentName, capacity) == 0) continue; // Create (or reuse) a child object for this component (some components are dynamic and don't have meshes). t = FindComponent(componentName.ToString()); if (t != null) { t.gameObject.SetActive(true); } else { t = new GameObject(componentName.ToString()).transform; t.parent = transform; t.gameObject.layer = gameObject.layer; // Also create a child 'attach' object for attaching things. var attach = new GameObject(k_localTransformName).transform; attach.parent = t; attach.localPosition = Vector3.zero; attach.localRotation = Quaternion.identity; attach.localScale = Vector3.one; attach.gameObject.layer = gameObject.layer; } // Reset transform. t.localPosition = Vector3.zero; t.localRotation = Quaternion.identity; t.localScale = Vector3.one; capacity = renderModels.GetComponentRenderModelName(renderModelName, componentName.ToString(), null, 0); if (capacity == 0) continue; var componentRenderModelName = new System.Text.StringBuilder((int)capacity); if (renderModels.GetComponentRenderModelName(renderModelName, componentName.ToString(), componentRenderModelName, capacity) == 0) continue; // Check the cache or load into memory. var model = models[componentRenderModelName] as RenderModel; if (model == null || model.mesh == null) { if (verbose) Debug.Log("Loading render model " + componentRenderModelName); model = LoadRenderModel(renderModels, componentRenderModelName.ToString(), renderModelName); if (model == null) continue; models[componentRenderModelName] = model; } t.gameObject.AddComponent<MeshFilter>().mesh = model.mesh; t.gameObject.AddComponent<MeshRenderer>().sharedMaterial = model.material; } return true; } void OnEnable() { #if UNITY_EDITOR if (!Application.isPlaying) return; #endif if (!string.IsNullOrEmpty(modelOverride)) { Debug.Log("Model override is really only meant to be used in the scene view for lining things up; using it at runtime is discouraged. Use tracked device index instead to ensure the correct model is displayed for all users."); enabled = false; return; } var system = OpenVR.System; if (system != null && system.IsTrackedDeviceConnected((uint)index)) { UpdateModel(); } SteamVR_Utils.Event.Listen("device_connected", OnDeviceConnected); SteamVR_Utils.Event.Listen("hide_render_models", OnHideRenderModels); SteamVR_Utils.Event.Listen("ModelSkinSettingsHaveChanged", OnModelSkinSettingsHaveChanged); } void OnDisable() { #if UNITY_EDITOR if (!Application.isPlaying) return; #endif SteamVR_Utils.Event.Remove("device_connected", OnDeviceConnected); SteamVR_Utils.Event.Remove("hide_render_models", OnHideRenderModels); SteamVR_Utils.Event.Remove("ModelSkinSettingsHaveChanged", OnModelSkinSettingsHaveChanged); } #if UNITY_EDITOR Hashtable values; #endif void Update() { #if UNITY_EDITOR if (!Application.isPlaying) { // See if anything has changed since this gets called whenever anything gets touched. var fields = GetType().GetFields(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public); bool modified = false; if (values == null) { modified = true; } else { foreach (var f in fields) { if (!values.Contains(f)) { modified = true; break; } var v0 = values[f]; var v1 = f.GetValue(this); if (v1 != null) { if (!v1.Equals(v0)) { modified = true; break; } } else if (v0 != null) { modified = true; break; } } } if (modified) { if (renderModelName != modelOverride) { renderModelName = modelOverride; SetModel(modelOverride); } values = new Hashtable(); foreach (var f in fields) values[f] = f.GetValue(this); } return; // Do not update transforms (below) when not playing in Editor (to avoid keeping OpenVR running all the time). } #endif // Update component transforms dynamically. if (updateDynamically) UpdateComponents(); } public void UpdateComponents() { var t = transform; if (t.childCount == 0) return; using (var holder = new RenderModelInterfaceHolder()) { var controllerState = (index != SteamVR_TrackedObject.EIndex.None) ? SteamVR_Controller.Input((int)index).GetState() : new VRControllerState_t(); for (int i = 0; i < t.childCount; i++) { var child = t.GetChild(i); var renderModels = holder.instance; if (renderModels == null) break; var componentState = new RenderModel_ComponentState_t(); if (!renderModels.GetComponentState(renderModelName, child.name, ref controllerState, ref controllerModeState, ref componentState)) continue; var componentTransform = new SteamVR_Utils.RigidTransform(componentState.mTrackingToComponentRenderModel); child.localPosition = componentTransform.pos; child.localRotation = componentTransform.rot; var attach = child.FindChild(k_localTransformName); if (attach != null) { var attachTransform = new SteamVR_Utils.RigidTransform(componentState.mTrackingToComponentLocal); attach.position = t.TransformPoint(attachTransform.pos); attach.rotation = t.rotation * attachTransform.rot; } bool visible = (componentState.uProperties & (uint)EVRComponentProperty.IsVisible) != 0; if (visible != child.gameObject.activeSelf) { child.gameObject.SetActive(visible); } } } } public void SetDeviceIndex(int index) { this.index = (SteamVR_TrackedObject.EIndex)index; modelOverride = ""; if (enabled) { UpdateModel(); } } }
//======= Copyright (c) Valve Corporation, All rights reserved. =============== // // Purpose: For controlling in-game objects with tracked devices. // //============================================================================= using UnityEngine; using Valve.VR; public class SteamVR_TrackedObject : MonoBehaviour { public enum EIndex { None = -1, Hmd = (int)OpenVR.k_unTrackedDeviceIndex_Hmd, Device1, Device2, Device3, Device4, Device5, Device6, Device7, Device8, Device9, Device10, Device11, Device12, Device13, Device14, Device15 } public EIndex index; public Transform origin; // if not set, relative to parent public bool isValid = false; private void OnNewPoses(params object[] args) { if (index == EIndex.None) return; var i = (int)index; isValid = false; var poses = (Valve.VR.TrackedDevicePose_t[])args[0]; if (poses.Length <= i) return; if (!poses[i].bDeviceIsConnected) return; if (!poses[i].bPoseIsValid) return; isValid = true; var pose = new SteamVR_Utils.RigidTransform(poses[i].mDeviceToAbsoluteTracking); if (origin != null) { pose = new SteamVR_Utils.RigidTransform(origin) * pose; pose.pos.x *= origin.localScale.x; pose.pos.y *= origin.localScale.y; pose.pos.z *= origin.localScale.z; transform.position = pose.pos; transform.rotation = pose.rot; } else { transform.localPosition = pose.pos; transform.localRotation = pose.rot; } } void OnEnable() { var render = SteamVR_Render.instance; if (render == null) { enabled = false; return; } SteamVR_Utils.Event.Listen("new_poses", OnNewPoses); } void OnDisable() { SteamVR_Utils.Event.Remove("new_poses", OnNewPoses); isValid = false; } public void SetDeviceIndex(int index) { if (System.Enum.IsDefined(typeof(EIndex), index)) this.index = (EIndex)index; } }
https://youtu.be/Gh33IkBLmxE
using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.UI; using System.Collections; using System.Collections.Generic; public class ViveControllerInput : BaseInputModule { public static ViveControllerInput Instance; [Header(" [Cursor setup]")] public Sprite CursorSprite; public Material CursorMaterial; public float NormalCursorScale = 0.00025f; [Space(10)] [Header(" [Runtime variables]")] [Tooltip("Indicates whether or not the gui was hit by any controller this frame")] public bool GuiHit; [Tooltip("Indicates whether or not a button was used this frame")] public bool ButtonUsed; [Tooltip("Generated cursors")] public RectTransform[] Cursors; private GameObject[] CurrentPoint; private GameObject[] CurrentPressed; private GameObject[] CurrentDragging; private PointerEventData[] PointEvents; private bool Initialized = false; [Tooltip("Generated non rendering camera (used for raycasting ui)")] public Camera ControllerCamera; private SteamVR_ControllerManager ControllerManager; private SteamVR_TrackedObject[] Controllers; private SteamVR_Controller.Device[] ControllerDevices; protected override void Start() { base.Start(); if (Initialized == false) { Instance = this; ControllerCamera = new GameObject("Controller UI Camera").AddComponent<Camera>(); ControllerCamera.clearFlags = CameraClearFlags.Nothing; //CameraClearFlags.Depth; ControllerCamera.cullingMask = 0; // 1 << LayerMask.NameToLayer("UI"); ControllerManager = GameObject.FindObjectOfType<SteamVR_ControllerManager>(); Controllers = new SteamVR_TrackedObject[] { ControllerManager.left.GetComponent<SteamVR_TrackedObject>(), ControllerManager.right.GetComponent<SteamVR_TrackedObject>() }; ControllerDevices = new SteamVR_Controller.Device[Controllers.Length]; Cursors = new RectTransform[Controllers.Length]; for (int index = 0; index < Cursors.Length; index++) { GameObject cursor = new GameObject("Cursor " + index); Canvas canvas = cursor.AddComponent<Canvas>(); cursor.AddComponent<CanvasRenderer>(); cursor.AddComponent<CanvasScaler>(); cursor.AddComponent<UIIgnoreRaycast>(); cursor.AddComponent<GraphicRaycaster>(); canvas.renderMode = RenderMode.WorldSpace; canvas.sortingOrder = 1000; //set to be on top of everything Image image = cursor.AddComponent<Image>(); image.sprite = CursorSprite; image.material = CursorMaterial; if (CursorSprite == null) Debug.LogError("Set CursorSprite on " + this.gameObject.name + " to the sprite you want to use as your cursor.", this.gameObject); Cursors[index] = cursor.GetComponent<RectTransform>(); } CurrentPoint = new GameObject[Cursors.Length]; CurrentPressed = new GameObject[Cursors.Length]; CurrentDragging = new GameObject[Cursors.Length]; PointEvents = new PointerEventData[Cursors.Length]; Canvas[] canvases = GameObject.FindObjectsOfType<Canvas>(); foreach (Canvas canvas in canvases) { canvas.worldCamera = ControllerCamera; } Initialized = true; } } // use screen midpoint as locked pointer location, enabling look location to be the "mouse" private bool GetLookPointerEventData(int index) { if (PointEvents[index] == null) PointEvents[index] = new PointerEventData(base.eventSystem); else PointEvents[index].Reset(); PointEvents[index].delta = Vector2.zero; PointEvents[index].position = new Vector2(Screen.width / 2, Screen.height / 2); PointEvents[index].scrollDelta = Vector2.zero; base.eventSystem.RaycastAll(PointEvents[index], m_RaycastResultCache); PointEvents[index].pointerCurrentRaycast = FindFirstRaycast(m_RaycastResultCache); if (PointEvents[index].pointerCurrentRaycast.gameObject != null) { GuiHit = true; //gets set to false at the beginning of the process event } m_RaycastResultCache.Clear(); return true; } // update the cursor location and whether it is enabled // this code is based on Unity's DragMe.cs code provided in the UI drag and drop example private void UpdateCursor(int index, PointerEventData pointData) { if (PointEvents[index].pointerCurrentRaycast.gameObject != null) { Cursors[index].gameObject.SetActive(true); if (pointData.pointerEnter != null) { RectTransform draggingPlane = pointData.pointerEnter.GetComponent<RectTransform>(); Vector3 globalLookPos; if (RectTransformUtility.ScreenPointToWorldPointInRectangle(draggingPlane, pointData.position, pointData.enterEventCamera, out globalLookPos)) { Cursors[index].position = globalLookPos; Cursors[index].rotation = draggingPlane.rotation; // scale cursor based on distance to camera float lookPointDistance = (Cursors[index].position - Camera.main.transform.position).magnitude; float cursorScale = lookPointDistance * NormalCursorScale; if (cursorScale < NormalCursorScale) { cursorScale = NormalCursorScale; } Cursors[index].localScale = Vector3.one * cursorScale; } } } else { Cursors[index].gameObject.SetActive(false); } } // clear the current selection public void ClearSelection() { if (base.eventSystem.currentSelectedGameObject) { base.eventSystem.SetSelectedGameObject(null); } } // select a game object private void Select(GameObject go) { ClearSelection(); if (ExecuteEvents.GetEventHandler<ISelectHandler>(go)) { base.eventSystem.SetSelectedGameObject(go); } } // send update event to selected object // needed for InputField to receive keyboard input private bool SendUpdateEventToSelectedObject() { if (base.eventSystem.currentSelectedGameObject == null) return false; BaseEventData data = GetBaseEventData(); ExecuteEvents.Execute(base.eventSystem.currentSelectedGameObject, data, ExecuteEvents.updateSelectedHandler); return data.used; } private void UpdateCameraPosition(int index) { ControllerCamera.transform.position = Controllers[index].transform.position; ControllerCamera.transform.forward = Controllers[index].transform.forward; } private void InitializeControllers() { for (int index = 0; index < Controllers.Length; index++) { if (Controllers[index] != null && Controllers[index].index != SteamVR_TrackedObject.EIndex.None) { ControllerDevices[index] = SteamVR_Controller.Input((int)Controllers[index].index); } else { ControllerDevices[index] = null; } } } // Process is called by UI system to process events public override void Process() { InitializeControllers(); GuiHit = false; ButtonUsed = false; // send update events if there is a selected object - this is important for InputField to receive keyboard events SendUpdateEventToSelectedObject(); // see if there is a UI element that is currently being looked at for (int index = 0; index < Cursors.Length; index++) { if (Controllers[index].gameObject.activeInHierarchy == false) { if (Cursors[index].gameObject.activeInHierarchy == true) { Cursors[index].gameObject.SetActive(false); } continue; } UpdateCameraPosition(index); bool hit = GetLookPointerEventData(index); if (hit == false) continue; CurrentPoint[index] = PointEvents[index].pointerCurrentRaycast.gameObject; // handle enter and exit events (highlight) base.HandlePointerExitAndEnter(PointEvents[index], CurrentPoint[index]); // update cursor UpdateCursor(index, PointEvents[index]); if (Controllers[index] != null) { if (ButtonDown(index)) { ClearSelection(); PointEvents[index].pressPosition = PointEvents[index].position; PointEvents[index].pointerPressRaycast = PointEvents[index].pointerCurrentRaycast; PointEvents[index].pointerPress = null; if (CurrentPoint[index] != null) { CurrentPressed[index] = CurrentPoint[index]; GameObject newPressed = ExecuteEvents.ExecuteHierarchy(CurrentPressed[index], PointEvents[index], ExecuteEvents.pointerDownHandler); if (newPressed == null) { // some UI elements might only have click handler and not pointer down handler newPressed = ExecuteEvents.ExecuteHierarchy(CurrentPressed[index], PointEvents[index], ExecuteEvents.pointerClickHandler); if (newPressed != null) { CurrentPressed[index] = newPressed; } } else { CurrentPressed[index] = newPressed; // we want to do click on button down at same time, unlike regular mouse processing // which does click when mouse goes up over same object it went down on // reason to do this is head tracking might be jittery and this makes it easier to click buttons ExecuteEvents.Execute(newPressed, PointEvents[index], ExecuteEvents.pointerClickHandler); } if (newPressed != null) { PointEvents[index].pointerPress = newPressed; CurrentPressed[index] = newPressed; Select(CurrentPressed[index]); ButtonUsed = true; } ExecuteEvents.Execute(CurrentPressed[index], PointEvents[index], ExecuteEvents.beginDragHandler); PointEvents[index].pointerDrag = CurrentPressed[index]; CurrentDragging[index] = CurrentPressed[index]; } } if (ButtonUp(index)) { if (CurrentDragging[index]) { ExecuteEvents.Execute(CurrentDragging[index], PointEvents[index], ExecuteEvents.endDragHandler); if (CurrentPoint[index] != null) { ExecuteEvents.ExecuteHierarchy(CurrentPoint[index], PointEvents[index], ExecuteEvents.dropHandler); } PointEvents[index].pointerDrag = null; CurrentDragging[index] = null; } if (CurrentPressed[index]) { ExecuteEvents.Execute(CurrentPressed[index], PointEvents[index], ExecuteEvents.pointerUpHandler); PointEvents[index].rawPointerPress = null; PointEvents[index].pointerPress = null; CurrentPressed[index] = null; } } // drag handling if (CurrentDragging[index] != null) { ExecuteEvents.Execute(CurrentDragging[index], PointEvents[index], ExecuteEvents.dragHandler); } } } } private bool ButtonDown(int index) { return (ControllerDevices[index] != null && ControllerDevices[index].GetPressDown(Valve.VR.EVRButtonId.k_EButton_SteamVR_Trigger) == true); } private bool ButtonUp(int index) { return (ControllerDevices[index] != null && ControllerDevices[index].GetPressUp(Valve.VR.EVRButtonId.k_EButton_SteamVR_Trigger) == true); } }