NAK_CVR_Mods/CVRGizmos/Popcron.Gizmos/GizmosInstance.cs
NotAKidoS 821b1efcd8 Tweaked Invoke time, properly disable Gizmos, added debug lines to CVRAvatar.
Invoke time is now 0f, so toggling and refreshing Gizmos are now instant... oops.
GizmosInstance is now properly disabled when not in use. Also removed the default gizmos line drawing at 0,0,0 that was purely there to clear gizmos queue.

CVRAvatar debugging now has useful lines coming from the viewpoint and mouth positions. Later on might actually parent these gizmos to the head : shrug :
2022-09-15 13:12:37 -05:00

443 lines
13 KiB
C#

using UnityEngine;
using UnityEngine.Rendering;
#if UNITY_EDITOR
using UnityEditor;
using UnityEditor.SceneManagement;
#endif
#if !UNITY_2019_1_OR_NEWER
public struct ScriptableRenderContext { }
public static class RenderPipelineManager
{
public static event Action<ScriptableRenderContext, Camera> endCameraRendering;
}
#endif
namespace Popcron
{
[ExecuteInEditMode]
[AddComponentMenu("")]
public class GizmosInstance : MonoBehaviour
{
private const int DefaultQueueSize = 4096;
private static GizmosInstance instance;
private static bool hotReloaded = true;
private static Material defaultMaterial;
private static Plane[] cameraPlanes = new Plane[6];
private Color color = Color.white;
private Matrix4x4 matrix;
private Material overrideMaterial;
private int queueIndex = 0;
private int lastFrame;
private Element[] queue = new Element[DefaultQueueSize];
/// <summary>
/// The material being used to render
/// </summary>
public static Material Material
{
get
{
GizmosInstance inst = GetOrCreate();
if (inst.overrideMaterial)
{
return inst.overrideMaterial;
}
return DefaultMaterial;
}
set
{
GizmosInstance inst = GetOrCreate();
inst.overrideMaterial = value;
}
}
/// <summary>
/// The matrix to use
/// </summary>
public static Matrix4x4 Matrix
{
get
{
GizmosInstance inst = GetOrCreate();
if (!inst.matrix.isIdentity)
{
return inst.matrix;
}
return Matrix4x4.identity;
}
set
{
GizmosInstance inst = GetOrCreate();
inst.matrix = value;
}
}
/// <summary>
/// The matrix to use
/// </summary>
public static Color Color
{
get
{
GizmosInstance inst = GetOrCreate();
return inst.color;
}
set
{
GizmosInstance inst = GetOrCreate();
inst.color = value;
}
}
/// <summary>
/// The default line renderer material
/// </summary>
public static Material DefaultMaterial
{
get
{
if (!defaultMaterial)
{
// Unity has a built-in shader that is useful for drawing
// simple colored things.
Shader shader = Shader.Find("Hidden/Internal-Colored");
defaultMaterial = new Material(shader)
{
hideFlags = HideFlags.HideAndDontSave
};
// Turn on alpha blending
defaultMaterial.SetInt("_SrcBlend", (int)BlendMode.SrcAlpha);
defaultMaterial.SetInt("_DstBlend", (int)BlendMode.OneMinusSrcAlpha);
defaultMaterial.SetInt("_Cull", (int)CullMode.Off);
defaultMaterial.SetInt("_ZWrite", 0);
}
return defaultMaterial;
}
}
internal static GizmosInstance GetOrCreate()
{
if (hotReloaded || !instance)
{
bool markDirty = false;
GizmosInstance[] gizmosInstances = FindObjectsOfType<GizmosInstance>();
for (int i = 0; i < gizmosInstances.Length; i++)
{
instance = gizmosInstances[i];
//destroy any extra gizmo instances
if (i > 0)
{
if (Application.isPlaying)
{
Destroy(gizmosInstances[i]);
}
else
{
DestroyImmediate(gizmosInstances[i]);
markDirty = true;
}
}
}
//none were found, create a new one
if (!instance)
{
instance = new GameObject(typeof(GizmosInstance).FullName).AddComponent<GizmosInstance>();
instance.gameObject.hideFlags = HideFlags.HideInHierarchy | HideFlags.HideInInspector;
markDirty = true;
}
#if UNITY_EDITOR
//mark scene as dirty
if (markDirty && !Application.isPlaying)
{
Scene scene = SceneManager.GetActiveScene();
EditorSceneManager.MarkSceneDirty(scene);
}
#endif
hotReloaded = false;
}
return instance;
}
private float CurrentTime
{
get
{
float time = 0f;
if (Application.isPlaying)
{
time = Time.time;
}
else
{
#if UNITY_EDITOR
time = (float)EditorApplication.timeSinceStartup;
#endif
}
return time;
}
}
/// <summary>
/// Submits an array of points to draw into the queue.
/// </summary>
internal static void Submit(Vector3[] points, Color? color2, bool dashed)
{
GizmosInstance inst = GetOrCreate();
//if new frame, reset index
if (inst.lastFrame != Time.frameCount)
{
inst.lastFrame = Time.frameCount;
inst.queueIndex = 0;
}
//multiply points by matrix
for (int i = 0; i < points.Length; i++)
{
points[i] = inst.matrix.MultiplyPoint3x4(points[i]);
}
//excedeed the length, so make it even bigger
if (inst.queueIndex >= inst.queue.Length)
{
Element[] bigger = new Element[inst.queue.Length + DefaultQueueSize];
for (int i = inst.queue.Length; i < bigger.Length; i++)
{
bigger[i] = new Element();
}
Array.Copy(inst.queue, 0, bigger, 0, inst.queue.Length);
inst.queue = bigger;
}
inst.queue[inst.queueIndex].color = inst.color;
inst.queue[inst.queueIndex].points = points;
inst.queue[inst.queueIndex].dashed = dashed;
inst.queue[inst.queueIndex].matrix = inst.matrix;
inst.queueIndex++;
}
private void OnEnable()
{
//populate queue with empty elements
queue = new Element[DefaultQueueSize];
for (int i = 0; i < DefaultQueueSize; i++)
{
queue[i] = new Element();
}
if (GraphicsSettings.renderPipelineAsset == null)
{
Camera.onPostRender += OnRendered;
}
else
{
RenderPipelineManager.endCameraRendering += OnRendered;
}
}
private void OnDisable()
{
if (GraphicsSettings.renderPipelineAsset == null)
{
Camera.onPostRender -= OnRendered;
}
else
{
RenderPipelineManager.endCameraRendering -= OnRendered;
}
}
private void OnRendered(ScriptableRenderContext context, Camera camera) => OnRendered(camera);
private bool ShouldRenderCamera(Camera camera)
{
if (!camera)
{
return false;
}
//allow the scene and main camera always
bool isSceneCamera = false;
#if UNITY_EDITOR
SceneView sceneView = SceneView.currentDrawingSceneView;
if (sceneView == null)
{
sceneView = SceneView.lastActiveSceneView;
}
if (sceneView != null && sceneView.camera == camera)
{
isSceneCamera = true;
}
#endif
if (isSceneCamera || camera.CompareTag("MainCamera"))
{
return true;
}
if (camera.name.StartsWith("Mirror "))
{
return true;
}
//it passed through the filter
if (Gizmos.CameraFilter?.Invoke(camera) == true)
{
return true;
}
return false;
}
private bool IsVisibleByCamera(Element points, Camera camera)
{
if (!camera)
{
return false;
}
//essentially check if at least 1 point is visible by the camera
for (int i = 0; i < points.points.Length; i++)
{
Vector3 vp = camera.WorldToViewportPoint(points.points[i], camera.stereoActiveEye);
if (vp.x >= 0 && vp.x <= 1 && vp.y >= 0 && vp.y <= 1)
{
return true;
}
}
return false;
}
//private void Update()
//{
// //always render something
// Gizmos.Line(default, default);
//}
private void OnRendered(Camera camera)
{
Material.SetPass(Gizmos.Pass);
//shouldnt be rendering
if (!Gizmos.Enabled)
{
queueIndex = 0;
}
//check if this camera is ok to render with
if (!ShouldRenderCamera(camera))
{
GL.PushMatrix();
GL.Begin(GL.LINES);
//bla bla bla
GL.End();
GL.PopMatrix();
return;
}
Vector3 offset = Gizmos.Offset;
GL.PushMatrix();
GL.MultMatrix(Matrix4x4.identity);
GL.Begin(GL.LINES);
bool alt = CurrentTime % 1 > 0.5f;
float dashGap = Mathf.Clamp(Gizmos.DashGap, 0.01f, 32f);
bool frustumCull = Gizmos.FrustumCulling;
List<Vector3> points = new List<Vector3>();
//draw le elements
for (int e = 0; e < queueIndex; e++)
{
//just in case
if (queue.Length <= e)
{
break;
}
Element element = queue[e];
//dont render this thingy if its not inside the frustum
if (frustumCull)
{
if (!IsVisibleByCamera(element, camera))
{
continue;
}
}
points.Clear();
if (element.dashed)
{
//subdivide
for (int i = 0; i < element.points.Length - 1; i++)
{
Vector3 pointA = element.points[i];
Vector3 pointB = element.points[i + 1];
Vector3 direction = pointB - pointA;
if (direction.sqrMagnitude > dashGap * dashGap * 2f)
{
float magnitude = direction.magnitude;
int amount = Mathf.RoundToInt(magnitude / dashGap);
direction /= magnitude;
for (int p = 0; p < amount - 1; p++)
{
if (p % 2 == (alt ? 1 : 0))
{
float startLerp = p / (amount - 1f);
float endLerp = (p + 1) / (amount - 1f);
Vector3 start = Vector3.Lerp(pointA, pointB, startLerp);
Vector3 end = Vector3.Lerp(pointA, pointB, endLerp);
points.Add(start);
points.Add(end);
}
}
}
else
{
points.Add(pointA);
points.Add(pointB);
}
}
}
else
{
points.AddRange(element.points);
}
GL.Color(element.color);
for (int i = 0; i < points.Count; i++)
{
GL.Vertex(points[i] + offset);
}
}
GL.End();
GL.PopMatrix();
}
}
}