[C#] GLWireframe

Cette section est destinée aux scripts partagés par la communauté. Chaque post est destiné à un script. Suivez bien les recommandations.
Répondre
Avatar de l’utilisateur
artemisart
Messages : 1893
Inscription : 21 Juin 2011 19:51
Localisation : Centre
Contact :

[C#] GLWireframe

Message par artemisart » 17 Fév 2013 13:20

-NOM DU SCRIPT : GLWireframe

-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).
-SCRIPT :

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
	}
}
Si vous avez des suggestions, idées d'améliorations ou d'optimisation, n’hésitez pas ;) .
Dernière édition par artemisart le 27 Oct 2014 13:51, édité 20 fois.

Avatar de l’utilisateur
Max
Newser
Newser
Messages : 7506
Inscription : 30 Juil 2011 13:57

Re: [C#] GLWireframe

Message par Max » 17 Fév 2013 14:09

J'ai testé, ça marche bien à priori ;)

Cela semble basé sur le code de Chubbspet non ?
Pas d'aide par MP, le forum est là pour ça.
En cas de doute sur les bonnes pratiques à adopter sur le forum, consulter la Charte et sa FAQ

Avatar de l’utilisateur
Franck
Bricoleur
Bricoleur
Messages : 2878
Inscription : 08 Jan 2011 18:43
Localisation : Tours

Re: [C#] GLWireframe

Message par Franck » 17 Fév 2013 14:16

Merci. 8-)
Dés fois j'bug, dés fois j'bug pas.

Avatar de l’utilisateur
artemisart
Messages : 1893
Inscription : 21 Juin 2011 19:51
Localisation : Centre
Contact :

Re: [C#] GLWireframe

Message par artemisart » 17 Fév 2013 14:20

Je connaissais pas du tout ce code en fait :mrgreen: .
Bon là je suis en train d'ajouter le support des mesh dynamiques, mais unity crash à chaque fois que je test le code :/

Code : Tout sélectionner

public class GLWireframe : MonoBehaviour
{
	private static Material mat;
	
	public Transform meshesRoot;
	public bool displayWireframe = true;
	public Color wireColor = Color.black;
	
	private List<Vector3> staticVertices = new List<Vector3> ();
	private List<Mesh> dynamicMeshes = new List<Mesh> ();
	private List<Transform> dynamicTransforms = new List<Transform> ();
	
	void Awake ()
	{
		// create material
		mat = new Material (
"Shader \"GLcurveline\" {" +
"SubShader { Pass { " +
"    Blend SrcAlpha OneMinusSrcAlpha " +
"    ZWrite On Cull Off Fog { Mode Off } " +
"    BindChannels {" +
"      Bind \"vertex\", vertex Bind \"color\", color }" +
"} } }"
);
		mat.hideFlags = HideFlags.HideAndDontSave;
		mat.shader.hideFlags = HideFlags.HideAndDontSave;
		
		MeshFilter[] mf;
		if (meshesRoot != null)
			mf = meshesRoot.GetComponentsInChildren<MeshFilter> ();
		else
			mf = FindObjectsOfType (typeof (MeshFilter)) as MeshFilter[];
		
		Transform tr; // cache transform
		Mesh mesh; // cache mesh
		for (int i = 0; i < mf.Length; i++) // foreach meshfilter
		{
			// if GO is static, it caches vertices in a list
			if (mf[i].gameObject.isStatic)
			{
				tr = mf[i].transform;
				mesh = mf[i].mesh;
				
				// foreach triangle
				for (int tri = 0; tri < mesh.triangles.Length; tri += 3)
				{
					staticVertices.Add ( tr.TransformPoint ( mesh.vertices[ mesh.triangles[tri    ] ] ) );
					staticVertices.Add ( tr.TransformPoint ( mesh.vertices[ mesh.triangles[tri + 1] ] ) );
					staticVertices.Add ( tr.TransformPoint ( mesh.vertices[ mesh.triangles[tri + 2] ] ) );
				}
			}
			else
			{
				dynamicMeshes.Add (mf[i].mesh);
				dynamicTransforms.Add (mf[i].transform);
			}
		}
	}
	
	void OnPostRender ()
	{
		if (!displayWireframe) return;
		
		mat.SetPass (0);
		
		GL.Begin (GL.LINES);
		GL.PushMatrix ();
		GL.Color (wireColor);
		
		// foreach static triangle
		for (int i = 0; i < staticVertices.Count; i += 3)
		{
			GL.Vertex (staticVertices[i    ]); // first line
			GL.Vertex (staticVertices[i + 1]);
			
			GL.Vertex (staticVertices[i + 1]); // second line
			GL.Vertex (staticVertices[i + 2]);
			
			GL.Vertex (staticVertices[i + 2]); // third line
			GL.Vertex (staticVertices[i    ]);
		}
		
		// foreach dynamic meshfilter
		Mesh mesh; // cache mesh
		for (int i = 0; i < dynamicMeshes.Count; i++)
		{
			mesh = dynamicMeshes[i];
			// foreach triangle
			// ça crash là apparemment
			for (int tri = 0; tri < mesh.triangles.Length; i += 3)
			{
				GL.Vertex (mesh.vertices[ mesh.triangles[tri    ] ]); // first line
				GL.Vertex (mesh.vertices[ mesh.triangles[tri + 1] ]);
				
				GL.Vertex (mesh.vertices[ mesh.triangles[tri + 1] ]); // second line
				GL.Vertex (mesh.vertices[ mesh.triangles[tri + 2] ]);
				
				GL.Vertex (mesh.vertices[ mesh.triangles[tri + 2] ]); // third line
				GL.Vertex (mesh.vertices[ mesh.triangles[tri    ] ]);
			}
		}
		
		GL.PopMatrix ();
		GL.End ();
	}
}

Avatar de l’utilisateur
Max
Newser
Newser
Messages : 7506
Inscription : 30 Juil 2011 13:57

Re: [C#] GLWireframe

Message par Max » 17 Fév 2013 14:37

artemisart a écrit :J mais unity crash à chaque fois que je test le code :/
Ben, j'ai pas vraiment testé, mais en lisant rapidement ton code, y-a un truc qui ressort :mrgreen:
for (int tri = 0; tri < mesh.triangles.Length; i += 3)
c'est pas plutôt
for (int tri = 0; tri < mesh.triangles.Length; tri += 3)
:roll:
Pas d'aide par MP, le forum est là pour ça.
En cas de doute sur les bonnes pratiques à adopter sur le forum, consulter la Charte et sa FAQ

Avatar de l’utilisateur
artemisart
Messages : 1893
Inscription : 21 Juin 2011 19:51
Localisation : Centre
Contact :

Re: [C#] GLWireframe

Message par artemisart » 17 Fév 2013 16:50

Effectivement, grosse boulette :oops:
Du coup j'ai pu le finir et ça marche plutôt bien, j'update le premier poste.

Avatar de l’utilisateur
artemisart
Messages : 1893
Inscription : 21 Juin 2011 19:51
Localisation : Centre
Contact :

Re: [C#] GLWireframe

Message par artemisart » 17 Fév 2013 17:18

Post mis à jour !

EDIT :
Un petit aperçu :
En haut j'ai désactivé les renderers et mis la couleur du wire à (1, 1, 1, 1) (j'aurais peut-être du baisser un peu l'alpha) et le background de la caméra en noir.
En bas tout est par défaut (le wire se "fond" avec les meshes parce que la caméra est loin, normalement ça fait pas ça).
Image

Avatar de l’utilisateur
artemisart
Messages : 1893
Inscription : 21 Juin 2011 19:51
Localisation : Centre
Contact :

Re: [C#] GLWireframe

Message par artemisart » 20 Fév 2013 23:19

Je viens de me rendre compte d'un très (très) gros soucis de perfs lors du build du jeu mais pas dans Unity (ce qui perd tout son intérêt car on a déjà des fonctions équivalentes et natives en editor).
Premièrement car, pour détecter les meshes statiques, j'utilise gameObject.isStatic qui est editor-only et renvoit toujours false dans un jeu buildé... (donc tous les meshes sont considérés comme dynamiques, mais je devrais pouvoir le résoudre dans peu de temps).
Deuxièmement car l'affichage des meshes dynamiques est 3x fois plus long (!) en WebPlayer qu'en editor, et là, je sèche :( , si vous avez une idée...

Pour info, voici les statistiques de la scène de test :
Environ 3500-4000 edges répartis dans 22 meshes, dont 5 dynamiques.
En editor : 0-1ms pour 3286 edges statiques et 3-6ms pour 5 meshes dynamiques.
En webplayer : aucun edge statique, 95-102ms pour 22 meshes dynamiques :lol: (je rigole mais c'est nerveux).

Avatar de l’utilisateur
artemisart
Messages : 1893
Inscription : 21 Juin 2011 19:51
Localisation : Centre
Contact :

Re: [C#] GLWireframe

Message par artemisart » 21 Fév 2013 00:50

WOW !
J'ai tout refait, et le résultat est 17x plus rapide ! (en webplayer) chuis très très content :mrgreen:

Les stats, avec la même scène qu'au dessus :
En editor : 2-5ms de baking et 1 ms de rendu.
En webplayer : 6ms de baking max, 0ms de rendu !

Le script fonctionne maintenant avec un système de baking, c'est à dire qu'il sauvegarde une liste de vertices (en world space) pour chaque mesh, et que cette liste est rebuilée seulement si le transform change.
J'update le premier message dès que possible.

EDIT : premier post mis à jour.

Avatar de l’utilisateur
Max
Newser
Newser
Messages : 7506
Inscription : 30 Juil 2011 13:57

Re: [C#] GLWireframe

Message par Max » 21 Fév 2013 10:16

C'est vrai qu'on sentait bien que les perf en prenaient vite un gros cout :mrgreen:
Donc bien vue cette mise à jour ;)
Pas d'aide par MP, le forum est là pour ça.
En cas de doute sur les bonnes pratiques à adopter sur le forum, consulter la Charte et sa FAQ

Répondre

Revenir vers « Scripts »