MB3_TextureCombiner.cs 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751
  1. //----------------------------------------------
  2. // MeshBaker
  3. // Copyright © 2011-2012 Ian Deane
  4. //----------------------------------------------
  5. using UnityEngine;
  6. using System.Collections;
  7. using System.IO;
  8. using System;
  9. using System.Collections.Generic;
  10. using System.Text;
  11. using System.Reflection;
  12. /*
  13. Test different texture packers
  14. Test lots of multiple material configs
  15. Try using on Coast scene
  16. */
  17. /*
  18. Notes on Normal Maps in Unity3d
  19. Unity stores normal maps in a non standard format for some platforms. Think of the standard format as being english, unity's as being
  20. french. The raw image files in the project folder are in english, the AssetImporter converts them to french. Texture2D.GetPixels returns
  21. french. This is a problem when we build an atlas from Texture2D objects and save the result in the project folder.
  22. Unity wants us to flag this file as a normal map but if we do it is effectively translated twice.
  23. Solutions:
  24. 1) convert the normal map to english just before saving to project. Then set the normal flag and let the Importer do translation.
  25. This was rejected because Unity doesn't translate for all platforms. I would need to check with every version of Unity which platforms
  26. use which format.
  27. 2) Uncheck "normal map" on importer before bake and re-check after bake. This is the solution I am using.
  28. */
  29. namespace DigitalOpus.MB.Core
  30. {
  31. [System.Serializable]
  32. public class ShaderTextureProperty
  33. {
  34. public string name;
  35. public bool isNormalMap;
  36. public ShaderTextureProperty(string n,
  37. bool norm)
  38. {
  39. name = n;
  40. isNormalMap = norm;
  41. }
  42. public override bool Equals(object obj)
  43. {
  44. if (!(obj is ShaderTextureProperty)) return false;
  45. ShaderTextureProperty b = (ShaderTextureProperty)obj;
  46. if (!name.Equals(b.name)) return false;
  47. if (isNormalMap != b.isNormalMap) return false;
  48. return true;
  49. }
  50. public override int GetHashCode()
  51. {
  52. return base.GetHashCode();
  53. }
  54. public static string[] GetNames(List<ShaderTextureProperty> props)
  55. {
  56. string[] ss = new string[props.Count];
  57. for (int i = 0; i < ss.Length; i++)
  58. {
  59. ss[i] = props[i].name;
  60. }
  61. return ss;
  62. }
  63. }
  64. [System.Serializable]
  65. public class MB3_TextureCombiner
  66. {
  67. private class TemporaryTexture
  68. {
  69. internal string property;
  70. internal Texture2D texture;
  71. public TemporaryTexture(string prop, Texture2D tex)
  72. {
  73. property = prop;
  74. texture = tex;
  75. }
  76. }
  77. /**
  78. Same as CombineTexturesIntoAtlases except this version runs as a coroutine to spread the load of baking textures at runtime across several frames
  79. */
  80. public class CombineTexturesIntoAtlasesCoroutineResult
  81. {
  82. public bool success = true;
  83. public bool isFinished = false;
  84. }
  85. public MB2_LogLevel LOG_LEVEL = MB2_LogLevel.info;
  86. [SerializeField]
  87. protected MB2_TextureBakeResults _textureBakeResults;
  88. public MB2_TextureBakeResults textureBakeResults
  89. {
  90. get { return _textureBakeResults; }
  91. set { _textureBakeResults = value; }
  92. }
  93. [SerializeField]
  94. protected int _atlasPadding = 1;
  95. public int atlasPadding
  96. {
  97. get { return _atlasPadding; }
  98. set { _atlasPadding = value; }
  99. }
  100. [SerializeField]
  101. protected int _maxAtlasSize = 1;
  102. public int maxAtlasSize
  103. {
  104. get { return _maxAtlasSize; }
  105. set { _maxAtlasSize = value; }
  106. }
  107. [SerializeField]
  108. protected int _maxAtlasWidthOverride = 4096;
  109. public virtual int maxAtlasWidthOverride
  110. {
  111. get { return _maxAtlasWidthOverride; }
  112. set { _maxAtlasWidthOverride = value; }
  113. }
  114. [SerializeField]
  115. protected int _maxAtlasHeightOverride = 4096;
  116. public virtual int maxAtlasHeightOverride
  117. {
  118. get { return _maxAtlasHeightOverride; }
  119. set { _maxAtlasHeightOverride = value; }
  120. }
  121. [SerializeField]
  122. protected bool _useMaxAtlasWidthOverride = false;
  123. public virtual bool useMaxAtlasWidthOverride
  124. {
  125. get { return _useMaxAtlasWidthOverride; }
  126. set { _useMaxAtlasWidthOverride = value; }
  127. }
  128. [SerializeField]
  129. protected bool _useMaxAtlasHeightOverride = false;
  130. public virtual bool useMaxAtlasHeightOverride
  131. {
  132. get { return _useMaxAtlasHeightOverride; }
  133. set { _useMaxAtlasHeightOverride = value; }
  134. }
  135. [SerializeField]
  136. protected bool _resizePowerOfTwoTextures = false;
  137. public bool resizePowerOfTwoTextures
  138. {
  139. get { return _resizePowerOfTwoTextures; }
  140. set { _resizePowerOfTwoTextures = value; }
  141. }
  142. [SerializeField]
  143. protected bool _fixOutOfBoundsUVs = false;
  144. public bool fixOutOfBoundsUVs
  145. {
  146. get { return _fixOutOfBoundsUVs; }
  147. set { _fixOutOfBoundsUVs = value; }
  148. }
  149. [SerializeField]
  150. protected int _maxTilingBakeSize = 1024;
  151. public int maxTilingBakeSize
  152. {
  153. get { return _maxTilingBakeSize; }
  154. set { _maxTilingBakeSize = value; }
  155. }
  156. [SerializeField]
  157. protected bool _saveAtlasesAsAssets = false;
  158. public bool saveAtlasesAsAssets
  159. {
  160. get { return _saveAtlasesAsAssets; }
  161. set { _saveAtlasesAsAssets = value; }
  162. }
  163. [SerializeField]
  164. protected MB2_PackingAlgorithmEnum _packingAlgorithm = MB2_PackingAlgorithmEnum.UnitysPackTextures;
  165. public MB2_PackingAlgorithmEnum packingAlgorithm
  166. {
  167. get { return _packingAlgorithm; }
  168. set { _packingAlgorithm = value; }
  169. }
  170. [SerializeField]
  171. protected bool _meshBakerTexturePackerForcePowerOfTwo = true;
  172. public bool meshBakerTexturePackerForcePowerOfTwo
  173. {
  174. get { return _meshBakerTexturePackerForcePowerOfTwo; }
  175. set { _meshBakerTexturePackerForcePowerOfTwo = value; }
  176. }
  177. [SerializeField]
  178. protected List<ShaderTextureProperty> _customShaderPropNames = new List<ShaderTextureProperty>();
  179. public List<ShaderTextureProperty> customShaderPropNames
  180. {
  181. get { return _customShaderPropNames; }
  182. set { _customShaderPropNames = value; }
  183. }
  184. [SerializeField]
  185. protected bool _normalizeTexelDensity = false;
  186. [SerializeField]
  187. protected bool _considerNonTextureProperties = false;
  188. public bool considerNonTextureProperties
  189. {
  190. get { return _considerNonTextureProperties; }
  191. set { _considerNonTextureProperties = value; }
  192. }
  193. //copies of textures created for the the atlas baking that should be destroyed in finalize
  194. private List<TemporaryTexture> _temporaryTextures = new List<TemporaryTexture>();
  195. //so we can undo read flag on procedural materials in finalize
  196. //internal List<ProceduralMaterialInfo> _proceduralMaterials = new List<ProceduralMaterialInfo>();
  197. //This runs a coroutine without pausing it is used to build the textures from the editor
  198. public static bool _RunCorutineWithoutPauseIsRunning = false;
  199. public static void RunCorutineWithoutPause(IEnumerator cor, int recursionDepth)
  200. {
  201. if (recursionDepth == 0)
  202. {
  203. _RunCorutineWithoutPauseIsRunning = true;
  204. }
  205. if (recursionDepth > 20)
  206. {
  207. Debug.LogError("Recursion Depth Exceeded.");
  208. return;
  209. }
  210. while (cor.MoveNext())
  211. {
  212. object retObj = cor.Current;
  213. if (retObj is YieldInstruction)
  214. {
  215. //do nothing
  216. }
  217. else if (retObj == null)
  218. {
  219. //do nothing
  220. }
  221. else if (retObj is IEnumerator)
  222. {
  223. RunCorutineWithoutPause((IEnumerator)cor.Current, recursionDepth + 1);
  224. }
  225. }
  226. if (recursionDepth == 0)
  227. {
  228. _RunCorutineWithoutPauseIsRunning = false;
  229. }
  230. }
  231. /**<summary>Combines meshes and generates texture atlases. NOTE running coroutines at runtime does not work in Unity 4</summary>
  232. * <param name="progressInfo">A delegate function that will be called to report progress.</param>
  233. * <param name="textureEditorMethods">If called from the editor should be an instance of MB2_EditorMethods. If called at runtime should be null.</param>
  234. * <remarks>Combines meshes and generates texture atlases</remarks> */
  235. public bool CombineTexturesIntoAtlases(ProgressUpdateDelegate progressInfo, MB_AtlasesAndRects resultAtlasesAndRects, Material resultMaterial, List<GameObject> objsToMesh, List<Material> allowedMaterialsFilter, MB2_EditorMethodsInterface textureEditorMethods = null, List<AtlasPackingResult> packingResults = null, bool onlyPackRects = false)
  236. {
  237. CombineTexturesIntoAtlasesCoroutineResult result = new CombineTexturesIntoAtlasesCoroutineResult();
  238. RunCorutineWithoutPause(_CombineTexturesIntoAtlases(progressInfo, result, resultAtlasesAndRects, resultMaterial, objsToMesh, allowedMaterialsFilter, textureEditorMethods, packingResults, onlyPackRects), 0);
  239. return result.success;
  240. }
  241. //float _maxTimePerFrameForCoroutine;
  242. public IEnumerator CombineTexturesIntoAtlasesCoroutine(ProgressUpdateDelegate progressInfo, MB_AtlasesAndRects resultAtlasesAndRects, Material resultMaterial, List<GameObject> objsToMesh, List<Material> allowedMaterialsFilter, MB2_EditorMethodsInterface textureEditorMethods = null, CombineTexturesIntoAtlasesCoroutineResult coroutineResult = null, float maxTimePerFrame = .01f, List<AtlasPackingResult> packingResults = null, bool onlyPackRects = false)
  243. {
  244. if (!_RunCorutineWithoutPauseIsRunning && (MBVersion.GetMajorVersion() < 5 || (MBVersion.GetMajorVersion() == 5 && MBVersion.GetMinorVersion() < 3)))
  245. {
  246. Debug.LogError("Running the texture combiner as a coroutine only works in Unity 5.3 and higher");
  247. yield return null;
  248. }
  249. coroutineResult.success = true;
  250. coroutineResult.isFinished = false;
  251. if (maxTimePerFrame <= 0f)
  252. {
  253. Debug.LogError("maxTimePerFrame must be a value greater than zero");
  254. coroutineResult.isFinished = true;
  255. yield break;
  256. }
  257. //_maxTimePerFrameForCoroutine = maxTimePerFrame;
  258. yield return _CombineTexturesIntoAtlases(progressInfo, coroutineResult, resultAtlasesAndRects, resultMaterial, objsToMesh, allowedMaterialsFilter, textureEditorMethods, packingResults, onlyPackRects);
  259. coroutineResult.isFinished = true;
  260. yield break;
  261. }
  262. IEnumerator _CombineTexturesIntoAtlases(ProgressUpdateDelegate progressInfo, CombineTexturesIntoAtlasesCoroutineResult result, MB_AtlasesAndRects resultAtlasesAndRects, Material resultMaterial, List<GameObject> objsToMesh, List<Material> allowedMaterialsFilter, MB2_EditorMethodsInterface textureEditorMethods, List<AtlasPackingResult> atlasPackingResult, bool onlyPackRects)
  263. {
  264. System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
  265. sw.Start();
  266. try
  267. {
  268. _temporaryTextures.Clear();
  269. MeshBakerMaterialTexture.readyToBuildAtlases = false;
  270. if (textureEditorMethods != null)
  271. {
  272. textureEditorMethods.Clear();
  273. textureEditorMethods.OnPreTextureBake();
  274. }
  275. if (objsToMesh == null || objsToMesh.Count == 0)
  276. {
  277. Debug.LogError("No meshes to combine. Please assign some meshes to combine.");
  278. result.success = false;
  279. yield break;
  280. }
  281. if (_atlasPadding < 0)
  282. {
  283. Debug.LogError("Atlas padding must be zero or greater.");
  284. result.success = false;
  285. yield break;
  286. }
  287. if (_maxTilingBakeSize < 2 || _maxTilingBakeSize > 4096)
  288. {
  289. Debug.LogError("Invalid value for max tiling bake size.");
  290. result.success = false;
  291. yield break;
  292. }
  293. for (int i = 0; i < objsToMesh.Count; i++)
  294. {
  295. Material[] ms = MB_Utility.GetGOMaterials(objsToMesh[i]);
  296. for (int j = 0; j < ms.Length; j++)
  297. {
  298. Material m = ms[j];
  299. if (m == null)
  300. {
  301. Debug.LogError("Game object " + objsToMesh[i] + " has a null material");
  302. result.success = false;
  303. yield break;
  304. }
  305. }
  306. }
  307. if (progressInfo != null)
  308. progressInfo("Collecting textures for " + objsToMesh.Count + " meshes.", .01f);
  309. MB3_TextureCombinerPipeline.TexturePipelineData data = LoadPipelineData(resultMaterial, new List<ShaderTextureProperty>(), objsToMesh, allowedMaterialsFilter, new List<MB_TexSet>());
  310. if (! MB3_TextureCombinerPipeline._CollectPropertyNames(data, LOG_LEVEL))
  311. {
  312. result.success = false;
  313. yield break;
  314. }
  315. if (_fixOutOfBoundsUVs && (_packingAlgorithm == MB2_PackingAlgorithmEnum.MeshBakerTexturePacker_Horizontal ||
  316. _packingAlgorithm == MB2_PackingAlgorithmEnum.MeshBakerTexturePacker_Vertical))
  317. {
  318. if (LOG_LEVEL >= MB2_LogLevel.info)
  319. {
  320. Debug.LogWarning("'Consider Mesh UVs' is enabled but packing algorithm is MeshBakerTexturePacker_Horizontal or MeshBakerTexturePacker_Vertical. It is recommended to use these packers without using 'Consider Mesh UVs'");
  321. }
  322. }
  323. data.nonTexturePropertyBlender.LoadTextureBlendersIfNeeded(data.resultMaterial);
  324. if (onlyPackRects)
  325. {
  326. yield return __RunTexturePacker(result, data, textureEditorMethods, atlasPackingResult);
  327. }
  328. else
  329. {
  330. yield return __CombineTexturesIntoAtlases(progressInfo, result, resultAtlasesAndRects, data, textureEditorMethods);
  331. }
  332. /*
  333. } catch (MissingReferenceException mrex){
  334. Debug.LogError("Creating atlases failed a MissingReferenceException was thrown. This is normally only happens when trying to create very large atlases and Unity is running out of Memory. Try changing the 'Texture Packer' to a different option, it may work with an alternate packer. This error is sometimes intermittant. Try baking again.");
  335. Debug.LogError(mrex);
  336. } catch (Exception ex){
  337. Debug.LogError(ex);*/
  338. }
  339. finally
  340. {
  341. _destroyAllTemporaryTextures();
  342. _restoreProceduralMaterials();
  343. if (textureEditorMethods != null)
  344. {
  345. textureEditorMethods.RestoreReadFlagsAndFormats(progressInfo);
  346. textureEditorMethods.OnPostTextureBake();
  347. }
  348. if (LOG_LEVEL >= MB2_LogLevel.debug) Debug.Log("===== Done creating atlases for " + resultMaterial + " Total time to create atlases " + sw.Elapsed.ToString());
  349. }
  350. //result.success = success;
  351. }
  352. MB3_TextureCombinerPipeline.TexturePipelineData LoadPipelineData(Material resultMaterial,
  353. List<ShaderTextureProperty> texPropertyNames, List<GameObject> objsToMesh, List<Material> allowedMaterialsFilter,
  354. List<MB_TexSet> distinctMaterialTextures)
  355. {
  356. MB3_TextureCombinerPipeline.TexturePipelineData data = new MB3_TextureCombinerPipeline.TexturePipelineData();
  357. data._textureBakeResults = _textureBakeResults;
  358. data._atlasPadding = _atlasPadding;
  359. if (_packingAlgorithm == MB2_PackingAlgorithmEnum.MeshBakerTexturePacker_Vertical && _useMaxAtlasHeightOverride)
  360. {
  361. data._maxAtlasHeight = _maxAtlasHeightOverride;
  362. data._useMaxAtlasHeightOverride = true;
  363. }
  364. else
  365. {
  366. data._maxAtlasHeight = _maxAtlasSize;
  367. }
  368. if (_packingAlgorithm == MB2_PackingAlgorithmEnum.MeshBakerTexturePacker_Horizontal && _useMaxAtlasWidthOverride)
  369. {
  370. data._maxAtlasWidth = _maxAtlasWidthOverride;
  371. data._useMaxAtlasWidthOverride = true;
  372. } else
  373. {
  374. data._maxAtlasWidth = _maxAtlasSize;
  375. }
  376. data._resizePowerOfTwoTextures = _resizePowerOfTwoTextures;
  377. data._fixOutOfBoundsUVs = _fixOutOfBoundsUVs;
  378. data._maxTilingBakeSize = _maxTilingBakeSize;
  379. data._saveAtlasesAsAssets = _saveAtlasesAsAssets;
  380. data._packingAlgorithm = _packingAlgorithm;
  381. data._meshBakerTexturePackerForcePowerOfTwo = _meshBakerTexturePackerForcePowerOfTwo;
  382. data._customShaderPropNames = _customShaderPropNames;
  383. data._normalizeTexelDensity = _normalizeTexelDensity;
  384. data._considerNonTextureProperties = _considerNonTextureProperties;
  385. data.nonTexturePropertyBlender = new MB3_TextureCombinerNonTextureProperties(LOG_LEVEL, _considerNonTextureProperties);
  386. data.resultMaterial = resultMaterial;
  387. data.distinctMaterialTextures = distinctMaterialTextures;
  388. data.allObjsToMesh = objsToMesh;
  389. data.allowedMaterialsFilter = allowedMaterialsFilter;
  390. data.texPropertyNames = texPropertyNames;
  391. return data;
  392. }
  393. //texPropertyNames is the list of texture properties in the resultMaterial
  394. //allowedMaterialsFilter is a list of materials. Objects without any of these materials will be ignored.
  395. // this is used by the multiple materials filter
  396. //textureEditorMethods encapsulates editor only functionality such as saving assets and tracking texture assets whos format was changed. Is null if using at runtime.
  397. IEnumerator __CombineTexturesIntoAtlases(ProgressUpdateDelegate progressInfo, CombineTexturesIntoAtlasesCoroutineResult result, MB_AtlasesAndRects resultAtlasesAndRects, MB3_TextureCombinerPipeline.TexturePipelineData data, MB2_EditorMethodsInterface textureEditorMethods)
  398. {
  399. if (LOG_LEVEL >= MB2_LogLevel.debug) Debug.Log("__CombineTexturesIntoAtlases texture properties in shader:" + data.texPropertyNames.Count + " objsToMesh:" + data.allObjsToMesh.Count + " _fixOutOfBoundsUVs:" + data._fixOutOfBoundsUVs);
  400. if (progressInfo != null) progressInfo("Collecting textures ", .01f);
  401. /*
  402. each atlas (maintex, bump, spec etc...) will have distinctMaterialTextures.Count images in it.
  403. each distinctMaterialTextures record is a set of textures, one for each atlas. And a list of materials
  404. that use that distinct set of textures.
  405. */
  406. List<GameObject> usedObjsToMesh = new List<GameObject>();
  407. yield return MB3_TextureCombinerPipeline.__Step1_CollectDistinctMatTexturesAndUsedObjects(progressInfo, result, data, this, textureEditorMethods, usedObjsToMesh, LOG_LEVEL);
  408. if (!result.success)
  409. {
  410. yield break;
  411. }
  412. if (MB3_MeshCombiner.EVAL_VERSION)
  413. {
  414. bool usesAllowedShaders = true;
  415. for (int i = 0; i < data.distinctMaterialTextures.Count; i++)
  416. {
  417. for (int j = 0; j < data.distinctMaterialTextures[i].matsAndGOs.mats.Count; j++)
  418. {
  419. if (!data.distinctMaterialTextures[i].matsAndGOs.mats[j].mat.shader.name.EndsWith("Diffuse") &&
  420. !data.distinctMaterialTextures[i].matsAndGOs.mats[j].mat.shader.name.EndsWith("Bumped Diffuse"))
  421. {
  422. Debug.LogError("The free version of Mesh Baker only works with Diffuse and Bumped Diffuse Shaders. The full version can be used with any shader. Material " + data.distinctMaterialTextures[i].matsAndGOs.mats[j].mat.name + " uses shader " + data.distinctMaterialTextures[i].matsAndGOs.mats[j].mat.shader.name);
  423. usesAllowedShaders = false;
  424. }
  425. }
  426. }
  427. if (!usesAllowedShaders)
  428. {
  429. result.success = false;
  430. yield break;
  431. }
  432. }
  433. //Textures in each material (_mainTex, Bump, Spec ect...) must be same size
  434. //Calculate the best sized to use. Takes into account tiling
  435. //if only one texture in atlas re-uses original sizes
  436. yield return MB3_TextureCombinerPipeline.CalculateIdealSizesForTexturesInAtlasAndPadding(progressInfo, result, data, this, textureEditorMethods, LOG_LEVEL);
  437. if (!result.success)
  438. {
  439. yield break;
  440. }
  441. //buildAndSaveAtlases
  442. StringBuilder report = MB3_TextureCombinerPipeline.GenerateReport(data);
  443. MB_ITextureCombinerPacker texturePaker = MB3_TextureCombinerPipeline.CreatePacker(data.OnlyOneTextureInAtlasReuseTextures(), data._packingAlgorithm);
  444. yield return texturePaker.ConvertTexturesToReadableFormats(progressInfo, result, data, this, textureEditorMethods, LOG_LEVEL);
  445. if (!result.success)
  446. {
  447. yield break;
  448. }
  449. AtlasPackingResult[] uvRects = texturePaker.CalculateAtlasRectangles(data, false, LOG_LEVEL);
  450. yield return MB3_TextureCombinerPipeline.__Step3_BuildAndSaveAtlasesAndStoreResults(result, progressInfo, data, this, texturePaker, uvRects[0], textureEditorMethods, resultAtlasesAndRects, report, LOG_LEVEL);
  451. }
  452. IEnumerator __RunTexturePacker(CombineTexturesIntoAtlasesCoroutineResult result, MB3_TextureCombinerPipeline.TexturePipelineData data, MB2_EditorMethodsInterface textureEditorMethods, List<AtlasPackingResult> packingResult)
  453. {
  454. if (LOG_LEVEL >= MB2_LogLevel.debug) Debug.Log("__RunTexturePacker texture properties in shader:" + data.texPropertyNames.Count + " objsToMesh:" + data.allObjsToMesh.Count + " _fixOutOfBoundsUVs:" + data._fixOutOfBoundsUVs);
  455. List<GameObject> usedObjsToMesh = new List<GameObject>();
  456. yield return MB3_TextureCombinerPipeline.__Step1_CollectDistinctMatTexturesAndUsedObjects(null, result, data, this, textureEditorMethods, usedObjsToMesh, LOG_LEVEL);
  457. if (!result.success)
  458. {
  459. yield break;
  460. }
  461. data.allTexturesAreNullAndSameColor = new MB3_TextureCombinerPipeline.CreateAtlasForProperty[data.texPropertyNames.Count];
  462. yield return MB3_TextureCombinerPipeline.CalculateIdealSizesForTexturesInAtlasAndPadding(null, result, data, this, textureEditorMethods, LOG_LEVEL);
  463. if (!result.success)
  464. {
  465. yield break;
  466. }
  467. MB_ITextureCombinerPacker texturePaker = MB3_TextureCombinerPipeline.CreatePacker(data.OnlyOneTextureInAtlasReuseTextures(), data._packingAlgorithm);
  468. // run the texture packer only
  469. AtlasPackingResult[] aprs = MB3_TextureCombinerPipeline.__Step3_RunTexturePacker(data, texturePaker, LOG_LEVEL);
  470. for (int i = 0; i < aprs.Length; i++)
  471. {
  472. packingResult.Add(aprs[i]);
  473. }
  474. }
  475. internal int _getNumTemporaryTextures()
  476. {
  477. return _temporaryTextures.Count;
  478. }
  479. //used to track temporary textures that were created so they can be destroyed
  480. public Texture2D _createTemporaryTexture(string propertyName, int w, int h, TextureFormat texFormat, bool mipMaps)
  481. {
  482. Texture2D t = new Texture2D(w, h, texFormat, mipMaps);
  483. t.name = string.Format("tmp{0}_{1}x{2}", _temporaryTextures.Count, w, h);
  484. MB_Utility.setSolidColor(t, Color.clear);
  485. TemporaryTexture txx = new TemporaryTexture(propertyName, t);
  486. _temporaryTextures.Add(txx);
  487. return t;
  488. }
  489. internal Texture2D _createTextureCopy(string propertyName, Texture2D t)
  490. {
  491. Texture2D tx = MB_Utility.createTextureCopy(t);
  492. tx.name = string.Format("tmpCopy{0}_{1}x{2}", _temporaryTextures.Count, tx.width, tx.height);
  493. TemporaryTexture txx = new TemporaryTexture(propertyName, tx);
  494. _temporaryTextures.Add(txx);
  495. return tx;
  496. }
  497. internal Texture2D _resizeTexture(string propertyName, Texture2D t, int w, int h)
  498. {
  499. Texture2D tx = MB_Utility.resampleTexture(t, w, h);
  500. tx.name = string.Format("tmpResampled{0}_{1}x{2}", _temporaryTextures.Count, w, h);
  501. TemporaryTexture txx = new TemporaryTexture(propertyName, tx);
  502. _temporaryTextures.Add(txx);
  503. return tx;
  504. }
  505. internal void _destroyAllTemporaryTextures()
  506. {
  507. if (LOG_LEVEL >= MB2_LogLevel.debug) Debug.Log("Destroying " + _temporaryTextures.Count + " temporary textures");
  508. for (int i = 0; i < _temporaryTextures.Count; i++)
  509. {
  510. MB_Utility.Destroy(_temporaryTextures[i].texture);
  511. }
  512. _temporaryTextures.Clear();
  513. }
  514. internal void _destroyTemporaryTextures(string propertyName)
  515. {
  516. int numDestroyed = 0;
  517. for (int i = _temporaryTextures.Count - 1; i >= 0; i--)
  518. {
  519. if (_temporaryTextures[i].property.Equals(propertyName))
  520. {
  521. numDestroyed++;
  522. MB_Utility.Destroy(_temporaryTextures[i].texture);
  523. _temporaryTextures.RemoveAt(i);
  524. }
  525. }
  526. if (LOG_LEVEL >= MB2_LogLevel.debug) Debug.Log("Destroying " + numDestroyed + " temporary textures " + propertyName + " num remaining " + _temporaryTextures.Count);
  527. }
  528. /*
  529. public void _addProceduralMaterial(ProceduralMaterial pm)
  530. {
  531. ProceduralMaterialInfo pmi = new ProceduralMaterialInfo();
  532. pm.isReadable = pm.isReadable;
  533. pmi.proceduralMat = pm;
  534. _proceduralMaterials.Add(pmi);
  535. }
  536. */
  537. public void _restoreProceduralMaterials()
  538. {
  539. /*
  540. for (int i = 0; i < _proceduralMaterials.Count; i++)
  541. {
  542. ProceduralMaterialInfo pmi = _proceduralMaterials[i];
  543. pmi.proceduralMat.isReadable = pmi.originalIsReadableVal;
  544. pmi.proceduralMat.RebuildTexturesImmediately();
  545. }
  546. _proceduralMaterials.Clear();
  547. */
  548. }
  549. public void SuggestTreatment(List<GameObject> objsToMesh, Material[] resultMaterials, List<ShaderTextureProperty> _customShaderPropNames)
  550. {
  551. this._customShaderPropNames = _customShaderPropNames;
  552. StringBuilder sb = new StringBuilder();
  553. Dictionary<int, MB_Utility.MeshAnalysisResult[]> meshAnalysisResultsCache = new Dictionary<int, MB_Utility.MeshAnalysisResult[]>(); //cache results
  554. for (int i = 0; i < objsToMesh.Count; i++)
  555. {
  556. GameObject obj = objsToMesh[i];
  557. if (obj == null) continue;
  558. Material[] ms = MB_Utility.GetGOMaterials(objsToMesh[i]);
  559. if (ms.Length > 1)
  560. { // and each material is not mapped to its own layer
  561. sb.AppendFormat("\nObject {0} uses {1} materials. Possible treatments:\n", objsToMesh[i].name, ms.Length);
  562. sb.AppendFormat(" 1) Collapse the submeshes together into one submesh in the combined mesh. Each of the original submesh materials will map to a different UV rectangle in the atlas(es) used by the combined material.\n");
  563. sb.AppendFormat(" 2) Use the multiple materials feature to map submeshes in the source mesh to submeshes in the combined mesh.\n");
  564. }
  565. Mesh m = MB_Utility.GetMesh(obj);
  566. MB_Utility.MeshAnalysisResult[] mar;
  567. if (!meshAnalysisResultsCache.TryGetValue(m.GetInstanceID(), out mar))
  568. {
  569. mar = new MB_Utility.MeshAnalysisResult[m.subMeshCount];
  570. MB_Utility.doSubmeshesShareVertsOrTris(m, ref mar[0]);
  571. for (int j = 0; j < m.subMeshCount; j++)
  572. {
  573. MB_Utility.hasOutOfBoundsUVs(m, ref mar[j], j);
  574. //DRect outOfBoundsUVRect = new DRect(mar[j].uvRect);
  575. mar[j].hasOverlappingSubmeshTris = mar[0].hasOverlappingSubmeshTris;
  576. mar[j].hasOverlappingSubmeshVerts = mar[0].hasOverlappingSubmeshVerts;
  577. }
  578. meshAnalysisResultsCache.Add(m.GetInstanceID(), mar);
  579. }
  580. for (int j = 0; j < ms.Length; j++)
  581. {
  582. if (mar[j].hasOutOfBoundsUVs)
  583. {
  584. DRect r = new DRect(mar[j].uvRect);
  585. sb.AppendFormat("\nObject {0} submesh={1} material={2} uses UVs outside the range 0,0 .. 1,1 to create tiling that tiles the box {3},{4} .. {5},{6}. This is a problem because the UVs outside the 0,0 .. 1,1 " +
  586. "rectangle will pick up neighboring textures in the atlas. Possible Treatments:\n", obj, j, ms[j], r.x.ToString("G4"), r.y.ToString("G4"), (r.x + r.width).ToString("G4"), (r.y + r.height).ToString("G4"));
  587. sb.AppendFormat(" 1) Ignore the problem. The tiling may not affect result significantly.\n");
  588. sb.AppendFormat(" 2) Use the 'fix out of bounds UVs' feature to bake the tiling and scale the UVs to fit in the 0,0 .. 1,1 rectangle.\n");
  589. sb.AppendFormat(" 3) Use the Multiple Materials feature to map the material on this submesh to its own submesh in the combined mesh. No other materials should map to this submesh. This will result in only one texture in the atlas(es) and the UVs should tile correctly.\n");
  590. sb.AppendFormat(" 4) Combine only meshes that use the same (or subset of) the set of materials on this mesh. The original material(s) can be applied to the result\n");
  591. }
  592. }
  593. if (mar[0].hasOverlappingSubmeshVerts)
  594. {
  595. sb.AppendFormat("\nObject {0} has submeshes that share vertices. This is a problem because each vertex can have only one UV coordinate and may be required to map to different positions in the various atlases that are generated. Possible treatments:\n", objsToMesh[i]);
  596. sb.AppendFormat(" 1) Ignore the problem. The vertices may not affect the result.\n");
  597. sb.AppendFormat(" 2) Use the Multiple Materials feature to map the submeshs that overlap to their own submeshs in the combined mesh. No other materials should map to this submesh. This will result in only one texture in the atlas(es) and the UVs should tile correctly.\n");
  598. sb.AppendFormat(" 3) Combine only meshes that use the same (or subset of) the set of materials on this mesh. The original material(s) can be applied to the result\n");
  599. }
  600. }
  601. Dictionary<Material, List<GameObject>> m2gos = new Dictionary<Material, List<GameObject>>();
  602. for (int i = 0; i < objsToMesh.Count; i++)
  603. {
  604. if (objsToMesh[i] != null)
  605. {
  606. Material[] ms = MB_Utility.GetGOMaterials(objsToMesh[i]);
  607. for (int j = 0; j < ms.Length; j++)
  608. {
  609. if (ms[j] != null)
  610. {
  611. List<GameObject> lgo;
  612. if (!m2gos.TryGetValue(ms[j], out lgo))
  613. {
  614. lgo = new List<GameObject>();
  615. m2gos.Add(ms[j], lgo);
  616. }
  617. if (!lgo.Contains(objsToMesh[i])) lgo.Add(objsToMesh[i]);
  618. }
  619. }
  620. }
  621. }
  622. for (int i = 0; i < resultMaterials.Length; i++)
  623. {
  624. string resultMatShaderName = resultMaterials[i] != null ? "None" : resultMaterials[i].shader.name;
  625. MB3_TextureCombinerPipeline.TexturePipelineData data = LoadPipelineData(resultMaterials[i], new List<ShaderTextureProperty>(), objsToMesh, new List<Material>(), new List<MB_TexSet>());
  626. MB3_TextureCombinerPipeline._CollectPropertyNames(data, LOG_LEVEL);
  627. foreach (Material m in m2gos.Keys)
  628. {
  629. for (int j = 0; j < data.texPropertyNames.Count; j++)
  630. {
  631. if (m.HasProperty(data.texPropertyNames[j].name))
  632. {
  633. Texture txx = MB3_TextureCombinerPipeline.GetTextureConsideringStandardShaderKeywords(resultMatShaderName, m, data.texPropertyNames[j].name);
  634. if (txx != null)
  635. {
  636. Vector2 o = m.GetTextureOffset(data.texPropertyNames[j].name);
  637. Vector3 s = m.GetTextureScale(data.texPropertyNames[j].name);
  638. if (o.x < 0f || o.x + s.x > 1f ||
  639. o.y < 0f || o.y + s.y > 1f)
  640. {
  641. sb.AppendFormat("\nMaterial {0} used by objects {1} uses texture {2} that is tiled (scale={3} offset={4}). If there is more than one texture in the atlas " +
  642. " then Mesh Baker will bake the tiling into the atlas. If the baked tiling is large then quality can be lost. Possible treatments:\n", m, PrintList(m2gos[m]), txx, s, o);
  643. sb.AppendFormat(" 1) Use the baked tiling.\n");
  644. sb.AppendFormat(" 2) Use the Multiple Materials feature to map the material on this object/submesh to its own submesh in the combined mesh. No other materials should map to this submesh. The original material can be applied to this submesh.\n");
  645. sb.AppendFormat(" 3) Combine only meshes that use the same (or subset of) the set of textures on this mesh. The original material can be applied to the result.\n");
  646. }
  647. }
  648. }
  649. }
  650. }
  651. }
  652. string outstr = "";
  653. if (sb.Length == 0)
  654. {
  655. outstr = "====== No problems detected. These meshes should combine well ====\n If there are problems with the combined meshes please report the problem to digitalOpus.ca so we can improve Mesh Baker.";
  656. }
  657. else
  658. {
  659. outstr = "====== There are possible problems with these meshes that may prevent them from combining well. TREATMENT SUGGESTIONS (copy and paste to text editor if too big) =====\n" + sb.ToString();
  660. }
  661. Debug.Log(outstr);
  662. }
  663. string PrintList(List<GameObject> gos)
  664. {
  665. StringBuilder sb = new StringBuilder();
  666. for (int i = 0; i < gos.Count; i++)
  667. {
  668. sb.Append(gos[i] + ",");
  669. }
  670. return sb.ToString();
  671. }
  672. }
  673. }