//*********************************************************** // Filename: Visualiser.cs // Author: Marco Fendrich, Moritz Kolvenbach // Last changes: Thursday, 9th of August 2018 // Content: A class to render the visual representation of the different teleportConditions //*********************************************************** using System.Collections; using System.Collections.Generic; using UnityEngine; /// /// Handles the rendering of the visual feedback for the different teleportMethods /// public class Visualiser : MonoBehaviour { [Header("Parabola Mesh Properties")] [Tooltip("Number of points on the parabola mesh. Greater point counts lead to a higher poly/smoother mesh.")] public int PointCount = 50; [Tooltip("Approximate spacing between each of the points on the parabola mesh.")] public float PointSpacing = 0.5f; [Tooltip("Thickness of the parabola mesh")] public float GraphicThickness = 0.05f; [Tooltip("Material to use to render the parabola mesh")] public Material GraphicMaterial; [Header("Selection Pad Properties")] [SerializeField] [Tooltip("Prefab to use as the selection pad when the player is pointing at a valid teleportable surface.")] private GameObject SelectionPadPrefab; [SerializeField] [Tooltip("Prefab to use as the selection pad when the player is pointing at an invalid teleportable surface.")] private GameObject InvalidPadPrefab; [SerializeField] [Tooltip("Prefab to use as the selection pad when using the turn teleport.")] private GameObject RotationPadPrefab; // The final point, where the player gets teleported to private Vector3 SelectedPoint; // The mesh used to check for collisions and validate whether the targetPoint is acutually teleportable private Mesh ParabolaMesh; private float rotation = float.NegativeInfinity; // Prefabs for the different kinds of goal markers private GameObject SelectionPadObject; private GameObject InvalidPadObject; private GameObject RotationPadObject; // Update values based on the information from the different teleportConditions public void updateRendering(bool PointOnNavMesh, List ParabolaPoints, Vector3 normal, Vector3 velocity, float rotation) { this.PointOnNavMesh = PointOnNavMesh; this.ParabolaPoints = ParabolaPoints; this.normal = normal; this.velocity = velocity; this.rotation = rotation; } public void updateRendering(bool PointOnNavMesh, List ParabolaPoints, Vector3 normal, Vector3 velocity) { this.PointOnNavMesh = PointOnNavMesh; this.ParabolaPoints = ParabolaPoints; this.normal = normal; this.velocity = velocity; } // True if the final point of parabolaPoints is at a teleportable location bool PointOnNavMesh; // Normal of selectedPoint Vector3 normal; Vector3 velocity; private List ParabolaPoints; void Update () { SelectedPoint = ParabolaPoints[ParabolaPoints.Count - 1]; // check which marker to render for each frame if (SelectionPadObject != null && rotation == float.NegativeInfinity) { SelectionPadObject.SetActive(PointOnNavMesh); SelectionPadObject.transform.position = SelectedPoint + Vector3.one * 0.005f; if (PointOnNavMesh) { SelectionPadObject.transform.rotation = Quaternion.LookRotation(normal); SelectionPadObject.transform.Rotate(90, 0, 0); } } else if (RotationPadObject != null) { RotationPadObject.SetActive(PointOnNavMesh); RotationPadObject.transform.position = SelectedPoint + Vector3.one * 0.005f; if (PointOnNavMesh) { RotationPadObject.transform.rotation = Quaternion.LookRotation(normal); RotationPadObject.transform.Rotate(90, 0, 0); // rotate selection pad Vector2 parabolicVector = new Vector2(velocity.x, velocity.z); RotationPadObject.transform.Rotate(0, Vector2.SignedAngle(parabolicVector, Vector2.up) - 90.0F, 0); RotationPadObject.transform.Rotate(0, rotation, 0); } } if (InvalidPadObject != null) { InvalidPadObject.SetActive(!PointOnNavMesh); InvalidPadObject.transform.position = SelectedPoint + Vector3.one * 0.005f; if (!PointOnNavMesh) { InvalidPadObject.transform.rotation = Quaternion.LookRotation(normal); InvalidPadObject.transform.Rotate(90, 0, 0); } } // Draw parabola (BEFORE the outside faces of the selection pad, to avoid depth issues) GenerateMesh(ref ParabolaMesh, ParabolaPoints, velocity, Time.time % 1); Graphics.DrawMesh(ParabolaMesh, Matrix4x4.identity, GraphicMaterial, gameObject.layer); } void Start() { // populate initial mesh ParabolaMesh = new Mesh(); ParabolaMesh.MarkDynamic(); ParabolaMesh.name = "Parabolic Pointer"; ParabolaMesh.vertices = new Vector3[0]; ParabolaMesh.triangles = new int[0]; // instantiate target marker prefabs if (SelectionPadPrefab != null) { SelectionPadObject = Instantiate(SelectionPadPrefab); SelectionPadObject.SetActive(false); } if (InvalidPadPrefab != null) { InvalidPadObject = Instantiate(InvalidPadPrefab); InvalidPadObject.SetActive(false); } if (RotationPadPrefab != null) { RotationPadObject = Instantiate(RotationPadPrefab); RotationPadObject.SetActive(false); } } void OnDisable() { if (SelectionPadObject != null) SelectionPadObject.SetActive(false); if (InvalidPadObject != null) InvalidPadObject.SetActive(false); if (RotationPadObject != null) RotationPadObject.SetActive(false); rotation = float.NegativeInfinity; } /// /// Updates the used mesh. Dependent on how the the points list to be visualized is populated /// /// The mesh used for collision detection /// List of Vector3 representing the points to be visualized for the teleport /// Vector3 representing the velocity of the physical throw calculation /// Offset private void GenerateMesh(ref Mesh m, List points, Vector3 forwardVelocity, float uvOffset) { Vector3[] verts = new Vector3[points.Count * 2]; Vector2[] uv = new Vector2[points.Count * 2]; Vector3 right = Vector3.Cross(forwardVelocity, Vector3.up).normalized; // calculate new vertices for the mesh for (int x = 0; x < points.Count; x++) { verts[2 * x] = points[x] - right * GraphicThickness / 2; verts[2 * x + 1] = points[x] + right * GraphicThickness / 2; float uvoffsetMod = uvOffset; if (x == points.Count - 1 && x > 1) { float distLast = (points[x - 2] - points[x - 1]).magnitude; float distCur = (points[x] - points[x - 1]).magnitude; uvoffsetMod += 1 - distCur / distLast; } uv[2 * x] = new Vector2(0, x - uvoffsetMod); uv[2 * x + 1] = new Vector2(1, x - uvoffsetMod); } // calculate new triangleArrays for mesh int[] indices = new int[2 * 3 * (verts.Length - 2)]; for (int x = 0; x < verts.Length / 2 - 1; x++) { int p1 = 2 * x; int p2 = 2 * x + 1; int p3 = 2 * x + 2; int p4 = 2 * x + 3; indices[12 * x] = p1; indices[12 * x + 1] = p2; indices[12 * x + 2] = p3; indices[12 * x + 3] = p3; indices[12 * x + 4] = p2; indices[12 * x + 5] = p4; indices[12 * x + 6] = p3; indices[12 * x + 7] = p2; indices[12 * x + 8] = p1; indices[12 * x + 9] = p4; indices[12 * x + 10] = p2; indices[12 * x + 11] = p3; } // update mesh with new values m.Clear(); m.vertices = verts; m.uv = uv; m.triangles = indices; m.RecalculateBounds(); m.RecalculateNormals(); } }