//***********************************************************
// 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();
}
}