-AUTEUR : artemisart
-DESCRIPTION : Comme son nom l'indique, ce script sert à afficher le wireframe des meshes en utilisant les fonctions GL, ça marche parfaitement bien sur la free 3.5 (et versions légèrement antérieures) bien que Unity dise le contraire. J'ai déjà vu certains WebPlayer avec le wireframe baké sur une texture et le résultat est souvent bofbof, donc j'ai décidé de coder ça.
-UTILISATION : Le script est à mettre sur une caméra (absolument). Variables utilisées :
- Transform[] meshRoots :
les transforms contenant les meshes (directement sur eux + enfants) dont on veut afficher le wireframe. - MeshFilter[] meshes:
les meshes dont on veut afficher le wireframe, les enfants ne sont pas pris en compte.
Si meshRoots et meshes ne contiennent rien, tous les meshes de la scène sont pris. - bool displayWireframe :
affiche ou non le wireframe. - KeyCode toggleDisplayKey:
touche pour inverser l'affichage du wireframe. - Color wireColor :
la couleur du wire (par défaut en noir avec alpha de 0.2f (51/255)). - Rect rect :
rect utilisé pour afficher les performances du script (on peut commenter cette variable, la fonction OnGUI et les Stopwatch si c'est inutile).
Code : Tout sélectionner
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using UnityEngine;
using Debug = UnityEngine.Debug;
using ThreadPriority = System.Threading.ThreadPriority;
public class GLWireframe : MonoBehaviour
{
private class MeshWire
{
private Vector3 position;
private Quaternion rotation;
private Vector3 scale;
private MeshFilter mf;
private Vector3[] vertices;
private int[] triangles;
private List<bool> uniqueEdges;
private bool isVisible;
private GLWireframe instance;
public MeshWire (MeshFilter meshFilter, GLWireframe inst)
{
mf = meshFilter;
isVisible = true;
vertices = mf.sharedMesh.vertices;
triangles = mf.sharedMesh.triangles;
uniqueEdges = new List<bool> ();
instance = inst;
ThreadPool.QueueUserWorkItem (CheckEdges);
}
private bool V3Equals (Vector3 a, Vector3 b)
{ return a.x == b.x && a.y == b.y && a.z == b.z; }
private Vector3 Vertex (int i)
{ return vertices[triangles[i]]; }
private bool ShouldDrawEdge (int i)
{ return uniqueEdges.Count > i ? uniqueEdges[i] : true; }
private bool TestAddEdge (List<Vector3> list, Vector3 a, Vector3 b)
{
Vector3 l_a, l_b;
for (int i = 0; i < list.Count && instance.runningThreads != -1; i += 2)
{
l_a = list[i];
l_b = list[i + 1];
if ((V3Equals (l_a, a) && V3Equals (l_b, b)) ||
(V3Equals (l_a, b) && V3Equals (l_b, a)))
return false;
}
list.Add (a);
list.Add (b);
return true;
}
private void CheckEdges (object uselessObject)
{
instance.runningThreads++;
uniqueEdges.Clear ();
List<Vector3> newVerts = new List<Vector3> ((int)(triangles.Length * 1.3f));
Vector3 a, b, c;
for (int i = 0; i < triangles.Length && instance.runningThreads != -1; i += 3)
{
a = Vertex (i);
b = Vertex (i + 1);
c = Vertex (i + 2);
uniqueEdges.Add (TestAddEdge (newVerts, a, b));
uniqueEdges.Add (TestAddEdge (newVerts, b, c));
uniqueEdges.Add (TestAddEdge (newVerts, c, a));
}
newVerts.TrimExcess ();
Debug.Log ("glwireframe: thread finished in " + instance.time);
instance.runningThreads--;
}
public void CullAndBake ()
{
isVisible = true;
if (mf.renderer && mf.renderer.enabled && !mf.renderer.isVisible)
isVisible = false;
else
{
Transform tr = mf.transform;
if (tr.position != position || tr.rotation != rotation || tr.lossyScale != scale)
{
position = tr.position;
rotation = tr.rotation;
scale = tr.lossyScale;
Matrix4x4 matrix = tr.localToWorldMatrix;
vertices = mf.sharedMesh.vertices.Select (v => matrix.MultiplyPoint3x4 (v)).ToArray ();
}
}
}
public void Render ()
{
if (!isVisible)
return;
Vector3 a, b, c;
for (int i = 0; i < triangles.Length; i += 3)
{
a = Vertex (i);
b = Vertex (i + 1);
c = Vertex (i + 2);
if (ShouldDrawEdge (i))
{
GL.Vertex (a);
GL.Vertex (b);
}
if (ShouldDrawEdge (i + 1))
{
GL.Vertex (b);
GL.Vertex (c);
}
if (ShouldDrawEdge (i + 2))
{
GL.Vertex (c);
GL.Vertex (a);
}
}
}
}
private static Material mat;
public Transform[] meshRoots;
public MeshFilter[] meshes;
public bool displayWireframe = true;
public KeyCode toggleDisplayKey = KeyCode.Z;
public Color wireColor = new Color (0, 0, 0, 0.2f);
public ThreadPriority threadPriority = ThreadPriority.Normal;
public ScreenRect rect = new ScreenRect (0, 0, 150, 110);
private List<MeshWire> wires;
// stats
private float startTime;
private float time;
private float milliseconds_cullbake = 0;
private float milliseconds_render = 0;
private int runningThreads = 0;
void Start ()
{
startTime = Time.realtimeSinceStartup;
wires = new List<MeshWire> ();
SetMaterial ();
GetSetMeshes ();
}
void SetMaterial ()
{
mat = new Material (
@"Shader ""GLwire"" {
SubShader {
Pass {
Blend SrcAlpha OneMinusSrcAlpha
ZWrite On
Cull Off
BindChannels {
Bind ""vertex"", vertex
Bind ""color"", color
}
}
}
}
");
mat.hideFlags = HideFlags.HideAndDontSave;
mat.shader.hideFlags = HideFlags.HideAndDontSave;
}
void GetSetMeshes ()
{
List<MeshFilter> mf;
if (meshRoots.Length > 0 || meshes.Length > 0)
{
mf = new List<MeshFilter> ();
for (int i = 0; i < meshRoots.Length; i++)
mf.AddRange (meshRoots[i].GetComponentsInChildren<MeshFilter> ());
mf.AddRange (meshes);
mf = mf.Distinct ().ToList ();
Debug.Log ("glwireframe : " + mf.Count + " wires to render");
}
else
{
mf = (FindObjectsOfType (typeof (MeshFilter)) as MeshFilter[]).ToList ();
Debug.Log ("glwireframe : " + mf.Count + " meshfilters (all)");
}
for (int i = 0; i < mf.Count; i++)
wires.Add (new MeshWire (mf[i], this));
}
void Update ()
{
if (Input.GetKeyDown (toggleDisplayKey))
displayWireframe = !displayWireframe;
time = Time.realtimeSinceStartup - startTime;
}
void OnGUI ()
{
int maxThreads;
int useless;
ThreadPool.GetMaxThreads (out maxThreads, out useless);
GUI.Label (rect,
"GLWireframe :\n" +
" max threads : " + maxThreads + "\n" +
" running threads : " + runningThreads + "\n" +
" cull/bake : " + milliseconds_cullbake.ToString ("f") + " ms\n" +
" render : " + milliseconds_render.ToString ("f") + " ms"
);
}
void OnPostRender ()
{
if (!displayWireframe)
{
milliseconds_cullbake = 0;
milliseconds_render = 0;
return;
}
ProcessRender ();
}
void ProcessRender ()
{
Stopwatch timer = Stopwatch.StartNew ();
for (int i = 0; i < wires.Count; i++)
wires[i].CullAndBake ();
timer.Stop ();
milliseconds_cullbake = timer.ElapsedTicks / 10000f;
timer.Reset ();
timer.Start ();
mat.SetPass (0);
GL.Color (wireColor);
GL.Begin (GL.LINES);
for (int i = 0; i < wires.Count; i++)
wires[i].Render ();
GL.End ();
timer.Stop ();
milliseconds_render = timer.ElapsedTicks / 10000f;
}
void OnApplicationQuit ()
{
runningThreads = -1; // stop threads
}
}