MB3_TextureCombinerNonTextureProperties.cs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501
  1. using UnityEngine;
  2. using System;
  3. using System.Collections;
  4. using System.Collections.Generic;
  5. using System.Reflection;
  6. namespace DigitalOpus.MB.Core
  7. {
  8. public class MB3_TextureCombinerNonTextureProperties
  9. {
  10. public interface MaterialProperty
  11. {
  12. string PropertyName { get; set; }
  13. MaterialPropertyValueAveraged GetAverageCalculator();
  14. object GetDefaultValue();
  15. }
  16. public class MaterialPropertyFloat : MaterialProperty
  17. {
  18. public string PropertyName { get; set; }
  19. MaterialPropertyValueAveragedFloat _averageCalc;
  20. float _defaultValue;
  21. public MaterialPropertyFloat(string name, float defValue)
  22. {
  23. _averageCalc = new MaterialPropertyValueAveragedFloat();
  24. _defaultValue = defValue;
  25. PropertyName = name;
  26. }
  27. public MaterialPropertyValueAveraged GetAverageCalculator()
  28. {
  29. return _averageCalc;
  30. }
  31. public object GetDefaultValue()
  32. {
  33. return _defaultValue;
  34. }
  35. }
  36. public class MaterialPropertyColor : MaterialProperty
  37. {
  38. public string PropertyName { get; set; }
  39. MaterialPropertyValueAveragedColor _averageCalc;
  40. Color _defaultValue;
  41. public MaterialPropertyColor(string name, Color defaultVal)
  42. {
  43. _averageCalc = new MaterialPropertyValueAveragedColor();
  44. _defaultValue = defaultVal;
  45. PropertyName = name;
  46. }
  47. public MaterialPropertyValueAveraged GetAverageCalculator()
  48. {
  49. return _averageCalc;
  50. }
  51. public object GetDefaultValue()
  52. {
  53. return _defaultValue;
  54. }
  55. }
  56. public interface MaterialPropertyValueAveraged
  57. {
  58. void TryGetPropValueFromMaterialAndBlendIntoAverage(Material mat, MaterialProperty property);
  59. object GetAverage();
  60. int NumValues();
  61. void SetAverageValueOrDefaultOnMaterial(Material mat, MaterialProperty property);
  62. }
  63. public class MaterialPropertyValueAveragedFloat : MaterialPropertyValueAveraged
  64. {
  65. public float averageVal;
  66. public int numValues;
  67. public void TryGetPropValueFromMaterialAndBlendIntoAverage(Material mat, MaterialProperty property)
  68. {
  69. if (mat.HasProperty(property.PropertyName))
  70. {
  71. float v = mat.GetFloat(property.PropertyName);
  72. averageVal = averageVal * ((float)numValues) / (numValues + 1) + v / (numValues + 1);
  73. numValues++;
  74. }
  75. }
  76. public object GetAverage()
  77. {
  78. return averageVal;
  79. }
  80. public int NumValues()
  81. {
  82. return numValues;
  83. }
  84. public void SetAverageValueOrDefaultOnMaterial(Material mat, MaterialProperty property)
  85. {
  86. if (mat.HasProperty(property.PropertyName))
  87. {
  88. if (numValues > 0)
  89. {
  90. mat.SetFloat(property.PropertyName, averageVal);
  91. } else
  92. {
  93. mat.SetFloat(property.PropertyName, (float)property.GetDefaultValue());
  94. }
  95. }
  96. }
  97. }
  98. public class MaterialPropertyValueAveragedColor : MaterialPropertyValueAveraged
  99. {
  100. public Color averageVal;
  101. public int numValues;
  102. public void TryGetPropValueFromMaterialAndBlendIntoAverage(Material mat, MaterialProperty property)
  103. {
  104. if (mat.HasProperty(property.PropertyName))
  105. {
  106. Color v = mat.GetColor(property.PropertyName);
  107. averageVal = averageVal * ((float)numValues) / (numValues + 1) + v / (numValues + 1);
  108. numValues++;
  109. }
  110. }
  111. public object GetAverage()
  112. {
  113. return averageVal;
  114. }
  115. public int NumValues()
  116. {
  117. return numValues;
  118. }
  119. public void SetAverageValueOrDefaultOnMaterial(Material mat, MaterialProperty property)
  120. {
  121. if (mat.HasProperty(property.PropertyName))
  122. {
  123. if (numValues > 0)
  124. {
  125. mat.SetColor(property.PropertyName, averageVal);
  126. }
  127. else
  128. {
  129. mat.SetColor(property.PropertyName, (Color) property.GetDefaultValue());
  130. }
  131. }
  132. }
  133. }
  134. public struct TexPropertyNameColorPair
  135. {
  136. public string name;
  137. public Color color;
  138. public TexPropertyNameColorPair(string nm, Color col)
  139. {
  140. name = nm;
  141. color = col;
  142. }
  143. }
  144. private interface NonTextureProperties
  145. {
  146. bool NonTexturePropertiesAreEqual(Material a, Material b);
  147. Texture2D TintTextureWithTextureCombiner(Texture2D t, MB_TexSet sourceMaterial, ShaderTextureProperty shaderPropertyName);
  148. void AdjustNonTextureProperties(Material resultMat, List<ShaderTextureProperty> texPropertyNames, MB2_EditorMethodsInterface editorMethods);
  149. Color GetColorForTemporaryTexture(Material matIfBlender, ShaderTextureProperty texProperty);
  150. Color GetColorAsItWouldAppearInAtlasIfNoTexture(Material matIfBlender, ShaderTextureProperty texProperty);
  151. }
  152. private class NonTexturePropertiesDontBlendProps : NonTextureProperties
  153. {
  154. MB3_TextureCombinerNonTextureProperties _textureProperties;
  155. public NonTexturePropertiesDontBlendProps(MB3_TextureCombinerNonTextureProperties textureProperties)
  156. {
  157. _textureProperties = textureProperties;
  158. }
  159. public bool NonTexturePropertiesAreEqual(Material a, Material b)
  160. {
  161. Debug.Assert(_textureProperties._considerNonTextureProperties == false);
  162. return true;
  163. }
  164. public Texture2D TintTextureWithTextureCombiner(Texture2D t, MB_TexSet sourceMaterial, ShaderTextureProperty shaderPropertyName)
  165. {
  166. Debug.Assert(_textureProperties._considerNonTextureProperties == false);
  167. Debug.LogError("TintTextureWithTextureCombiner should never be called if resultMaterialTextureBlender is null");
  168. return t;
  169. }
  170. public void AdjustNonTextureProperties(Material resultMat, List<ShaderTextureProperty> texPropertyNames, MB2_EditorMethodsInterface editorMethods)
  171. {
  172. Debug.Assert(_textureProperties._considerNonTextureProperties == false);
  173. if (resultMat == null || texPropertyNames == null) return;
  174. for (int nonTexPropIdx = 0; nonTexPropIdx < _textureProperties._nonTextureProperties.Length; nonTexPropIdx++)
  175. {
  176. MaterialProperty nonTexProperty = _textureProperties._nonTextureProperties[nonTexPropIdx];
  177. if (resultMat.HasProperty(nonTexProperty.PropertyName))
  178. {
  179. nonTexProperty.GetAverageCalculator().SetAverageValueOrDefaultOnMaterial(resultMat, nonTexProperty);
  180. }
  181. }
  182. if (editorMethods != null)
  183. {
  184. editorMethods.CommitChangesToAssets();
  185. }
  186. }
  187. public Color GetColorAsItWouldAppearInAtlasIfNoTexture(Material matIfBlender, ShaderTextureProperty texProperty)
  188. {
  189. Debug.Assert(false, "Should never be called");
  190. return Color.white;
  191. }
  192. public Color GetColorForTemporaryTexture(Material matIfBlender, ShaderTextureProperty texProperty)
  193. {
  194. Debug.Assert(_textureProperties._considerNonTextureProperties == false);
  195. if (texProperty.isNormalMap)
  196. {
  197. return NEUTRAL_NORMAL_MAP_COLOR;
  198. }
  199. else if (_textureProperties.textureProperty2DefaultColorMap.ContainsKey(texProperty.name))
  200. {
  201. return _textureProperties.textureProperty2DefaultColorMap[texProperty.name];
  202. }
  203. return new Color(1f, 1f, 1f, 0f);
  204. }
  205. }
  206. private class NonTexturePropertiesBlendProps : NonTextureProperties
  207. {
  208. MB3_TextureCombinerNonTextureProperties _textureProperties;
  209. TextureBlender resultMaterialTextureBlender;
  210. public NonTexturePropertiesBlendProps(MB3_TextureCombinerNonTextureProperties textureProperties, TextureBlender resultMats)
  211. {
  212. resultMaterialTextureBlender = resultMats;
  213. _textureProperties = textureProperties;
  214. }
  215. public bool NonTexturePropertiesAreEqual(Material a, Material b)
  216. {
  217. Debug.Assert(resultMaterialTextureBlender != null);
  218. return resultMaterialTextureBlender.NonTexturePropertiesAreEqual(a, b);
  219. }
  220. public Texture2D TintTextureWithTextureCombiner(Texture2D t, MB_TexSet sourceMaterial, ShaderTextureProperty shaderPropertyName)
  221. {
  222. Debug.Assert(resultMaterialTextureBlender != null);
  223. resultMaterialTextureBlender.OnBeforeTintTexture(sourceMaterial.matsAndGOs.mats[0].mat, shaderPropertyName.name);
  224. if (_textureProperties.LOG_LEVEL >= MB2_LogLevel.trace) Debug.Log(string.Format("Blending texture {0} mat {1} with non-texture properties using TextureBlender {2}", t.name, sourceMaterial.matsAndGOs.mats[0].mat, resultMaterialTextureBlender));
  225. for (int i = 0; i < t.height; i++)
  226. {
  227. Color[] cs = t.GetPixels(0, i, t.width, 1);
  228. for (int j = 0; j < cs.Length; j++)
  229. {
  230. cs[j] = resultMaterialTextureBlender.OnBlendTexturePixel(shaderPropertyName.name, cs[j]);
  231. }
  232. t.SetPixels(0, i, t.width, 1, cs);
  233. }
  234. t.Apply();
  235. return t;
  236. }
  237. public void AdjustNonTextureProperties(Material resultMat, List<ShaderTextureProperty> texPropertyNames, MB2_EditorMethodsInterface editorMethods)
  238. {
  239. if (resultMat == null || texPropertyNames == null) return;
  240. //try to use a texture blender if we can find one to set the non-texture property values
  241. if (_textureProperties.LOG_LEVEL >= MB2_LogLevel.debug) Debug.Log("Adjusting non texture properties using TextureBlender for shader: " + resultMat.shader.name);
  242. resultMaterialTextureBlender.SetNonTexturePropertyValuesOnResultMaterial(resultMat);
  243. if (editorMethods != null)
  244. {
  245. editorMethods.CommitChangesToAssets();
  246. }
  247. }
  248. public Color GetColorAsItWouldAppearInAtlasIfNoTexture(Material matIfBlender, ShaderTextureProperty texProperty)
  249. {
  250. resultMaterialTextureBlender.OnBeforeTintTexture(matIfBlender, texProperty.name);
  251. Color c = GetColorForTemporaryTexture(matIfBlender, texProperty);
  252. return resultMaterialTextureBlender.OnBlendTexturePixel(texProperty.name, c);
  253. }
  254. public Color GetColorForTemporaryTexture(Material matIfBlender, ShaderTextureProperty texProperty)
  255. {
  256. return resultMaterialTextureBlender.GetColorIfNoTexture(matIfBlender, texProperty);
  257. }
  258. }
  259. public static Color NEUTRAL_NORMAL_MAP_COLOR = new Color(.5f, .5f, 1f);
  260. TexPropertyNameColorPair[] defaultTextureProperty2DefaultColorMap = new TexPropertyNameColorPair[]
  261. {
  262. new TexPropertyNameColorPair("_MainTex", new Color(1f, 1f, 1f, 0f)),
  263. new TexPropertyNameColorPair("_MetallicGlossMap", new Color(0f, 0f, 0f, 1f)),
  264. new TexPropertyNameColorPair("_ParallaxMap", new Color(0f, 0f, 0f, 0f)),
  265. new TexPropertyNameColorPair("_OcclusionMap", new Color(1f, 1f, 1f, 1f)),
  266. new TexPropertyNameColorPair("_EmissionMap", new Color(0f, 0f, 0f, 0f)),
  267. new TexPropertyNameColorPair("_DetailMask", new Color(0f, 0f, 0f, 0f)),
  268. };
  269. MB3_TextureCombinerNonTextureProperties.MaterialProperty[] _nonTextureProperties = new MB3_TextureCombinerNonTextureProperties.MaterialProperty[]
  270. {
  271. new MB3_TextureCombinerNonTextureProperties.MaterialPropertyColor("_Color", Color.white),
  272. //new MB3_TextureCombinerNonTextureProperties.MaterialPropertyFloat("_Cutoff"),
  273. new MB3_TextureCombinerNonTextureProperties.MaterialPropertyFloat("_Glossiness", .5f),
  274. new MB3_TextureCombinerNonTextureProperties.MaterialPropertyFloat("_GlossMapScale", 1f),
  275. new MB3_TextureCombinerNonTextureProperties.MaterialPropertyFloat("_Metallic", 0f),
  276. //new MB3_TextureCombinerNonTextureProperties.MaterialPropertyFloat("_SpecularHightlights"),
  277. //new MB3_TextureCombinerNonTextureProperties.MaterialPropertyFloat("_GlossyReflections"),
  278. new MB3_TextureCombinerNonTextureProperties.MaterialPropertyFloat("_BumpScale", .1f),
  279. new MB3_TextureCombinerNonTextureProperties.MaterialPropertyFloat("_Parallax", .02f),
  280. new MB3_TextureCombinerNonTextureProperties.MaterialPropertyFloat("_OcclusionStrength", 1f),
  281. new MB3_TextureCombinerNonTextureProperties.MaterialPropertyColor("_EmissionColor", Color.black),
  282. };
  283. MB2_LogLevel LOG_LEVEL = MB2_LogLevel.info;
  284. bool _considerNonTextureProperties = false;
  285. private TextureBlender resultMaterialTextureBlender;
  286. private TextureBlender[] textureBlenders = new TextureBlender[0];
  287. private Dictionary<string, Color> textureProperty2DefaultColorMap = new Dictionary<string, Color>();
  288. private NonTextureProperties _nonTexturePropertiesBlender;
  289. public MB3_TextureCombinerNonTextureProperties(MB2_LogLevel ll, bool considerNonTextureProps)
  290. {
  291. LOG_LEVEL = ll;
  292. _considerNonTextureProperties = considerNonTextureProps;
  293. textureProperty2DefaultColorMap = new Dictionary<string, Color>();
  294. for (int i = 0; i < defaultTextureProperty2DefaultColorMap.Length; i++)
  295. {
  296. textureProperty2DefaultColorMap.Add(defaultTextureProperty2DefaultColorMap[i].name,
  297. defaultTextureProperty2DefaultColorMap[i].color);
  298. _nonTexturePropertiesBlender = new NonTexturePropertiesDontBlendProps(this);
  299. }
  300. }
  301. internal void CollectAverageValuesOfNonTextureProperties(Material resultMaterial, Material mat)
  302. {
  303. for (int i = 0; i < _nonTextureProperties.Length; i++)
  304. {
  305. MB3_TextureCombinerNonTextureProperties.MaterialProperty prop = _nonTextureProperties[i];
  306. if (resultMaterial.HasProperty(prop.PropertyName))
  307. {
  308. prop.GetAverageCalculator().TryGetPropValueFromMaterialAndBlendIntoAverage(mat, prop);
  309. }
  310. }
  311. }
  312. internal void LoadTextureBlendersIfNeeded(Material resultMaterial)
  313. {
  314. if (_considerNonTextureProperties)
  315. {
  316. LoadTextureBlenders();
  317. FindBestTextureBlender(resultMaterial);
  318. }
  319. }
  320. #if UNITY_WSA && !UNITY_EDITOR
  321. //not defined for WSA runtime
  322. #else
  323. private static bool InterfaceFilter(Type typeObj, System.Object criteriaObj)
  324. {
  325. return typeObj.ToString() == criteriaObj.ToString();
  326. }
  327. #endif
  328. private void FindBestTextureBlender(Material resultMaterial)
  329. {
  330. Debug.Assert(_considerNonTextureProperties);
  331. resultMaterialTextureBlender = FindMatchingTextureBlender(resultMaterial.shader.name);
  332. if (resultMaterialTextureBlender != null)
  333. {
  334. if (LOG_LEVEL >= MB2_LogLevel.debug) Debug.Log("Using Consider Non-Texture Properties found a TextureBlender for result material. Using: " + resultMaterialTextureBlender);
  335. }
  336. else
  337. {
  338. if (LOG_LEVEL >= MB2_LogLevel.error) Debug.LogWarning("Using _considerNonTextureProperties could not find a TextureBlender that matches the shader on the result material. Using the Fallback Texture Blender.");
  339. resultMaterialTextureBlender = new TextureBlenderFallback();
  340. }
  341. _nonTexturePropertiesBlender = new NonTexturePropertiesBlendProps(this, resultMaterialTextureBlender);
  342. }
  343. private void LoadTextureBlenders()
  344. {
  345. #if UNITY_WSA && !UNITY_EDITOR
  346. //not defined for WSA runtime
  347. #else
  348. Debug.Assert(_considerNonTextureProperties);
  349. string qualifiedInterfaceName = "DigitalOpus.MB.Core.TextureBlender";
  350. var interfaceFilter = new TypeFilter(InterfaceFilter);
  351. List<Type> types = new List<Type>();
  352. foreach (Assembly ass in AppDomain.CurrentDomain.GetAssemblies())
  353. {
  354. System.Collections.IEnumerable typesIterator = null;
  355. try
  356. {
  357. typesIterator = ass.GetTypes();
  358. }
  359. catch (Exception e)
  360. {
  361. //Debug.Log("The assembly that I could not read types for was: " + ass.GetName());
  362. //suppress error
  363. e.Equals(null);
  364. }
  365. if (typesIterator != null)
  366. {
  367. foreach (Type ty in ass.GetTypes())
  368. {
  369. var myInterfaces = ty.FindInterfaces(interfaceFilter, qualifiedInterfaceName);
  370. if (myInterfaces.Length > 0)
  371. {
  372. types.Add(ty);
  373. }
  374. }
  375. }
  376. }
  377. TextureBlender fallbackTB = null;
  378. List<TextureBlender> textureBlendersList = new List<TextureBlender>();
  379. foreach (Type tt in types)
  380. {
  381. if (!tt.IsAbstract && !tt.IsInterface)
  382. {
  383. TextureBlender instance = (TextureBlender) System.Activator.CreateInstance(tt);
  384. if (instance is TextureBlenderFallback)
  385. {
  386. fallbackTB = instance;
  387. }
  388. else
  389. {
  390. textureBlendersList.Add(instance);
  391. }
  392. }
  393. }
  394. if (fallbackTB != null) textureBlendersList.Add(fallbackTB); // must come last in list
  395. textureBlenders = textureBlendersList.ToArray();
  396. if (LOG_LEVEL >= MB2_LogLevel.debug)
  397. {
  398. Debug.Log(string.Format("Loaded {0} TextureBlenders.", textureBlenders.Length));
  399. }
  400. #endif
  401. }
  402. internal bool NonTexturePropertiesAreEqual(Material a, Material b)
  403. {
  404. return _nonTexturePropertiesBlender.NonTexturePropertiesAreEqual(a, b);
  405. }
  406. internal Texture2D TintTextureWithTextureCombiner(Texture2D t, MB_TexSet sourceMaterial, ShaderTextureProperty shaderPropertyName)
  407. {
  408. return _nonTexturePropertiesBlender.TintTextureWithTextureCombiner(t, sourceMaterial, shaderPropertyName);
  409. }
  410. //If we are switching from a Material that uses color properties to
  411. //using atlases don't want some properties such as _Color to be copied
  412. //from the original material because the atlas texture will be multiplied
  413. //by that color
  414. internal void AdjustNonTextureProperties(Material resultMat, List<ShaderTextureProperty> texPropertyNames, MB2_EditorMethodsInterface editorMethods)
  415. {
  416. if (resultMat == null || texPropertyNames == null) return;
  417. _nonTexturePropertiesBlender.AdjustNonTextureProperties(resultMat, texPropertyNames, editorMethods);
  418. }
  419. internal Color GetColorAsItWouldAppearInAtlasIfNoTexture(Material matIfBlender, ShaderTextureProperty texProperty)
  420. {
  421. return _nonTexturePropertiesBlender.GetColorAsItWouldAppearInAtlasIfNoTexture(matIfBlender, texProperty);
  422. }
  423. internal Color GetColorForTemporaryTexture(Material matIfBlender, ShaderTextureProperty texProperty)
  424. {
  425. return _nonTexturePropertiesBlender.GetColorForTemporaryTexture(matIfBlender, texProperty);
  426. }
  427. private TextureBlender FindMatchingTextureBlender(string shaderName)
  428. {
  429. for (int i = 0; i < textureBlenders.Length; i++)
  430. {
  431. if (textureBlenders[i].DoesShaderNameMatch(shaderName))
  432. {
  433. return textureBlenders[i];
  434. }
  435. }
  436. return null;
  437. }
  438. }
  439. }