178 lines
7.0 KiB
C#
178 lines
7.0 KiB
C#
|
// Copyright 2017 Google Inc. All rights reserved.
|
||
|
//
|
||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
// you may not use this file except in compliance with the License.
|
||
|
// You may obtain a copy of the License at
|
||
|
//
|
||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||
|
//
|
||
|
// Unless required by applicable law or agreed to in writing, software
|
||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
// See the License for the specific language governing permissions and
|
||
|
// limitations under the License.
|
||
|
|
||
|
using UnityEngine;
|
||
|
using UnityEngine.EventSystems;
|
||
|
|
||
|
/// Draws a circular reticle in front of any object that the user points at.
|
||
|
/// The circle dilates if the object is clickable.
|
||
|
public class GvrReticlePointerImpl : GvrBasePointer {
|
||
|
// The constants below are expsed for testing.
|
||
|
// Minimum inner angle of the reticle (in degrees).
|
||
|
public const float RETICLE_MIN_INNER_ANGLE = 0.0f;
|
||
|
// Minimum outer angle of the reticle (in degrees).
|
||
|
public const float RETICLE_MIN_OUTER_ANGLE = 0.5f;
|
||
|
// Angle at which to expand the reticle when intersecting with an object
|
||
|
// (in degrees).
|
||
|
public const float RETICLE_GROWTH_ANGLE = 1.5f;
|
||
|
|
||
|
// Minimum distance of the reticle (in meters).
|
||
|
public const float RETICLE_DISTANCE_MIN = 0.45f;
|
||
|
// Maximum distance of the reticle (in meters).
|
||
|
public const float RETICLE_DISTANCE_MAX = 10.0f;
|
||
|
|
||
|
/// Growth speed multiplier for the reticle.
|
||
|
public float ReticleGrowthSpeed { private get; set; }
|
||
|
|
||
|
public Material MaterialComp { private get; set; }
|
||
|
|
||
|
// Current inner angle of the reticle (in degrees).
|
||
|
// Exposed for testing.
|
||
|
public float ReticleInnerAngle { get; private set; }
|
||
|
|
||
|
// Current outer angle of the reticle (in degrees).
|
||
|
// Exposed for testing.
|
||
|
public float ReticleOuterAngle { get; private set; }
|
||
|
|
||
|
// Current distance of the reticle (in meters).
|
||
|
// Getter exposed for testing.
|
||
|
public float ReticleDistanceInMeters { get; private set; }
|
||
|
|
||
|
// Current inner and outer diameters of the reticle, before distance multiplication.
|
||
|
// Getters exposed for testing.
|
||
|
public float ReticleInnerDiameter { get; private set; }
|
||
|
|
||
|
public float ReticleOuterDiameter { get; private set; }
|
||
|
|
||
|
private Vector3 targetPoint = Vector3.zero;
|
||
|
public override Vector3 LineEndPoint { get { return targetPoint; } }
|
||
|
|
||
|
public override float MaxPointerDistance { get { return RETICLE_DISTANCE_MAX; } }
|
||
|
|
||
|
public GvrReticlePointerImpl() {
|
||
|
ReticleGrowthSpeed = 8.0f;
|
||
|
ReticleInnerAngle = 0.0f;
|
||
|
ReticleOuterAngle = 0.5f;
|
||
|
ReticleDistanceInMeters = 10.0f;
|
||
|
ReticleInnerDiameter = 0.0f;
|
||
|
ReticleOuterDiameter = 0.0f;
|
||
|
}
|
||
|
|
||
|
public override void OnStart () {
|
||
|
base.OnStart();
|
||
|
}
|
||
|
|
||
|
/// This is called when the 'BaseInputModule' system should be enabled.
|
||
|
public override void OnInputModuleEnabled() {}
|
||
|
|
||
|
/// This is called when the 'BaseInputModule' system should be disabled.
|
||
|
public override void OnInputModuleDisabled() {}
|
||
|
|
||
|
/// Called when the user is pointing at valid GameObject. This can be a 3D
|
||
|
/// or UI element.
|
||
|
///
|
||
|
/// The targetObject is the object the user is pointing at.
|
||
|
/// The intersectionPosition is where the ray intersected with the targetObject.
|
||
|
/// The intersectionRay is the ray that was cast to determine the intersection.
|
||
|
public override void OnPointerEnter(RaycastResult rayastResult, Ray ray,
|
||
|
bool isInteractive) {
|
||
|
SetPointerTarget(rayastResult.worldPosition, isInteractive);
|
||
|
}
|
||
|
|
||
|
/// Called every frame the user is still pointing at a valid GameObject. This
|
||
|
/// can be a 3D or UI element.
|
||
|
///
|
||
|
/// The targetObject is the object the user is pointing at.
|
||
|
/// The intersectionPosition is where the ray intersected with the targetObject.
|
||
|
/// The intersectionRay is the ray that was cast to determine the intersection.
|
||
|
public override void OnPointerHover(RaycastResult rayastResult, Ray ray,
|
||
|
bool isInteractive) {
|
||
|
SetPointerTarget(rayastResult.worldPosition, isInteractive);
|
||
|
}
|
||
|
|
||
|
/// Called when the user's look no longer intersects an object previously
|
||
|
/// intersected with a ray projected from the camera.
|
||
|
/// This is also called just before **OnInputModuleDisabled** and may have have any of
|
||
|
/// the values set as **null**.
|
||
|
public override void OnPointerExit(GameObject previousObject) {
|
||
|
ReticleDistanceInMeters = RETICLE_DISTANCE_MAX;
|
||
|
ReticleInnerAngle = RETICLE_MIN_INNER_ANGLE;
|
||
|
ReticleOuterAngle = RETICLE_MIN_OUTER_ANGLE;
|
||
|
}
|
||
|
|
||
|
/// Called when a trigger event is initiated. This is practically when
|
||
|
/// the user begins pressing the trigger.
|
||
|
public override void OnPointerClickDown() {}
|
||
|
|
||
|
/// Called when a trigger event is finished. This is practically when
|
||
|
/// the user releases the trigger.
|
||
|
public override void OnPointerClickUp() {}
|
||
|
|
||
|
public override void GetPointerRadius(out float enterRadius, out float exitRadius) {
|
||
|
float min_inner_angle_radians = Mathf.Deg2Rad * RETICLE_MIN_INNER_ANGLE;
|
||
|
float max_inner_angle_radians = Mathf.Deg2Rad * (RETICLE_MIN_INNER_ANGLE + RETICLE_GROWTH_ANGLE);
|
||
|
|
||
|
enterRadius = 2.0f * Mathf.Tan(min_inner_angle_radians);
|
||
|
exitRadius = 2.0f * Mathf.Tan(max_inner_angle_radians);
|
||
|
}
|
||
|
|
||
|
public void UpdateDiameters() {
|
||
|
ReticleDistanceInMeters =
|
||
|
Mathf.Clamp(ReticleDistanceInMeters, RETICLE_DISTANCE_MIN, RETICLE_DISTANCE_MAX);
|
||
|
|
||
|
if (ReticleInnerAngle < RETICLE_MIN_INNER_ANGLE) {
|
||
|
ReticleInnerAngle = RETICLE_MIN_INNER_ANGLE;
|
||
|
}
|
||
|
|
||
|
if (ReticleOuterAngle < RETICLE_MIN_OUTER_ANGLE) {
|
||
|
ReticleOuterAngle = RETICLE_MIN_OUTER_ANGLE;
|
||
|
}
|
||
|
|
||
|
float inner_half_angle_radians = Mathf.Deg2Rad * ReticleInnerAngle * 0.5f;
|
||
|
float outer_half_angle_radians = Mathf.Deg2Rad * ReticleOuterAngle * 0.5f;
|
||
|
|
||
|
float inner_diameter = 2.0f * Mathf.Tan(inner_half_angle_radians);
|
||
|
float outer_diameter = 2.0f * Mathf.Tan(outer_half_angle_radians);
|
||
|
|
||
|
ReticleInnerDiameter =
|
||
|
Mathf.Lerp(ReticleInnerDiameter, inner_diameter, Time.deltaTime * ReticleGrowthSpeed);
|
||
|
ReticleOuterDiameter =
|
||
|
Mathf.Lerp(ReticleOuterDiameter, outer_diameter, Time.deltaTime * ReticleGrowthSpeed);
|
||
|
|
||
|
MaterialComp.SetFloat("_InnerDiameter", ReticleInnerDiameter * ReticleDistanceInMeters);
|
||
|
MaterialComp.SetFloat("_OuterDiameter", ReticleOuterDiameter * ReticleDistanceInMeters);
|
||
|
MaterialComp.SetFloat("_DistanceInMeters", ReticleDistanceInMeters);
|
||
|
}
|
||
|
|
||
|
private bool SetPointerTarget(Vector3 target, bool interactive) {
|
||
|
if (base.PointerTransform == null) {
|
||
|
Debug.LogWarning("Cannot operate on a null pointer transform");
|
||
|
return false;
|
||
|
}
|
||
|
targetPoint = target;
|
||
|
Vector3 targetLocalPosition = base.PointerTransform.InverseTransformPoint(target);
|
||
|
|
||
|
ReticleDistanceInMeters =
|
||
|
Mathf.Clamp(targetLocalPosition.z, RETICLE_DISTANCE_MIN, RETICLE_DISTANCE_MAX);
|
||
|
if (interactive) {
|
||
|
ReticleInnerAngle = RETICLE_MIN_INNER_ANGLE + RETICLE_GROWTH_ANGLE;
|
||
|
ReticleOuterAngle = RETICLE_MIN_OUTER_ANGLE + RETICLE_GROWTH_ANGLE;
|
||
|
} else {
|
||
|
ReticleInnerAngle = RETICLE_MIN_INNER_ANGLE;
|
||
|
ReticleOuterAngle = RETICLE_MIN_OUTER_ANGLE;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
}
|