context: #define PROTOTYPE
  #if UNITY_EDITOR || UNITY_STANDALONE
  using UnityEngine;
  using System.Collections;
  using System.Collections.Generic;
  using System.Linq;
  using ProBuilder2.Common;
  using ProBuilder2.MeshOperations;
  
  namespace ProBuilder2.Examples
  {
  
  	[RequireComponent(typeof(AudioSource))]
  	public class IcoBumpin : MonoBehaviour
  	{
  		pb_Object ico;			// A reference to the icosphere pb_Object component
  		Mesh icoMesh;			// A reference to the icosphere mesh (cached because we access the vertex array every frame)
  		Transform icoTransform;	// A reference to the icosphere transform component.  Cached because I can't remember if GameObject.transform is still a performance drain :|
  		AudioSource audioSource;// Cached reference to the audiosource.
  
  		/**
  		 * Holds a pb_Face, the normal of that face, and the index of every vertex that touches it (sharedIndices).
  		 */
  		struct FaceRef
  		{
  			public pb_Face face;
  			public Vector3 nrm;		// face normal
  			public int[] indices;	// all vertex indices (including shared connected vertices)
  
  			public FaceRef(pb_Face f, Vector3 n, int[] i)
  			{
  				face = f;
  				nrm = n;
  				indices = i;
  			}
  		}
  
  		// All faces that have been extruded
  		FaceRef[] outsides;
  
  		// Keep a copy of the original vertex array to calculate the distance from origin.
  		Vector3[] original_vertices, displaced_vertices;
  
  		// The radius of the mesh icosphere on instantiation.
  		[Range(1f, 10f)]
  		public float icoRadius = 2f;
  
  		// The number of subdivisions to give the icosphere.
  		[Range(0, 3)]
  		public int icoSubdivisions = 2;
  
  		// How far along the normal should each face be extruded when at idle (no audio input).
  		[Range(0f, 1f)]
  		public float startingExtrusion = .1f;
  
  		// The material to apply to the icosphere.
  		public Material material;
  
  		// The max distance a frequency range will extrude a face.
  		[Range(1f, 50f)]
  		public float extrusion = 30f;
  
  		// An FFT returns a spectrum including frequencies that are out of human hearing range -
  		// this restricts the number of bins used from the spectrum to the lower @fftBounds.
  		[Range(8, 128)]
  		public int fftBounds = 32;
  
  		// How high the icosphere transform will bounce (sample volume determines height).
  		[Range(0f, 10f)]
  		public float verticalBounce = 4f;
  
  		// Optionally weights the frequency amplitude when calculating extrude distance.
  		public AnimationCurve frequencyCurve;
  
  		// A reference to the line renderer that will be used to render the raw waveform.
  		public LineRenderer waveform;
  
  		// The y size of the waveform.
  		public float waveformHeight	= 2f;
  
  		// How far from the icosphere should the waveform be.
  		public float waveformRadius	= 20f;
  
  		// If @rotateWaveformRing is true, this is the speed it will travel.
  		public float waveformSpeed = .1f;
  
  		// If true, the waveform ring will randomly orbit the icosphere.
  		public bool rotateWaveformRing = false;
  
  		// If true, the waveform will bounce up and down with the icosphere.
  		public bool bounceWaveform = false;
  
  		public GameObject missingClipWarning;
  
  		// Icosphere's starting position.
  		Vector3 icoPosition = Vector3.zero;
  		float faces_length;
  
  		const float TWOPI = 6.283185f;		// 2 * PI
  		const int WAVEFORM_SAMPLES = 1024;	// How many samples make up the waveform ring.
  		const int FFT_SAMPLES = 4096;		// How many samples are used in the FFT.  More means higher resolution.
  
  		// Keep copy of the last frame's sample data to average with the current when calculating
  		// deformation amounts.  Smoothes the visual effect.
  		float[] fft = new float[FFT_SAMPLES],
  				fft_history = new float[FFT_SAMPLES],
  				data = new float[WAVEFORM_SAMPLES],
  				data_history = new float[WAVEFORM_SAMPLES];
  
  		// Root mean square of raw data (volume, but not in dB).
  		float rms = 0f, rms_history = 0f;
  
  		/**
  		 * Creates the icosphere, and loads all the cache information.
  		 */
  		void Start()
  		{
  			audioSource = GetComponent<AudioSource>();
  
  			if( audioSource.clip == null )
  				missingClipWarning.SetActive(true);
  
  			// Create a new icosphere.
  			ico = pb_ShapeGenerator.IcosahedronGenerator(icoRadius, icoSubdivisions);
  
  			// Shell is all the faces on the new icosphere.
  			pb_Face[] shell = ico.faces;
  
  			// Materials are set per-face on pb_Object meshes.  pb_Objects will automatically
  			// condense the mesh to the smallest set of subMeshes possible based on materials.
  #if !PROTOTYPE
  			foreach(pb_Face f in shell)
  				f.material = material;
  #else
  			ico.gameObject.GetComponent<MeshRenderer>().sharedMaterial = material;
  #endif
  
  			pb_Face[] connectingFaces;
  
  			// Extrude all faces on the icosphere by a small amount.  The third boolean parameter
  			// specifies that extrusion should treat each face as an individual, not try to group
  			// all faces together.
  			ico.Extrude(shell, startingExtrusion, false, out connectingFaces);
  
  			// ToMesh builds the mesh positions, submesh, and triangle arrays.  Call after adding
  			// or deleting vertices, or changing face properties.
  			ico.ToMesh();
  
  			// Refresh builds the normals, tangents, and UVs.
  			ico.Refresh();
  
  			outsides = new FaceRef[shell.Length];
  			Dictionary<int, int> lookup = ico.sharedIndices.ToDictionary();
  
  			// Populate the outsides[] cache.  This is a reference to the tops of each extruded column, including
  			// copies of the sharedIndices.
  			for(int i = 0; i < shell.Length; ++i)
  				outsides[i] = new FaceRef( 	shell[i],
  											pb_Math.Normal(ico, shell[i]),
  											ico.sharedIndices.AllIndicesWithValues(lookup, shell[i].distinctIndices).ToArray()
  											);
  
  			// Store copy of positions array un-modified
  			original_vertices = new Vector3[ico.vertices.Length];
  			System.Array.Copy(ico.vertices, original_vertices, ico.vertices.Length);
  
  			// displaced_vertices should mirror icosphere mesh vertices.
  			displaced_vertices = ico.vertices;
  
  			icoMesh = ico.msh;
  			icoTransform = ico.transform;
  
  			faces_length = (float)outsides.Length;
  
  			// Build the waveform ring.
  			icoPosition = icoTransform.position;
  #if UNITY_4_5 || UNITY_4_6 || UNITY_4_7 || UNITY_5_0 || UNITY_5_1 || UNITY_5_2 || UNITY_5_3 || UNITY_5_4
  			waveform.SetVertexCount(WAVEFORM_SAMPLES);
  #else
  			waveform.numPositions = WAVEFORM_SAMPLES;
  #endif
  
  			if( bounceWaveform )
  				waveform.transform.parent = icoTransform;
  
  			audioSource.Play();
  		}
  
  		void Update()
  		{
  			// fetch the fft spectrum
  			audioSource.GetSpectrumData(fft, 0, FFTWindow.BlackmanHarris);
  
  			// get raw data for waveform
  			audioSource.GetOutputData(data, 0);
  
  			// calculate root mean square (volume)
  			rms = RMS(data);
  
  			/**
  			 * For each face, translate the vertices some distance depending on the frequency range assigned.
  			 * Not using the TranslateVertices() pb_Object extension method because as a convenience, that method
  			 * gathers the sharedIndices per-face on every call, which while not tremondously expensive in most
  			 * contexts, is far too slow for use when dealing with audio, and especially so when the mesh is
  			 * somewhat large.
  			 */
  			for(int i = 0; i < outsides.Length; i++)
  			{
  				float normalizedIndex = (i/faces_length);
  
  
gt: 				int n = (int)(normalizedIndex*fftBounds);