The Swartz-Manning’s first exhibit will provide a detailed history of Aaron Swartz Day. https://www.aaronswartzday.org/vr
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

IcoBumpin.cs 8.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. #if UNITY_EDITOR || UNITY_STANDALONE
  2. using UnityEngine;
  3. using System.Collections;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using ProBuilder2.Common;
  7. using ProBuilder2.MeshOperations;
  8. namespace ProBuilder2.Examples
  9. {
  10. [RequireComponent(typeof(AudioSource))]
  11. public class IcoBumpin : MonoBehaviour
  12. {
  13. pb_Object ico; // A reference to the icosphere pb_Object component
  14. Mesh icoMesh; // A reference to the icosphere mesh (cached because we access the vertex array every frame)
  15. Transform icoTransform; // A reference to the icosphere transform component. Cached because I can't remember if GameObject.transform is still a performance drain :|
  16. AudioSource audioSource;// Cached reference to the audiosource.
  17. /**
  18. * Holds a pb_Face, the normal of that face, and the index of every vertex that touches it (sharedIndices).
  19. */
  20. struct FaceRef
  21. {
  22. public pb_Face face;
  23. public Vector3 nrm; // face normal
  24. public int[] indices; // all vertex indices (including shared connected vertices)
  25. public FaceRef(pb_Face f, Vector3 n, int[] i)
  26. {
  27. face = f;
  28. nrm = n;
  29. indices = i;
  30. }
  31. }
  32. // All faces that have been extruded
  33. FaceRef[] outsides;
  34. // Keep a copy of the original vertex array to calculate the distance from origin.
  35. Vector3[] original_vertices, displaced_vertices;
  36. // The radius of the mesh icosphere on instantiation.
  37. [Range(1f, 10f)]
  38. public float icoRadius = 2f;
  39. // The number of subdivisions to give the icosphere.
  40. [Range(0, 3)]
  41. public int icoSubdivisions = 2;
  42. // How far along the normal should each face be extruded when at idle (no audio input).
  43. [Range(0f, 1f)]
  44. public float startingExtrusion = .1f;
  45. // The material to apply to the icosphere.
  46. public Material material;
  47. // The max distance a frequency range will extrude a face.
  48. [Range(1f, 50f)]
  49. public float extrusion = 30f;
  50. // An FFT returns a spectrum including frequencies that are out of human hearing range -
  51. // this restricts the number of bins used from the spectrum to the lower @fftBounds.
  52. [Range(8, 128)]
  53. public int fftBounds = 32;
  54. // How high the icosphere transform will bounce (sample volume determines height).
  55. [Range(0f, 10f)]
  56. public float verticalBounce = 4f;
  57. // Optionally weights the frequency amplitude when calculating extrude distance.
  58. public AnimationCurve frequencyCurve;
  59. // A reference to the line renderer that will be used to render the raw waveform.
  60. public LineRenderer waveform;
  61. // The y size of the waveform.
  62. public float waveformHeight = 2f;
  63. // How far from the icosphere should the waveform be.
  64. public float waveformRadius = 20f;
  65. // If @rotateWaveformRing is true, this is the speed it will travel.
  66. public float waveformSpeed = .1f;
  67. // If true, the waveform ring will randomly orbit the icosphere.
  68. public bool rotateWaveformRing = false;
  69. // If true, the waveform will bounce up and down with the icosphere.
  70. public bool bounceWaveform = false;
  71. public GameObject missingClipWarning;
  72. // Icosphere's starting position.
  73. Vector3 icoPosition = Vector3.zero;
  74. float faces_length;
  75. const float TWOPI = 6.283185f; // 2 * PI
  76. const int WAVEFORM_SAMPLES = 1024; // How many samples make up the waveform ring.
  77. const int FFT_SAMPLES = 4096; // How many samples are used in the FFT. More means higher resolution.
  78. // Keep copy of the last frame's sample data to average with the current when calculating
  79. // deformation amounts. Smoothes the visual effect.
  80. float[] fft = new float[FFT_SAMPLES],
  81. fft_history = new float[FFT_SAMPLES],
  82. data = new float[WAVEFORM_SAMPLES],
  83. data_history = new float[WAVEFORM_SAMPLES];
  84. // Root mean square of raw data (volume, but not in dB).
  85. float rms = 0f, rms_history = 0f;
  86. /**
  87. * Creates the icosphere, and loads all the cache information.
  88. */
  89. void Start()
  90. {
  91. audioSource = GetComponent<AudioSource>();
  92. if( audioSource.clip == null )
  93. missingClipWarning.SetActive(true);
  94. // Create a new icosphere.
  95. ico = pb_ShapeGenerator.IcosahedronGenerator(icoRadius, icoSubdivisions);
  96. // Shell is all the faces on the new icosphere.
  97. pb_Face[] shell = ico.faces;
  98. // Materials are set per-face on pb_Object meshes. pb_Objects will automatically
  99. // condense the mesh to the smallest set of subMeshes possible based on materials.
  100. #if !PROTOTYPE
  101. foreach(pb_Face f in shell)
  102. f.material = material;
  103. #else
  104. ico.gameObject.GetComponent<MeshRenderer>().sharedMaterial = material;
  105. #endif
  106. // Extrude all faces on the icosphere by a small amount. The third boolean parameter
  107. // specifies that extrusion should treat each face as an individual, not try to group
  108. // all faces together.
  109. ico.Extrude(shell, ExtrudeMethod.IndividualFaces, startingExtrusion);
  110. // ToMesh builds the mesh positions, submesh, and triangle arrays. Call after adding
  111. // or deleting vertices, or changing face properties.
  112. ico.ToMesh();
  113. // Refresh builds the normals, tangents, and UVs.
  114. ico.Refresh();
  115. outsides = new FaceRef[shell.Length];
  116. Dictionary<int, int> lookup = ico.sharedIndices.ToDictionary();
  117. // Populate the outsides[] cache. This is a reference to the tops of each extruded column, including
  118. // copies of the sharedIndices.
  119. for(int i = 0; i < shell.Length; ++i)
  120. outsides[i] = new FaceRef( shell[i],
  121. pb_Math.Normal(ico, shell[i]),
  122. ico.sharedIndices.AllIndicesWithValues(lookup, shell[i].distinctIndices).ToArray()
  123. );
  124. // Store copy of positions array un-modified
  125. original_vertices = new Vector3[ico.vertices.Length];
  126. System.Array.Copy(ico.vertices, original_vertices, ico.vertices.Length);
  127. // displaced_vertices should mirror icosphere mesh vertices.
  128. displaced_vertices = ico.vertices;
  129. icoMesh = ico.msh;
  130. icoTransform = ico.transform;
  131. faces_length = (float)outsides.Length;
  132. // Build the waveform ring.
  133. icoPosition = icoTransform.position;
  134. #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
  135. waveform.SetVertexCount(WAVEFORM_SAMPLES);
  136. #elif UNITY_5_5
  137. waveform.numPositions = WAVEFORM_SAMPLES;
  138. #else
  139. waveform.positionCount = WAVEFORM_SAMPLES;
  140. #endif
  141. if( bounceWaveform )
  142. waveform.transform.parent = icoTransform;
  143. audioSource.Play();
  144. }
  145. void Update()
  146. {
  147. // fetch the fft spectrum
  148. audioSource.GetSpectrumData(fft, 0, FFTWindow.BlackmanHarris);
  149. // get raw data for waveform
  150. audioSource.GetOutputData(data, 0);
  151. // calculate root mean square (volume)
  152. rms = RMS(data);
  153. /**
  154. * For each face, translate the vertices some distance depending on the frequency range assigned.
  155. * Not using the TranslateVertices() pb_Object extension method because as a convenience, that method
  156. * gathers the sharedIndices per-face on every call, which while not tremondously expensive in most
  157. * contexts, is far too slow for use when dealing with audio, and especially so when the mesh is
  158. * somewhat large.
  159. */
  160. for(int i = 0; i < outsides.Length; i++)
  161. {
  162. float normalizedIndex = (i/faces_length);
  163. int n = (int)(normalizedIndex*fftBounds);
  164. Vector3 displacement = outsides[i].nrm * ( ((fft[n]+fft_history[n]) * .5f) * (frequencyCurve.Evaluate(normalizedIndex) * .5f + .5f)) * extrusion;
  165. foreach(int t in outsides[i].indices)
  166. {
  167. displaced_vertices[t] = original_vertices[t] + displacement;
  168. }
  169. }
  170. Vector3 vec = Vector3.zero;
  171. // Waveform ring
  172. for(int i = 0; i < WAVEFORM_SAMPLES; i++)
  173. {
  174. int n = i < WAVEFORM_SAMPLES-1 ? i : 0;
  175. vec.x = Mathf.Cos((float)n/WAVEFORM_SAMPLES * TWOPI) * (waveformRadius + (((data[n] + data_history[n]) * .5f) * waveformHeight));
  176. vec.z = Mathf.Sin((float)n/WAVEFORM_SAMPLES * TWOPI) * (waveformRadius + (((data[n] + data_history[n]) * .5f) * waveformHeight));
  177. vec.y = 0f;
  178. waveform.SetPosition(i, vec);
  179. }
  180. // Ring rotation
  181. if( rotateWaveformRing )
  182. {
  183. Vector3 rot = waveform.transform.localRotation.eulerAngles;
  184. rot.x = Mathf.PerlinNoise(Time.time * waveformSpeed, 0f) * 360f;
  185. rot.y = Mathf.PerlinNoise(0f, Time.time * waveformSpeed) * 360f;
  186. waveform.transform.localRotation = Quaternion.Euler(rot);
  187. }
  188. icoPosition.y = -verticalBounce + ((rms + rms_history) * verticalBounce);
  189. icoTransform.position = icoPosition;
  190. // Keep copy of last FFT samples so we can average with the current. Smoothes the movement.
  191. System.Array.Copy(fft, fft_history, FFT_SAMPLES);
  192. System.Array.Copy(data, data_history, WAVEFORM_SAMPLES);
  193. rms_history = rms;
  194. icoMesh.vertices = displaced_vertices;
  195. }
  196. /**
  197. * Root mean square is a good approximation of perceived loudness.
  198. */
  199. float RMS(float[] arr)
  200. {
  201. float v = 0f,
  202. len = (float)arr.Length;
  203. for(int i = 0; i < len; i++)
  204. v += Mathf.Abs(arr[i]);
  205. return Mathf.Sqrt(v / (float)len);
  206. }
  207. }
  208. }
  209. #endif