Assignment for RMIT Mixed Reality in 2020
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.

430 lines
18 KiB

  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using UnityEngine;
  5. using UnityEngine.Rendering;
  6. public class OvrAvatarMaterialManager : MonoBehaviour
  7. {
  8. private Renderer TargetRenderer;
  9. private AvatarTextureArrayProperties[] TextureArrays;
  10. public enum TextureType
  11. {
  12. DiffuseTextures = 0,
  13. NormalMaps,
  14. RoughnessMaps,
  15. Count
  16. }
  17. // Material properties required to render a single component
  18. public struct AvatarComponentMaterialProperties
  19. {
  20. public ovrAvatarBodyPartType TypeIndex;
  21. public Color Color;
  22. public Texture2D[] Textures;
  23. public float DiffuseIntensity;
  24. public float RimIntensity;
  25. public float ReflectionIntensity;
  26. }
  27. // Texture arrays
  28. public struct AvatarTextureArrayProperties
  29. {
  30. public Texture2D[] Textures;
  31. public Texture2DArray TextureArray;
  32. }
  33. // Material property arrays that are pushed to the shader
  34. public struct AvatarMaterialPropertyBlock
  35. {
  36. public Vector4[] Colors;
  37. public float[] DiffuseIntensities;
  38. public float[] RimIntensities;
  39. public float[] ReflectionIntensities;
  40. }
  41. private readonly string[] TextureTypeToShaderProperties =
  42. {
  43. "_MainTex", // TextureType.DiffuseTextures = 0
  44. "_NormalMap", // TextureType.NormalMaps
  45. "_RoughnessMap" // TextureType.RoughnessMaps
  46. };
  47. // Container class for all the data relating to an avatar material description
  48. [System.Serializable]
  49. public class AvatarMaterialConfig
  50. {
  51. public AvatarComponentMaterialProperties[] ComponentMaterialProperties;
  52. public AvatarMaterialPropertyBlock MaterialPropertyBlock;
  53. }
  54. // Local config that this manager instance will render
  55. public AvatarMaterialConfig LocalAvatarConfig = new AvatarMaterialConfig();
  56. public List<ReflectionProbeBlendInfo> ReflectionProbes = new List<ReflectionProbeBlendInfo>();
  57. // Cache the previous shader when swapping in the loading shader.
  58. private Shader CombinedShader;
  59. // Shader properties
  60. public static string AVATAR_SHADER_LOADER = "OvrAvatar/Avatar_Mobile_Loader";
  61. public static string AVATAR_SHADER_MAINTEX = "_MainTex";
  62. public static string AVATAR_SHADER_NORMALMAP = "_NormalMap";
  63. public static string AVATAR_SHADER_ROUGHNESSMAP = "_RoughnessMap";
  64. public static string AVATAR_SHADER_COLOR = "_BaseColor";
  65. public static string AVATAR_SHADER_DIFFUSEINTENSITY = "_DiffuseIntensity";
  66. public static string AVATAR_SHADER_RIMINTENSITY = "_RimIntensity";
  67. public static string AVATAR_SHADER_REFLECTIONINTENSITY = "_ReflectionIntensity";
  68. public static string AVATAR_SHADER_CUBEMAP = "_Cubemap";
  69. public static string AVATAR_SHADER_ALPHA = "_Alpha";
  70. public static string AVATAR_SHADER_LOADING_DIMMER = "_LoadingDimmer";
  71. public static string AVATAR_SHADER_IRIS_COLOR = "_MaskColorIris";
  72. public static string AVATAR_SHADER_LIP_COLOR = "_MaskColorLips";
  73. public static string AVATAR_SHADER_BROW_COLOR = "_MaskColorBrows";
  74. public static string AVATAR_SHADER_LASH_COLOR = "_MaskColorLashes";
  75. public static string AVATAR_SHADER_SCLERA_COLOR = "_MaskColorSclera";
  76. public static string AVATAR_SHADER_GUM_COLOR = "_MaskColorGums";
  77. public static string AVATAR_SHADER_TEETH_COLOR = "_MaskColorTeeth";
  78. public static string AVATAR_SHADER_LIP_SMOOTHNESS = "_LipSmoothness";
  79. // Diffuse Intensity constants: body, clothes, eyewear, hair, beard
  80. public static float[] DiffuseIntensities = new[] {0.3f, 0.1f, 0f, 0.15f, 0.15f};
  81. // Rim Intensity constants: body, clothes, eyewear, hair, beard
  82. public static float[] RimIntensities = new[] {5f, 2f, 2.84f, 4f, 4f};
  83. // Reflection Intensity constants: body, clothes, eyewear, hair, beard
  84. public static float[] ReflectionIntensities = new[] {0f, 0.3f, 0.4f, 0f, 0f};
  85. // Loading animation
  86. private const float LOADING_ANIMATION_AMPLITUDE = 0.5f;
  87. private const float LOADING_ANIMATION_PERIOD = 0.35f;
  88. private const float LOADING_ANIMATION_CURVE_SCALE = 0.25f;
  89. private const float LOADING_ANIMATION_DIMMER_MIN = 0.3f;
  90. public void CreateTextureArrays()
  91. {
  92. const int componentCount = (int)ovrAvatarBodyPartType.Count;
  93. const int textureTypeCount = (int)TextureType.Count;
  94. LocalAvatarConfig.ComponentMaterialProperties = new AvatarComponentMaterialProperties[componentCount];
  95. LocalAvatarConfig.MaterialPropertyBlock.Colors = new Vector4[componentCount];
  96. LocalAvatarConfig.MaterialPropertyBlock.DiffuseIntensities = new float[componentCount];
  97. LocalAvatarConfig.MaterialPropertyBlock.RimIntensities = new float[componentCount];
  98. LocalAvatarConfig.MaterialPropertyBlock.ReflectionIntensities = new float[componentCount];
  99. for (int i = 0; i < LocalAvatarConfig.ComponentMaterialProperties.Length; ++i)
  100. {
  101. LocalAvatarConfig.ComponentMaterialProperties[i].Textures = new Texture2D[textureTypeCount];
  102. }
  103. TextureArrays = new AvatarTextureArrayProperties[textureTypeCount];
  104. }
  105. public void SetRenderer(Renderer renderer)
  106. {
  107. TargetRenderer = renderer;
  108. TargetRenderer.GetClosestReflectionProbes(ReflectionProbes);
  109. }
  110. public void OnCombinedMeshReady()
  111. {
  112. InitTextureArrays();
  113. SetMaterialPropertyBlock();
  114. // Callback to delete texture set once the avatar is fully loaded
  115. StartCoroutine(RunLoadingAnimation(DeleteTextureSet));
  116. }
  117. // Add a texture ID so that it's managed for deletion
  118. public void AddTextureIDToTextureManager(ulong assetID, bool isSingleComponent)
  119. {
  120. OvrAvatarSDKManager.Instance.GetTextureCopyManager().AddTextureIDToTextureSet(
  121. GetInstanceID(), assetID, isSingleComponent);
  122. }
  123. // Once avatar loading is completed trigger the texture set for deletion
  124. private void DeleteTextureSet()
  125. {
  126. OvrAvatarSDKManager.Instance.GetTextureCopyManager().DeleteTextureSet(GetInstanceID());
  127. }
  128. // Prepare texture arrays and copy to GPU
  129. public void InitTextureArrays()
  130. {
  131. var localProps = LocalAvatarConfig.ComponentMaterialProperties[0];
  132. for (int i = 0; i < TextureArrays.Length && i < localProps.Textures.Length; i++)
  133. {
  134. TextureArrays[i].TextureArray = new Texture2DArray(
  135. localProps.Textures[0].height, localProps.Textures[0].width,
  136. LocalAvatarConfig.ComponentMaterialProperties.Length,
  137. localProps.Textures[0].format,
  138. true,
  139. QualitySettings.activeColorSpace == ColorSpace.Gamma ? false : true
  140. ) { filterMode = FilterMode.Trilinear,
  141. //Can probably get away with 4 for roughness maps as well, once we switch
  142. //to BC7/ASTC4x4 texture compression.
  143. anisoLevel = (TextureType)i == TextureType.RoughnessMaps ? 16 : 4 };
  144. //So a name shows up in Renderdoc
  145. TextureArrays[i].TextureArray.name = string.Format("Texture Array Type: {0}", (TextureType)i);
  146. TextureArrays[i].Textures
  147. = new Texture2D[LocalAvatarConfig.ComponentMaterialProperties.Length];
  148. for (int j = 0; j < LocalAvatarConfig.ComponentMaterialProperties.Length; j++)
  149. {
  150. TextureArrays[i].Textures[j]
  151. = LocalAvatarConfig.ComponentMaterialProperties[j].Textures[i];
  152. //So a name shows up in Renderdoc
  153. TextureArrays[i].Textures[j].name = string.Format("Texture Type: {0} Component: {1}", (TextureType)i, j);
  154. }
  155. ProcessTexturesWithMips(
  156. TextureArrays[i].Textures,
  157. localProps.Textures[i].height,
  158. TextureArrays[i].TextureArray);
  159. }
  160. }
  161. private void ProcessTexturesWithMips(
  162. Texture2D[] textures,
  163. int texArrayResolution,
  164. Texture2DArray texArray)
  165. {
  166. for (int i = 0; i < textures.Length; i++)
  167. {
  168. int currentMipSize = texArrayResolution;
  169. int correctNumberOfMips = textures[i].mipmapCount - 1;
  170. // Add mips to copyTexture queue in low-high order from correctNumberOfMips..0
  171. for (int mipLevel = correctNumberOfMips; mipLevel >= 0; mipLevel--)
  172. {
  173. int mipSize = texArrayResolution / currentMipSize;
  174. OvrAvatarSDKManager.Instance.GetTextureCopyManager().CopyTexture(
  175. textures[i],
  176. texArray,
  177. mipLevel,
  178. mipSize,
  179. i,
  180. false);
  181. currentMipSize /= 2;
  182. }
  183. }
  184. }
  185. private void SetMaterialPropertyBlock()
  186. {
  187. if (TargetRenderer != null)
  188. {
  189. for (int i = 0; i < LocalAvatarConfig.ComponentMaterialProperties.Length; i++)
  190. {
  191. LocalAvatarConfig.MaterialPropertyBlock.Colors[i]
  192. = LocalAvatarConfig.ComponentMaterialProperties[i].Color;
  193. LocalAvatarConfig.MaterialPropertyBlock.DiffuseIntensities[i] = DiffuseIntensities[i];
  194. LocalAvatarConfig.MaterialPropertyBlock.RimIntensities[i] = RimIntensities[i];
  195. LocalAvatarConfig.MaterialPropertyBlock.ReflectionIntensities[i] = ReflectionIntensities[i];
  196. }
  197. }
  198. }
  199. private void ApplyMaterialPropertyBlock()
  200. {
  201. MaterialPropertyBlock materialPropertyBlock = new MaterialPropertyBlock();
  202. materialPropertyBlock.SetVectorArray(AVATAR_SHADER_COLOR,
  203. LocalAvatarConfig.MaterialPropertyBlock.Colors);
  204. materialPropertyBlock.SetFloatArray(AVATAR_SHADER_DIFFUSEINTENSITY,
  205. LocalAvatarConfig.MaterialPropertyBlock.DiffuseIntensities);
  206. materialPropertyBlock.SetFloatArray(AVATAR_SHADER_RIMINTENSITY,
  207. LocalAvatarConfig.MaterialPropertyBlock.RimIntensities);
  208. materialPropertyBlock.SetFloatArray(AVATAR_SHADER_REFLECTIONINTENSITY,
  209. LocalAvatarConfig.MaterialPropertyBlock.ReflectionIntensities);
  210. TargetRenderer.GetClosestReflectionProbes(ReflectionProbes);
  211. if (ReflectionProbes != null && ReflectionProbes.Count > 0 && ReflectionProbes[0].probe.texture != null)
  212. {
  213. materialPropertyBlock.SetTexture(AVATAR_SHADER_CUBEMAP, ReflectionProbes[0].probe.texture);
  214. }
  215. for (int i = 0; i < TextureArrays.Length; i++)
  216. {
  217. materialPropertyBlock.SetTexture(TextureTypeToShaderProperties[i],
  218. TextureArrays[(int)(TextureType)i].TextureArray);
  219. }
  220. TargetRenderer.SetPropertyBlock(materialPropertyBlock);
  221. }
  222. // Return a component type based on name
  223. public static ovrAvatarBodyPartType GetComponentType(string objectName)
  224. {
  225. if (objectName.Contains("0"))
  226. {
  227. return ovrAvatarBodyPartType.Body;
  228. }
  229. else if (objectName.Contains("1"))
  230. {
  231. return ovrAvatarBodyPartType.Clothing;
  232. }
  233. else if (objectName.Contains("2"))
  234. {
  235. return ovrAvatarBodyPartType.Eyewear;
  236. }
  237. else if (objectName.Contains("3"))
  238. {
  239. return ovrAvatarBodyPartType.Hair;
  240. }
  241. else if (objectName.Contains("4"))
  242. {
  243. return ovrAvatarBodyPartType.Beard;
  244. }
  245. return ovrAvatarBodyPartType.Count;
  246. }
  247. UInt64 GetTextureIDForType(ovrAvatarPBSMaterialState materialState, TextureType type)
  248. {
  249. if (type == TextureType.DiffuseTextures)
  250. {
  251. return materialState.albedoTextureID;
  252. }
  253. else if (type == TextureType.NormalMaps)
  254. {
  255. return materialState.normalTextureID;
  256. }
  257. else if (type == TextureType.RoughnessMaps)
  258. {
  259. return materialState.metallicnessTextureID;
  260. }
  261. return 0;
  262. }
  263. public void ValidateTextures(ovrAvatarPBSMaterialState[] materialStates)
  264. {
  265. var props = LocalAvatarConfig.ComponentMaterialProperties;
  266. int[] heights = new int[(int)TextureType.Count];
  267. TextureFormat[] formats = new TextureFormat[(int)TextureType.Count];
  268. for (var propIndex = 0; propIndex < props.Length; propIndex++)
  269. {
  270. for (var index = 0; index < props[propIndex].Textures.Length; index++)
  271. {
  272. if (props[propIndex].Textures[index] == null)
  273. {
  274. throw new System.Exception(
  275. props[propIndex].TypeIndex.ToString()
  276. + "Invalid: "
  277. + ((TextureType)index).ToString());
  278. }
  279. heights[index] = props[propIndex].Textures[index].height;
  280. formats[index] = props[propIndex].Textures[index].format;
  281. }
  282. }
  283. for (int textureIndex = 0; textureIndex < (int)TextureType.Count; textureIndex++)
  284. {
  285. for (var propIndex = 1; propIndex < props.Length; propIndex++)
  286. {
  287. if (props[propIndex - 1].Textures[textureIndex].height
  288. != props[propIndex].Textures[textureIndex].height)
  289. {
  290. throw new System.Exception(
  291. props[propIndex].TypeIndex.ToString()
  292. + " Mismatching Resolutions: "
  293. + ((TextureType)textureIndex).ToString()
  294. + " "
  295. + props[propIndex - 1].Textures[textureIndex].height
  296. + " (ID: "
  297. + GetTextureIDForType(materialStates[propIndex - 1], (TextureType)textureIndex)
  298. + ") vs "
  299. + props[propIndex].Textures[textureIndex].height
  300. + " (ID: "
  301. + GetTextureIDForType(materialStates[propIndex], (TextureType)textureIndex)
  302. + ") Ensure you are using ASTC texture compression on Android or turn off CombineMeshes");
  303. }
  304. if (props[propIndex - 1].Textures[textureIndex].format
  305. != props[propIndex].Textures[textureIndex].format)
  306. {
  307. throw new System.Exception(
  308. props[propIndex].TypeIndex.ToString()
  309. + " Mismatching Formats: "
  310. + ((TextureType)textureIndex).ToString()
  311. + " "
  312. + props[propIndex - 1].Textures[textureIndex].format
  313. + " (ID: "
  314. + GetTextureIDForType(materialStates[propIndex - 1], (TextureType)textureIndex)
  315. + ") vs "
  316. + props[propIndex].Textures[textureIndex].format
  317. + " (ID: "
  318. + GetTextureIDForType(materialStates[propIndex], (TextureType)textureIndex)
  319. + ") Ensure you are using ASTC texture compression on Android or turn off CombineMeshes");
  320. }
  321. }
  322. }
  323. }
  324. // Loading animation on the Dimmer properyt
  325. // Smooth sine lerp every 0.3 seconds between 0.25 and 0.5
  326. private IEnumerator RunLoadingAnimation(Action callBack)
  327. {
  328. // Set the material to single component while the avatar loads
  329. CombinedShader = TargetRenderer.sharedMaterial.shader;
  330. // Save shader properties
  331. int srcBlend = TargetRenderer.sharedMaterial.GetInt("_SrcBlend");
  332. int dstBlend = TargetRenderer.sharedMaterial.GetInt("_DstBlend");
  333. string lightModeTag = TargetRenderer.sharedMaterial.GetTag("LightMode", false);
  334. string renderTypeTag = TargetRenderer.sharedMaterial.GetTag("RenderType", false);
  335. string renderQueueTag = TargetRenderer.sharedMaterial.GetTag("Queue", false);
  336. string ignoreProjectorTag = TargetRenderer.sharedMaterial.GetTag("IgnoreProjector", false);
  337. int renderQueue = TargetRenderer.sharedMaterial.renderQueue;
  338. bool transparentQueue = TargetRenderer.sharedMaterial.IsKeywordEnabled("_ALPHATEST_ON");
  339. // Swap in loading shader
  340. TargetRenderer.sharedMaterial.shader = Shader.Find(AVATAR_SHADER_LOADER);
  341. TargetRenderer.sharedMaterial.SetColor(AVATAR_SHADER_COLOR, Color.white);
  342. while (OvrAvatarSDKManager.Instance.GetTextureCopyManager().GetTextureCount() > 0)
  343. {
  344. float distance = (LOADING_ANIMATION_AMPLITUDE * Mathf.Sin(Time.timeSinceLevelLoad / LOADING_ANIMATION_PERIOD) +
  345. LOADING_ANIMATION_AMPLITUDE) * (LOADING_ANIMATION_CURVE_SCALE) + LOADING_ANIMATION_DIMMER_MIN;
  346. TargetRenderer.sharedMaterial.SetFloat(AVATAR_SHADER_LOADING_DIMMER, distance);
  347. yield return null;
  348. }
  349. // Swap back main shader
  350. TargetRenderer.sharedMaterial.SetFloat(AVATAR_SHADER_LOADING_DIMMER, 1f);
  351. TargetRenderer.sharedMaterial.shader = CombinedShader;
  352. // Restore shader properties
  353. TargetRenderer.sharedMaterial.SetInt("_SrcBlend", srcBlend);
  354. TargetRenderer.sharedMaterial.SetInt("_DstBlend", dstBlend);
  355. TargetRenderer.sharedMaterial.SetOverrideTag("LightMode", lightModeTag);
  356. TargetRenderer.sharedMaterial.SetOverrideTag("RenderType", renderTypeTag);
  357. TargetRenderer.sharedMaterial.SetOverrideTag("Queue", renderQueueTag);
  358. TargetRenderer.sharedMaterial.SetOverrideTag("IgnoreProjector", ignoreProjectorTag);
  359. if (transparentQueue)
  360. {
  361. TargetRenderer.sharedMaterial.EnableKeyword("_ALPHATEST_ON");
  362. TargetRenderer.sharedMaterial.EnableKeyword("_ALPHABLEND_ON");
  363. TargetRenderer.sharedMaterial.EnableKeyword("_ALPHAPREMULTIPLY_ON");
  364. }
  365. else
  366. {
  367. TargetRenderer.sharedMaterial.DisableKeyword("_ALPHATEST_ON");
  368. TargetRenderer.sharedMaterial.DisableKeyword("_ALPHABLEND_ON");
  369. TargetRenderer.sharedMaterial.DisableKeyword("_ALPHAPREMULTIPLY_ON");
  370. }
  371. TargetRenderer.sharedMaterial.renderQueue = renderQueue;
  372. ApplyMaterialPropertyBlock();
  373. if (callBack != null)
  374. {
  375. callBack();
  376. }
  377. }
  378. }