1
This repository has been archived on 2025-03-15. You can view files and clone it, but cannot push or open issues or pull requests.
unity-vr-portoflio/Assets/GoogleVR/Scripts/UI/GvrLaserPointerImpl.cs
2025-03-15 20:02:21 +01:00

235 lines
8.7 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.
// The controller is not available for versions of Unity without the
// GVR native integration.
using UnityEngine;
using UnityEngine.EventSystems;
/// Implementation of GvrBasePointer for a laser pointer visual.
/// This script should be attached to the controller object.
/// The laser visual is important to help users locate their cursor
/// when its not directly in their field of view.
public class GvrLaserPointerImpl : GvrBasePointer {
/// Small offset to prevent z-fighting of the reticle (meters).
private const float Z_OFFSET_EPSILON = 0.1f;
/// Final size of the reticle in meters when it is 1 meter from the camera.
/// The reticle will be scaled based on the size of the mesh so that it's size
/// matches this size.
private const float RETICLE_SIZE_METERS = 0.1f;
/// The percentage of the reticle mesh that shows the reticle.
/// The rest of the reticle mesh is transparent.
private const float RETICLE_VISUAL_RATIO = 0.1f;
public Camera MainCamera { private get; set; }
public Color LaserColor { private get; set; }
public LineRenderer LaserLineRenderer { get; set; }
public float MaxLaserDistance { private get; set; }
public float MaxReticleDistance { private get; set; }
private GameObject reticle;
public GameObject Reticle {
get {
return reticle;
}
set {
reticle = value;
reticleMeshSizeMeters = 1.0f;
reticleMeshSizeRatio = 1.0f;
if (reticle != null) {
MeshFilter meshFilter = reticle.GetComponent<MeshFilter>();
if (meshFilter != null && meshFilter.mesh != null) {
reticleMeshSizeMeters = meshFilter.mesh.bounds.size.x;
if (reticleMeshSizeMeters != 0.0f) {
reticleMeshSizeRatio = 1.0f / reticleMeshSizeMeters;
}
}
}
}
}
// Properties exposed for testing purposes.
public Vector3 PointerIntersection { get; private set; }
public bool IsPointerIntersecting { get; private set; }
public Ray PointerIntersectionRay { get; private set; }
// The size of the reticle's mesh in meters.
private float reticleMeshSizeMeters;
// The ratio of the reticleMeshSizeMeters to 1 meter.
// If reticleMeshSizeMeters is 10, then reticleMeshSizeRatio is 0.1.
private float reticleMeshSizeRatio;
private Vector3 lineEndPoint = Vector3.zero;
public override Vector3 LineEndPoint { get { return lineEndPoint; } }
public override float MaxPointerDistance {
get {
#if UNITY_HAS_GOOGLEVR && (UNITY_ANDROID || UNITY_EDITOR)
return MaxReticleDistance;
#else
return 0;
#endif // UNITY_HAS_GOOGLEVR && (UNITY_ANDROID || UNITY_EDITOR)
}
}
public GvrLaserPointerImpl() {
MaxLaserDistance = 0.75f;
MaxReticleDistance = 2.5f;
}
#if !(UNITY_HAS_GOOGLEVR && (UNITY_ANDROID || UNITY_EDITOR))
public override void OnStart() {
// Don't call base.Start() so that this pointer isn't activated when
// the editor doesn't have UNITY_HAS_GOOGLE_VR.
}
#endif // UNITY_HAS_GOOGLEVR && (UNITY_ANDROID || UNITY_EDITOR)
public override void OnInputModuleEnabled() {
#if UNITY_HAS_GOOGLEVR && (UNITY_ANDROID || UNITY_EDITOR)
if (LaserLineRenderer != null) {
LaserLineRenderer.enabled = true;
}
#endif // UNITY_HAS_GOOGLEVR && (UNITY_ANDROID || UNITY_EDITOR)
}
public override void OnInputModuleDisabled() {
#if UNITY_HAS_GOOGLEVR && (UNITY_ANDROID || UNITY_EDITOR)
if (LaserLineRenderer != null) {
LaserLineRenderer.enabled = false;
}
#endif // UNITY_HAS_GOOGLEVR && (UNITY_ANDROID || UNITY_EDITOR)
}
public override void OnPointerEnter(RaycastResult rayastResult, Ray ray,
bool isInteractive) {
#if UNITY_HAS_GOOGLEVR && (UNITY_ANDROID || UNITY_EDITOR)
PointerIntersection = rayastResult.worldPosition;
PointerIntersectionRay = ray;
IsPointerIntersecting = true;
#endif // UNITY_HAS_GOOGLEVR && (UNITY_ANDROID || UNITY_EDITOR)
}
public override void OnPointerHover(RaycastResult rayastResult, Ray ray,
bool isInteractive) {
#if UNITY_HAS_GOOGLEVR && (UNITY_ANDROID || UNITY_EDITOR)
PointerIntersection = rayastResult.worldPosition;
PointerIntersectionRay = ray;
#endif // UNITY_HAS_GOOGLEVR && (UNITY_ANDROID || UNITY_EDITOR)
}
public override void OnPointerExit(GameObject previousObject) {
#if UNITY_HAS_GOOGLEVR && (UNITY_ANDROID || UNITY_EDITOR)
PointerIntersection = Vector3.zero;
PointerIntersectionRay = new Ray();
IsPointerIntersecting = false;
#endif // UNITY_HAS_GOOGLEVR && (UNITY_ANDROID || UNITY_EDITOR)
}
public override void OnPointerClickDown() {
// User has performed a click on the target. In a derived class, you could
// handle visual feedback such as laser or cursor color changes here.
}
public override void OnPointerClickUp() {
// User has released a click from the target. In a derived class, you could
// handle visual feedback such as laser or cursor color changes here.
}
public override void GetPointerRadius(out float enterRadius, out float exitRadius) {
#if UNITY_HAS_GOOGLEVR && (UNITY_ANDROID || UNITY_EDITOR)
if (Reticle != null) {
float reticleScale = Reticle.transform.localScale.x;
// Fixed size for enter radius to avoid flickering.
// This will cause some slight variability based on the distance of the object
// from the camera, and is optimized for the average case.
enterRadius = RETICLE_SIZE_METERS * 0.5f * RETICLE_VISUAL_RATIO;
// Dynamic size for exit radius.
// Always correct because we know the intersection point of the object and can
// therefore use the correct radius based on the object's distance from the camera.
exitRadius = reticleScale * reticleMeshSizeMeters * RETICLE_VISUAL_RATIO;
} else {
enterRadius = 0.0f;
exitRadius = 0.0f;
}
#else
enterRadius = 0.0f;
exitRadius = 0.0f;
#endif // UNITY_HAS_GOOGLEVR && (UNITY_ANDROID || UNITY_EDITOR)
}
#if UNITY_HAS_GOOGLEVR && (UNITY_ANDROID || UNITY_EDITOR)
public void OnUpdate() {
// Set the reticle's position and scale
if (Reticle != null) {
if (IsPointerIntersecting) {
Vector3 controllerDiff = PointerTransform.position - PointerIntersectionRay.origin;
Vector3 proj = Vector3.Project(controllerDiff, PointerIntersectionRay.direction);
Vector3 controllerAlongRay = PointerIntersectionRay.origin + proj;
Vector3 difference = PointerIntersection - controllerAlongRay;
Vector3 clampedDifference = Vector3.ClampMagnitude(difference, MaxReticleDistance);
Vector3 clampedPosition = controllerAlongRay + clampedDifference;
Reticle.transform.position = clampedPosition;
} else {
Reticle.transform.localPosition = new Vector3(0, 0, MaxReticleDistance);
}
float reticleDistanceFromCamera =
(Reticle.transform.position - MainCamera.transform.position).magnitude;
float scale = RETICLE_SIZE_METERS * reticleMeshSizeRatio * reticleDistanceFromCamera;
Reticle.transform.localScale = new Vector3(scale, scale, scale);
}
if (LaserLineRenderer == null) {
Debug.LogWarning("Line renderer is null, returning");
return;
}
// Set the line renderer positions.
if (IsPointerIntersecting) {
Vector3 laserDiff = PointerIntersection - base.PointerTransform.position;
float intersectionDistance = laserDiff.magnitude;
Vector3 direction = laserDiff.normalized;
float laserDistance = intersectionDistance > MaxLaserDistance ? MaxLaserDistance : intersectionDistance;
lineEndPoint = base.PointerTransform.position + (direction * laserDistance);
} else {
lineEndPoint = base.PointerTransform.position + (base.PointerTransform.forward * MaxLaserDistance);
}
LaserLineRenderer.SetPosition(0,base.PointerTransform.position);
LaserLineRenderer.SetPosition(1,lineEndPoint);
// Adjust transparency
float alpha = GvrArmModel.Instance.preferredAlpha;
#if UNITY_5_6_OR_NEWER
LaserLineRenderer.startColor = Color.Lerp(Color.clear, LaserColor, alpha);
LaserLineRenderer.endColor = Color.clear;
#else
LaserLineRenderer.SetColors(Color.Lerp(Color.clear, LaserColor, alpha), Color.clear);
#endif // UNITY_5_6_OR_NEWER
}
#endif // UNITY_HAS_GOOGLEVR && (UNITY_ANDROID || UNITY_EDITOR)
}