public override void OverrideRayEndPoint(Ray ray, ref Vector3 rayEndPoint) { bool triggerJustClicked = false; bool triggerJustReleased = false; VRInput.GetInstantButtonEvent(VRInput.primaryController, CommonUsages.triggerButton, ref triggerJustClicked, ref triggerJustReleased); // Project ray on the widget plane. Plane widgetPlane = new Plane(-transform.forward, transform.position); widgetPlane.Raycast(ray, out float enter); Vector3 worldCollisionOnWidgetPlane = ray.GetPoint(enter); Vector3 localWidgetPosition = transform.InverseTransformPoint(worldCollisionOnWidgetPlane); Vector3 localProjectedWidgetPosition = new Vector3(localWidgetPosition.x, localWidgetPosition.y, 0.0f); if (IgnoreRayInteraction()) { rayEndPoint = transform.TransformPoint(localProjectedWidgetPosition); return; } //Vector2 localCenter = new Vector2(width / 2.0f, -height / 2.0f); Vector2 project = new Vector3(localWidgetPosition.x, localWidgetPosition.y); if (triggerJustClicked) { grippedPos = project; } if (Vector2.Distance(project, grippedPos) < 3.0f * height) { grippedUnderThreshold = true; localProjectedWidgetPosition.x = grippedPos.x; localProjectedWidgetPosition.y = grippedPos.y; } else { grippedUnderThreshold = false; } Vector3 worldProjectedWidgetPosition = transform.TransformPoint(localProjectedWidgetPosition); rayEndPoint = worldProjectedWidgetPosition; }
public override void OverrideRayEndPoint(Ray ray, ref Vector3 rayEndPoint) { bool triggerJustClicked = false; bool triggerJustReleased = false; VRInput.GetInstantButtonEvent(VRInput.primaryController, CommonUsages.triggerButton, ref triggerJustClicked, ref triggerJustReleased); // Project ray on the widget plane. Plane widgetPlane = new Plane(-transform.forward, transform.position); float enter; widgetPlane.Raycast(ray, out enter); Vector3 worldCollisionOnWidgetPlane = ray.GetPoint(enter); Vector3 localWidgetPosition = transform.InverseTransformPoint(worldCollisionOnWidgetPlane); Vector3 localProjectedWidgetPosition = new Vector3(localWidgetPosition.x, localWidgetPosition.y, 0.0f); if (IgnoreRayInteraction()) { // return endPoint at the surface of the widget. rayEndPoint = transform.TransformPoint(localProjectedWidgetPosition); return; } float startX = 0; float endX = width; float currentValuePct = (float)(Value - minValue) / (float)(maxValue - minValue); float currentKnobPositionX = startX + currentValuePct * (endX - startX); // TODO: apply drag directly on the Value and previous Value. // DRAG if (!triggerJustClicked && lerp) { localProjectedWidgetPosition.x = Mathf.Lerp(currentKnobPositionX, localProjectedWidgetPosition.x, GlobalState.Settings.RaySliderDrag); } // CLAMP if (localProjectedWidgetPosition.x < startX) { localProjectedWidgetPosition.x = startX; } if (localProjectedWidgetPosition.x > endX) { localProjectedWidgetPosition.x = endX; } // Compute closest int for snapping. float pct = (localProjectedWidgetPosition.x - startX) / (endX - startX); float fValue = (float)minValue + pct * (float)(maxValue - minValue); int roundedValue = Mathf.RoundToInt(fValue); // SNAP X to closest int localProjectedWidgetPosition.x = startX + ((float)roundedValue - minValue) * (endX - startX) / (float)(maxValue - minValue); // SNAP Y to middle of knob object. TODO: use actual knob dimensions localProjectedWidgetPosition.y = -height + 0.02f; // SNAP Z to the thickness of the knob localProjectedWidgetPosition.z = -0.005f; // SET if (roundedValue != GlobalState.Animation.CurrentFrame) { onSlideEvent.Invoke(roundedValue); if (triggerJustClicked) { lerp = false; } } // Haptic intensity as we go deeper into the widget. //float intensity = Mathf.Clamp01(0.001f + 0.999f * localWidgetPosition.z / UIElement.collider_min_depth_deep); //intensity *= intensity; // ease-in //VRInput.SendHaptic(VRInput.rightController, 0.005f, intensity); Vector3 worldProjectedWidgetPosition = transform.TransformPoint(localProjectedWidgetPosition); rayEndPoint = worldProjectedWidgetPosition; }
public override void OverrideRayEndPoint(Ray ray, ref Vector3 rayEndPoint) { bool triggerJustClicked = false; bool triggerJustReleased = false; VRInput.GetInstantButtonEvent(VRInput.primaryController, CommonUsages.triggerButton, ref triggerJustClicked, ref triggerJustReleased); // Project ray on the widget plane. Plane widgetPlane = new Plane(-transform.forward, transform.position); float enter; widgetPlane.Raycast(ray, out enter); Vector3 worldCollisionOnWidgetPlane = ray.GetPoint(enter); Vector3 localWidgetPosition = transform.InverseTransformPoint(worldCollisionOnWidgetPlane); Vector3 localProjectedWidgetPosition = new Vector3(localWidgetPosition.x, localWidgetPosition.y, 0.0f); if (IgnoreRayInteraction()) { // return endPoint at the surface of the widget. rayEndPoint = transform.TransformPoint(localProjectedWidgetPosition); return; } float widthWithoutMargins = width - 2.0f * margin; float startX = margin + widthWithoutMargins * sliderPositionBegin + railMargin; float endX = margin + widthWithoutMargins * sliderPositionEnd - railMargin; // SPAWN KEYBOARD if (triggerJustClicked && localProjectedWidgetPosition.x > endX) { ToolsUIManager.Instance.OpenNumericKeyboard(OnValidateKeyboard, transform, (float)Value); rayEndPoint = transform.TransformPoint(localProjectedWidgetPosition); return; } // DRAG if (!triggerJustClicked) // if trigger just clicked, use the actual projection, no interpolation. { float drag = GlobalState.Settings.RaySliderDrag; localProjectedWidgetPosition.x = Mathf.Lerp(lastProjected, localProjectedWidgetPosition.x, drag); } lastProjected = localProjectedWidgetPosition.x; // CLAMP if (localProjectedWidgetPosition.x < startX) { localProjectedWidgetPosition.x = startX; } if (localProjectedWidgetPosition.x > endX) { localProjectedWidgetPosition.x = endX; } localProjectedWidgetPosition.y = -height / 2.0f; // SET float pct = (localProjectedWidgetPosition.x - startX) / (endX - startX); if (HasCurveData()) { Value = dataCurve.Evaluate(pct); } else // linear { float v = minValue + pct * (maxValue - minValue); Value = v; // will replace the slider cursor. } onSlideEvent.Invoke(currentValue); int intValue = Mathf.RoundToInt(currentValue); onSlideEventInt.Invoke(intValue); // OUT ray end point Vector3 worldProjectedWidgetPosition = transform.TransformPoint(localProjectedWidgetPosition); rayEndPoint = worldProjectedWidgetPosition; }
private void HandleRaycast() { // TODO: // - audioClick.Play(); ???? // - VRInput.SendHaptic(VRInput.rightController, 0.005f, intensity); ??? // // Find out if the trigger button was pressed, in order to send the info the the widget hit. // bool triggerJustClicked = false; bool triggerJustReleased = false; VRInput.GetInstantButtonEvent(VRInput.primaryController, CommonUsages.triggerButton, ref triggerJustClicked, ref triggerJustReleased); // // Raycast, find out the closest volume, handle and widget. // Vector3 worldStart = transform.TransformPoint(0.0104f, 0, 0.065f); Vector3 worldEnd = transform.TransformPoint(0.0104f, 0, 1f); Vector3 newWorldDirection = worldEnd - worldStart; Vector3 worldDirection = prevWorldDirection != Vector3.zero ? Vector3.Lerp(prevWorldDirection, newWorldDirection, rayStiffness) : newWorldDirection; worldDirection.Normalize(); prevWorldDirection = worldDirection; Vector3 rayEndPoint = worldEnd; Ray r = new Ray(worldStart, worldDirection); if (UIElement.UIEnabled.Value) { // If a widget is locked (trigger has been pressed on it), give it a chance to handle the ray endpoint. if (WidgetIsClicked && widgetClicked.OverridesRayEndPoint()) { widgetClicked.OverrideRayEndPoint(r, ref rayEndPoint); } } // Lambda to avoid copy-paste. System.Action handleHitNothing = () => { if (WidgetIsClicked && widgetClicked.OverridesRayEndPoint()) { ActivateRay(true); ray.SetParameters(worldStart, rayEndPoint, newWorldDirection); } else { ActivateRay(false); ReleaseUIEnabledGuard(); // release the guard if there was one. } HandleRayOutOfWidget(triggerJustReleased); }; // // First, try to hit anything, in order to findout if we hit a Non-UI object first. // RaycastHit hitInfo; int allLayersMask = -1; // ~0 float scale = 1f / GlobalState.WorldScale; if (!Physics.Raycast(r, out hitInfo, 3.0f * scale, allLayersMask, QueryTriggerInteraction.Collide)) { // Nothing hit HandleHoverPhysicObject(null); handleHitNothing(); return; } bool UIHit = (null != hitInfo.transform.gameObject.GetComponent <UIElement>()) || (null != hitInfo.transform.gameObject.GetComponent <UIHandle>()) || (null != hitInfo.transform.gameObject.GetComponent <UIVolumeTag>()); if (!UIHit) { // hit a non-UI object. HandleHoverPhysicObject(null); handleHitNothing(); return; } // detect if the first ray was shoot from inside an object Ray backRay = new Ray(hitInfo.point - 0.01f * worldDirection, -worldDirection); float d = hitInfo.distance - 0.01f; bool raycastOK = Physics.Raycast(backRay, out hitInfo, hitInfo.distance, allLayersMask, QueryTriggerInteraction.Collide); if (raycastOK && hitInfo.distance < d) { HandleHoverPhysicObject(null); handleHitNothing(); return; } // // Raycast ALL UI elements // RaycastHit[] hits; //int layersMask = LayerMask.GetMask(new string[] { "CameraHidden", "SelectionCameraHidden", "HoverCameraHidden", "Default" }); int layersMask = LayerMask.GetMask(new string[] { "CameraHidden", "SelectionCameraHidden", "HoverCameraHidden" }); hits = Physics.RaycastAll(r, 3.0f * scale, layersMask, QueryTriggerInteraction.Collide); if (hits.Length > 0) { if (!UIElement.UIEnabled.Value) { return; } // Ray hits anything UI, but a tool action is happening. // Create a guard to disable any action if not already created. if (CommandManager.IsUndoGroupOpened()) { if (null == uiEnabledGuard) { uiEnabledGuard = UIElement.UIEnabled.SetValue(false); } ActivateRay(false); return; } bool volumeIsHit = false; bool widgetIsHit = false; bool physicIsHit = false; float closestVolumeDistance = Mathf.Infinity; float closestWidgetDistance = Mathf.Infinity; float closestHandleDistance = Mathf.Infinity; float closestPhysicDistance = Mathf.Infinity; Vector3 volumeCollisionPoint = Vector3.zero; Vector3 widgetCollisionPoint = Vector3.zero; Vector3 handleCollisionPoint = Vector3.zero; Vector3 physicCollisionPoint = Vector3.zero; UIElement widget = null; UIHandle handle = null; UIVolumeTag volume = null; GameObject physic = null; // // Find if a volume/handle/widget has been hit, and compute the closest hit distance/point. // for (int i = 0; i < hits.Length; ++i) { Transform hit = hits[i].transform; UIVolumeTag volumeHit = hit.GetComponent <UIVolumeTag>(); if (volumeHit != null) { volumeIsHit = true; if (hits[i].distance < closestVolumeDistance) { volume = volumeHit; volumeCollisionPoint = hits[i].point; // world space closestVolumeDistance = hits[i].distance; } } UIHandle handleHit = hit.GetComponent <UIHandle>(); if (handleHit != null) { if (hits[i].distance < closestHandleDistance) { handle = handleHit; handleCollisionPoint = hits[i].point; // world space closestHandleDistance = hits[i].distance; } } UIElement widgetHit = hit.GetComponent <UIElement>(); if (widgetHit != null) { widgetIsHit = true; if (hits[i].distance < closestWidgetDistance) { widget = widgetHit; widgetCollisionPoint = hits[i].point; // world space closestWidgetDistance = hits[i].distance; } } GameObject objectHit = hit.gameObject; if (objectHit.CompareTag("PhysicObject")) { physicIsHit = true; if (hits[i].distance < closestPhysicDistance) { physic = objectHit; physicCollisionPoint = hits[i].point; // world space closestPhysicDistance = hits[i].distance; } } } // // Send messages and states to the widget hit, with priorities. // //else if (handleIsHit) //{ // HandleHoverPhysicObject(null); // if (WidgetIsClicked && widgetClicked.OverridesRayEndPoint()) // { // ActivateRay(true); // ray.SetParameters(worldStart, rayEndPoint, newWorldDirection); // } // else // { // ActivateRay(false); // } // HandleRayOutOfWidget(triggerJustReleased); //} if (widgetIsHit) { HandleHoverPhysicObject(null); if (prevWidget != widget) // change widget { if (WidgetIsClicked) // trigger is held pushed. { if (widgetClicked == widget) // on same widget { if (!widgetClicked.IgnoreRayInteraction()) { widgetClicked.OnRayEnterClicked(); // act as if we re-click on widget. } } else // click has been pushed on another widget { if (prevWidget == widgetClicked) { if (!widgetClicked.IgnoreRayInteraction()) { widgetClicked.OnRayExitClicked(); } } // dont do anything for the new widget, not even hover. } } else // no click, simple hover { if (prevWidget != null) { if (!prevWidget.IgnoreRayInteraction()) { prevWidget.OnRayExit(); } } if (!widget.IgnoreRayInteraction()) { widget.OnRayEnter(); } } } else // still on same widget { if (WidgetIsClicked) // trigger is held pushed. { if (widgetClicked == widget) // on same widget { if (!widgetClicked.IgnoreRayInteraction()) { widgetClicked.OnRayHoverClicked(); } } else // still hovering a widget which is not the one clicked. { // TODO: should we add another state here? this is a FAKE hover. // we want to show that this was the clicked widget but the ray is elsewhere. //if (!widgetClicked.IgnoreRayInteraction()) //widgetClicked.OnRayHover(r); // simple hover without the click effect. // do nothing for the new widget. } } else { if (!widget.IgnoreRayInteraction()) { widget.OnRayHover(r); } } } // "Just click" is independant of whether we stay or change hit widget. if (triggerJustClicked) { if (!widget.IgnoreRayInteraction()) { widget.OnRayClick(); SoundManager.Instance.Play3DSound(audioSource, SoundManager.Sounds.ClickIn); UIElement.ClickHapticFeedback(); // TODO: voir si on le met individuellement dans chaque widget avec des exceptions. } widgetClicked = widget; if (widgetClicked.OverridesRayEndPoint()) { // call this here when the "triggerJustClicked" state of VRInput is still set. widgetClicked.OverrideRayEndPoint(r, ref rayEndPoint); } } // I prefer treating "Just released" outside of the rest. // BUT this leads to maybe 2 events sent for the same widget. if (triggerJustReleased) { // do not send Release to another widget than the one which received the click. if (WidgetIsClicked) { if (widgetClicked == widget) { if (!widget.IgnoreRayInteraction()) { widget.OnRayReleaseInside(); SoundManager.Instance.Play3DSound(audioSource, SoundManager.Sounds.ClickOut); UIElement.ClickHapticFeedback(); } } else { // clear state of previously clicked widget if (!widgetClicked.IgnoreRayInteraction()) { if (widgetClicked.OnRayReleaseOutside()) { SoundManager.Instance.Play3DSound(audioSource, SoundManager.Sounds.ClickOut); UIElement.ClickHapticFeedback(); } } // give the new widget a chance to play some OnHover animation. if (!widget.IgnoreRayInteraction()) { widget.OnRayEnter(); } } } else { Debug.LogWarning("Just Released received without having clicked before on any widget!!"); } widgetClicked = null; } prevWidget = widget; // even if the same. ActivateRay(true); if (widget.GetComponent <UIPanel>()) { ray.SetPanelColor(); } else { ray.SetWidgetColor(); } if (WidgetIsClicked && widgetClicked.OverridesRayEndPoint()) { ray.SetParameters(worldStart, rayEndPoint, newWorldDirection); } else { ray.SetParameters(worldStart, widgetCollisionPoint, newWorldDirection); } } else if (physicIsHit) { if (WidgetIsClicked && widgetClicked.OverridesRayEndPoint()) { ActivateRay(true); ray.SetParameters(worldStart, rayEndPoint, newWorldDirection); HandleHoverPhysicObject(null); } else { ActivateRay(true); ray.SetParameters(worldStart, physicCollisionPoint, newWorldDirection); HandleHoverPhysicObject(physic); } HandleRayOutOfWidget(triggerJustReleased); } else if (volumeIsHit) { //HandleHoverPhysicObject(null); ray.gameObject.SetActive(true); if (WidgetIsClicked && widgetClicked.OverridesRayEndPoint()) { ray.SetParameters(worldStart, rayEndPoint, newWorldDirection); } else { ray.SetVolumeColor(); ray.SetParameters(worldStart, worldStart + worldDirection * 0.3f, newWorldDirection); // volumeCollisionPoint } HandleRayOutOfWidget(triggerJustReleased); } else // Layer UI but neither UIVolumeTag nor UIElement ==> Grid or Tool Mouthpiece. { HandleHoverPhysicObject(null); if (WidgetIsClicked && widgetClicked.OverridesRayEndPoint()) { ActivateRay(true); ray.SetParameters(worldStart, rayEndPoint, newWorldDirection); } else { ActivateRay(false); } HandleRayOutOfWidget(triggerJustReleased); } } else // No collision, most common case. { HandleHoverPhysicObject(null); handleHitNothing(); } }
public override void OverrideRayEndPoint(Ray ray, ref Vector3 rayEndPoint) { bool triggerJustClicked = false; bool triggerJustReleased = false; VRInput.GetInstantButtonEvent(VRInput.primaryController, CommonUsages.triggerButton, ref triggerJustClicked, ref triggerJustReleased); // Project ray on the widget plane. Plane widgetPlane = new Plane(-transform.forward, transform.position); float enter; widgetPlane.Raycast(ray, out enter); Vector3 worldCollisionOnWidgetPlane = ray.GetPoint(enter); Vector3 localWidgetPosition = transform.InverseTransformPoint(worldCollisionOnWidgetPlane); Vector3 localProjectedWidgetPosition = new Vector3(localWidgetPosition.x, localWidgetPosition.y, 0.0f); if (IgnoreRayInteraction()) { // return endPoint at the surface of the widget. rayEndPoint = transform.TransformPoint(localProjectedWidgetPosition); return; } float startX = 0; float endX = width; float currentKnobPositionX = cursorPosition * width; // DRAG if (!triggerJustClicked) // if trigger just clicked, use the actual projection, no interpolation. { localProjectedWidgetPosition.x = Mathf.Lerp(currentKnobPositionX, localProjectedWidgetPosition.x, GlobalState.Settings.RaySliderDrag); } // CLAMP if (localProjectedWidgetPosition.x < startX) { localProjectedWidgetPosition.x = startX; } if (localProjectedWidgetPosition.x > endX) { localProjectedWidgetPosition.x = endX; } localProjectedWidgetPosition.y = -height / 2.0f; // SET float pct = localProjectedWidgetPosition.x / width; SetAlpha(Mathf.Clamp(pct, 0, 1)); colorPicker.OnColorChanged(); Vector3 worldProjectedWidgetPosition = transform.TransformPoint(localProjectedWidgetPosition); //cursorShapeTransform.position = worldProjectedWidgetPosition; rayEndPoint = worldProjectedWidgetPosition; }
public override void OverrideRayEndPoint(Ray ray, ref Vector3 rayEndPoint) { bool triggerJustClicked = false; bool triggerJustReleased = false; VRInput.GetInstantButtonEvent(VRInput.primaryController, CommonUsages.triggerButton, ref triggerJustClicked, ref triggerJustReleased); // Project ray on the widget plane. Plane widgetPlane = new Plane(-transform.forward, transform.position); float enter; widgetPlane.Raycast(ray, out enter); Vector3 worldCollisionOnWidgetPlane = ray.GetPoint(enter); Vector3 localWidgetPosition = transform.InverseTransformPoint(worldCollisionOnWidgetPlane); Vector3 localProjectedWidgetPosition = new Vector3(localWidgetPosition.x, localWidgetPosition.y, 0.0f); if (IgnoreRayInteraction()) { // return endPoint at the surface of the widget. rayEndPoint = transform.TransformPoint(localProjectedWidgetPosition); return; } // CLAMP float startX = 0.0f; float endX = width; if (localProjectedWidgetPosition.x < startX) { localProjectedWidgetPosition.x = startX; } if (localProjectedWidgetPosition.x > endX) { localProjectedWidgetPosition.x = endX; } localProjectedWidgetPosition.y = -height / 2.0f; // GRIP CLOSEST Keyframe if (triggerJustClicked) { deltaFrame = 0; float distThreshold = 0.31f / 20.0f; closestIndex = -1; float closestDistance = Mathf.Infinity; int i = 0; foreach (Transform child in transform) { float dist = Mathf.Abs(localProjectedWidgetPosition.x - child.localPosition.x); if (dist < closestDistance && dist < distThreshold) { closestDistance = dist; closestIndex = i; } i++; } if (closestIndex != -1) { localProjectedWidgetPosition.x = transform.GetChild(closestIndex).localPosition.x; } } else if (triggerJustReleased) { dopesheet.OnUpdateKeyframe(closestIndex, deltaFrame); deltaFrame = 0; closestIndex = -1; } else { // JOYSTICK Left/Right bool joyRightJustClicked = false; bool joyRightJustReleased = false; bool joyRightLongPush = false; VRInput.GetInstantJoyEvent(VRInput.primaryController, VRInput.JoyDirection.RIGHT, ref joyRightJustClicked, ref joyRightJustReleased, ref joyRightLongPush); bool joyLeftJustClicked = false; bool joyLeftJustReleased = false; bool joyLeftLongPush = false; VRInput.GetInstantJoyEvent(VRInput.primaryController, VRInput.JoyDirection.LEFT, ref joyLeftJustClicked, ref joyLeftJustReleased, ref joyLeftLongPush); if (joyRightJustClicked || joyLeftJustClicked || joyRightLongPush || joyLeftLongPush) { float localDeltaOneFrame = dopesheet != null ? 0.31f / (dopesheet.LocalLastFrame - dopesheet.LocalFirstFrame) : 0.0f; if (joyRightJustClicked || joyRightLongPush) { if (closestIndex != -1) { deltaFrame++; Transform child = transform.GetChild(closestIndex); Vector3 newChildPosition = child.localPosition + new Vector3(+localDeltaOneFrame, 0, 0); localProjectedWidgetPosition.x = newChildPosition.x; child.localPosition = newChildPosition; } } else if (joyLeftJustClicked || joyLeftLongPush) { if (closestIndex != -1) { deltaFrame--; Transform child = transform.GetChild(closestIndex); Vector3 newChildPosition = child.localPosition + new Vector3(-localDeltaOneFrame, 0, 0); localProjectedWidgetPosition.x = newChildPosition.x; child.localPosition = newChildPosition; } } } else { if (closestIndex != -1) { localProjectedWidgetPosition.x = transform.GetChild(closestIndex).localPosition.x; } } } // OUT ray end point Vector3 worldProjectedWidgetPosition = transform.TransformPoint(localProjectedWidgetPosition); rayEndPoint = worldProjectedWidgetPosition; }
public override void OverrideRayEndPoint(Ray ray, ref Vector3 rayEndPoint) { bool triggerJustClicked = false; bool triggerJustReleased = false; VRInput.GetInstantButtonEvent(VRInput.primaryController, CommonUsages.triggerButton, ref triggerJustClicked, ref triggerJustReleased); // Project ray on the widget plane. Plane widgetPlane = new Plane(-transform.forward, transform.position); float enter; widgetPlane.Raycast(ray, out enter); Vector3 worldCollisionOnWidgetPlane = ray.GetPoint(enter); Vector3 localWidgetPosition = transform.InverseTransformPoint(worldCollisionOnWidgetPlane); Vector3 localProjectedWidgetPosition = new Vector3(localWidgetPosition.x, localWidgetPosition.y, 0.0f); if (IgnoreRayInteraction()) { // return endPoint at the surface of the widget. rayEndPoint = transform.TransformPoint(localProjectedWidgetPosition); return; } float startX = 0; float endX = width; float startY = 0; float endY = -height; Vector2 currentKnobPosition = new Vector2(cursorPosition.x * width, (-1.0f + cursorPosition.y) * height); // DRAG if (!triggerJustClicked) // if trigger just clicked, use the actual projection, no interpolation. { localProjectedWidgetPosition.x = Mathf.Lerp(currentKnobPosition.x, localProjectedWidgetPosition.x, GlobalState.Settings.RaySliderDrag); localProjectedWidgetPosition.y = Mathf.Lerp(currentKnobPosition.y, localProjectedWidgetPosition.y, GlobalState.Settings.RaySliderDrag); } // CLAMP if (localProjectedWidgetPosition.x < startX) { localProjectedWidgetPosition.x = startX; } if (localProjectedWidgetPosition.x > endX) { localProjectedWidgetPosition.x = endX; } if (localProjectedWidgetPosition.y > startY) { localProjectedWidgetPosition.y = startY; } if (localProjectedWidgetPosition.y < endY) { localProjectedWidgetPosition.y = endY; } // SET float x = localProjectedWidgetPosition.x / width; float y = 1.0f - (-localProjectedWidgetPosition.y / height); x = Mathf.Clamp(x, 0, 1); y = Mathf.Clamp(y, 0, 1); SetSaturation(new Vector2(x, y)); colorPicker.OnColorChanged(); // Haptic intensity as we go deeper into the widget. //float intensity = Mathf.Clamp01(0.001f + 0.999f * localWidgetPosition.z / UIElement.collider_min_depth_deep); //intensity *= intensity; // ease-in //VRInput.SendHaptic(VRInput.rightController, 0.005f, intensity); Vector3 worldProjectedWidgetPosition = transform.TransformPoint(localProjectedWidgetPosition); //cursorShapeTransform.position = worldProjectedWidgetPosition; rayEndPoint = worldProjectedWidgetPosition; }
public override void OverrideRayEndPoint(Ray ray, ref Vector3 rayEndPoint) { bool triggerJustClicked = false; bool triggerJustReleased = false; VRInput.GetInstantButtonEvent(VRInput.primaryController, CommonUsages.triggerButton, ref triggerJustClicked, ref triggerJustReleased); // Project ray on the widget plane. Plane widgetPlane = new Plane(-transform.forward, transform.position); float enter; widgetPlane.Raycast(ray, out enter); Vector3 worldCollisionOnWidgetPlane = ray.GetPoint(enter); Vector3 localWidgetPosition = transform.InverseTransformPoint(worldCollisionOnWidgetPlane); Vector3 localProjectedWidgetPosition = new Vector3(localWidgetPosition.x, localWidgetPosition.y, 0.0f); if (IgnoreRayInteraction()) { // return endPoint at the surface of the widget. rayEndPoint = transform.TransformPoint(localProjectedWidgetPosition); return; } float heightWithoutMargins = height - 2.0f * margin; float startY = -height + margin + heightWithoutMargins * sliderPositionBegin + railMargin; float endY = -height + margin + heightWithoutMargins * sliderPositionEnd - railMargin; float currentValuePct = (Value - minValue) / (maxValue - minValue); if (HasCurveData()) { currentValuePct = invDataCurve.Evaluate(Value); } float currentKnobPositionY = startY + currentValuePct * (endY - startY); // DRAG if (!triggerJustClicked) { localProjectedWidgetPosition.y = Mathf.Lerp(currentKnobPositionY, localProjectedWidgetPosition.y, GlobalState.Settings.RaySliderDrag); } // CLAMP // SNAP Y top if (localProjectedWidgetPosition.y < startY) { localProjectedWidgetPosition.y = startY; } // SNAP Y bottom if (localProjectedWidgetPosition.y > endY) { localProjectedWidgetPosition.y = endY; } // SNAP X to middle localProjectedWidgetPosition.x = width / 2.0f; // SET float pct = (localProjectedWidgetPosition.y - startY) / (endY - startY); if (HasCurveData()) { Value = dataCurve.Evaluate(pct); } else // linear { Value = minValue + pct * (maxValue - minValue); // will replace the slider cursor. } Value = minValue + pct * (maxValue - minValue); // will replace the slider cursor. onSlideEvent.Invoke(currentValue); int intValue = Mathf.RoundToInt(currentValue); onSlideEventInt.Invoke(intValue); // Haptic intensity as we go deeper into the widget. //float intensity = Mathf.Clamp01(0.001f + 0.999f * localWidgetPosition.z / UIElement.collider_min_depth_deep); //intensity *= intensity; // ease-in //VRInput.SendHaptic(VRInput.rightController, 0.005f, intensity); Vector3 worldProjectedWidgetPosition = transform.TransformPoint(localProjectedWidgetPosition); //cursorShapeTransform.position = worldProjectedWidgetPosition; rayEndPoint = worldProjectedWidgetPosition; }
public override void OverrideRayEndPoint(Ray ray, ref Vector3 rayEndPoint) { bool triggerJustClicked = false; bool triggerJustReleased = false; VRInput.GetInstantButtonEvent(VRInput.primaryController, CommonUsages.triggerButton, ref triggerJustClicked, ref triggerJustReleased); // Project ray on the widget plane. Plane widgetPlane = new Plane(-transform.forward, transform.position); float enter; widgetPlane.Raycast(ray, out enter); Vector3 worldCollisionOnWidgetPlane = ray.GetPoint(enter); Vector3 localWidgetPosition = transform.InverseTransformPoint(worldCollisionOnWidgetPlane); Vector3 localProjectedWidgetPosition = new Vector3(localWidgetPosition.x, localWidgetPosition.y, -thickness); if (IgnoreRayInteraction()) { // return endPoint at the surface of the widget. rayEndPoint = transform.TransformPoint(localProjectedWidgetPosition); return; } float w2 = width / 2.0f; float h2 = height / 2.0f; float ir = innerCirclePct * w2; // circle inner radius float or = outerCirclePct * w2; // circle outer radius float mr = (ir + or) / 2.0f; // circle middle radius float cw = (or - ir); // circle width float tr = trianglePct * w2; Vector2 circleCenter_L = new Vector2(w2, -h2); Vector2 cursor_L = new Vector2(localProjectedWidgetPosition.x, localProjectedWidgetPosition.y); // Just clicked, find out which subpart to lock on if (triggerJustClicked) { float cursorDistanceFromCenter = Vector2.Distance(cursor_L, circleCenter_L); if (cursorDistanceFromCenter >= ir && cursorDistanceFromCenter <= or) { lockedOnCircle = true; } else { // TODO: really check for triangle bounds, not only bounding circle. if (cursorDistanceFromCenter <= tr) { lockedOnTriangle = true; } } } if (lockedOnCircle) { Vector2 cursor_C = cursor_L - circleCenter_L; float angle = Mathf.Rad2Deg * (Mathf.PI - Mathf.Atan2(cursor_C.y, cursor_C.x)); float newHue = angle / 360.0f; // DRAG if (!triggerJustClicked) { float oldHue = hsv.x; if (newHue - oldHue < -0.5f) // ex: 0.9 -> 0.1 ==> 0.9 -> 1.1 { newHue = Mathf.Lerp(oldHue, newHue + 1.0f, GlobalState.Settings.RayHueDrag); if (newHue >= 1.0f) { newHue -= 1.0f; } } else if (newHue - oldHue > 0.5f) // ex: 0.1 -> 0.9 ==> 1.1 -> 0.9 { newHue = Mathf.Lerp(oldHue + 1.0f, newHue, GlobalState.Settings.RayHueDrag); if (newHue >= 1.0f) { newHue -= 1.0f; } } else // ex: 0.1 -> 0.2 { newHue = Mathf.Lerp(oldHue, newHue, GlobalState.Settings.RayHueDrag); } } HSV = new Vector3(newHue, hsv.y, hsv.z); // NOTE: also re-position the cursors. colorPicker.OnColorChanged(); localProjectedWidgetPosition = new Vector3( w2 + mr * -Mathf.Cos(hsv.x * 2.0f * Mathf.PI), -h2 + mr * Mathf.Sin(hsv.x * 2.0f * Mathf.PI), -thickness); } else if (lockedOnTriangle) { Vector3 closest; Vector3 baryOfClosest; ClosestPointToTriangle2D(localProjectedWidgetPosition, pt_A_HUE, pt_B_WHITE, pt_C_BLACK, out closest, out baryOfClosest); localProjectedWidgetPosition = new Vector3(closest.x, closest.y, -thickness); barycentric = baryOfClosest; // DRAG //if (!triggerJustClicked) //{ // localProjectedWidgetPosition = Vector3.Lerp(lastProjected, localProjectedWidgetPosition, GlobalState.Settings.RaySliderDrag); // barycentric = GetBarycentricCoordinates2D(localProjectedWidgetPosition, pt_A_HUE, pt_B_WHITE, pt_C_BLACK); //} //lastProjected = localProjectedWidgetPosition; //float H, S, V; //Color.RGBToHSV(BarycentricToRGB(), out H, out S, out V); //// TODO: make a function PointInTriangleToRGB(closest, a, b, c), using the resterizer algo to find color instead of barycentric. //hsv = new Vector3(hsv.x, S, V); Vector3 _sv = PointInTriangleToHSV(closest); hsv = new Vector3(hsv.x, _sv.y, _sv.z); UpdateCursorPositions(); colorPicker.OnColorChanged(); } Vector3 worldProjectedWidgetPosition = transform.TransformPoint(localProjectedWidgetPosition); rayEndPoint = worldProjectedWidgetPosition; }