MB3_TextureCombinerPipeline.cs 52 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997
  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. public class MB3_TextureCombinerPipeline
  32. {
  33. public static bool USE_EXPERIMENTAL_HOIZONTALVERTICAL = false;
  34. public struct CreateAtlasForProperty
  35. {
  36. public bool allTexturesAreNull;
  37. public bool allTexturesAreSame;
  38. public bool allNonTexturePropsAreSame;
  39. public override string ToString()
  40. {
  41. return String.Format("AllTexturesNull={0} areSame={1} nonTexPropsAreSame={2}", allTexturesAreNull, allTexturesAreSame, allNonTexturePropsAreSame);
  42. }
  43. }
  44. public static ShaderTextureProperty[] shaderTexPropertyNames = new ShaderTextureProperty[] {
  45. new ShaderTextureProperty("_MainTex",false),
  46. new ShaderTextureProperty("_BumpMap",true),
  47. new ShaderTextureProperty("_Normal",true),
  48. new ShaderTextureProperty("_BumpSpecMap",false),
  49. new ShaderTextureProperty("_DecalTex",false),
  50. new ShaderTextureProperty("_Detail",false),
  51. new ShaderTextureProperty("_GlossMap",false),
  52. new ShaderTextureProperty("_Illum",false),
  53. new ShaderTextureProperty("_LightTextureB0",false),
  54. new ShaderTextureProperty("_ParallaxMap",false),
  55. new ShaderTextureProperty("_ShadowOffset",false),
  56. new ShaderTextureProperty("_TranslucencyMap",false),
  57. new ShaderTextureProperty("_SpecMap",false),
  58. new ShaderTextureProperty("_SpecGlossMap",false),
  59. new ShaderTextureProperty("_TranspMap",false),
  60. new ShaderTextureProperty("_MetallicGlossMap",false),
  61. new ShaderTextureProperty("_OcclusionMap",false),
  62. new ShaderTextureProperty("_EmissionMap",false),
  63. new ShaderTextureProperty("_DetailMask",false),
  64. // new ShaderTextureProperty("_DetailAlbedoMap",false),
  65. // new ShaderTextureProperty("_DetailNormalMap",true),
  66. };
  67. internal class TexturePipelineData
  68. {
  69. internal MB2_TextureBakeResults _textureBakeResults;
  70. internal int _atlasPadding = 1;
  71. internal int _maxAtlasWidth = 1;
  72. internal int _maxAtlasHeight = 1;
  73. internal bool _useMaxAtlasHeightOverride = false;
  74. internal bool _useMaxAtlasWidthOverride = false;
  75. internal bool _resizePowerOfTwoTextures = false;
  76. internal bool _fixOutOfBoundsUVs = false;
  77. internal int _maxTilingBakeSize = 1024;
  78. internal bool _saveAtlasesAsAssets = false;
  79. internal MB2_PackingAlgorithmEnum _packingAlgorithm = MB2_PackingAlgorithmEnum.UnitysPackTextures;
  80. internal bool _meshBakerTexturePackerForcePowerOfTwo = true;
  81. internal List<ShaderTextureProperty> _customShaderPropNames = new List<ShaderTextureProperty>();
  82. internal bool _normalizeTexelDensity = false;
  83. internal bool _considerNonTextureProperties = false;
  84. internal MB3_TextureCombinerNonTextureProperties nonTexturePropertyBlender;
  85. internal List<MB_TexSet> distinctMaterialTextures;
  86. internal List<GameObject> allObjsToMesh;
  87. internal List<Material> allowedMaterialsFilter;
  88. internal List<ShaderTextureProperty> texPropertyNames;
  89. internal CreateAtlasForProperty[] allTexturesAreNullAndSameColor;
  90. internal int numAtlases { get
  91. {
  92. if (texPropertyNames != null) return texPropertyNames.Count;
  93. else return 0;
  94. }
  95. }
  96. internal Material resultMaterial;
  97. internal bool OnlyOneTextureInAtlasReuseTextures()
  98. {
  99. if (distinctMaterialTextures != null &&
  100. distinctMaterialTextures.Count == 1 &&
  101. distinctMaterialTextures[0].thisIsOnlyTexSetInAtlas == true &&
  102. !_fixOutOfBoundsUVs &&
  103. !_considerNonTextureProperties)
  104. {
  105. return true;
  106. }
  107. return false;
  108. }
  109. }
  110. internal static bool _ShouldWeCreateAtlasForThisProperty(int propertyIndex, bool considerNonTextureProperties, CreateAtlasForProperty[] allTexturesAreNullAndSameColor)
  111. {
  112. CreateAtlasForProperty v = allTexturesAreNullAndSameColor[propertyIndex];
  113. if (considerNonTextureProperties)
  114. {
  115. if (!v.allNonTexturePropsAreSame || !v.allTexturesAreNull)
  116. {
  117. return true;
  118. }
  119. else
  120. {
  121. return false;
  122. }
  123. }
  124. else
  125. {
  126. if (!v.allTexturesAreNull)
  127. {
  128. return true;
  129. }
  130. else
  131. {
  132. return false;
  133. }
  134. }
  135. }
  136. internal static bool _CollectPropertyNames(TexturePipelineData data, MB2_LogLevel LOG_LEVEL)
  137. {
  138. //try custom properties remove duplicates
  139. for (int i = 0; i < data.texPropertyNames.Count; i++)
  140. {
  141. ShaderTextureProperty s = data._customShaderPropNames.Find(x => x.name.Equals(data.texPropertyNames[i].name));
  142. if (s != null)
  143. {
  144. data._customShaderPropNames.Remove(s);
  145. }
  146. }
  147. Material m = data.resultMaterial;
  148. if (m == null)
  149. {
  150. Debug.LogError("Please assign a result material. The combined mesh will use this material.");
  151. return false;
  152. }
  153. //Collect the property names for the textures
  154. string shaderPropStr = "";
  155. for (int i = 0; i < shaderTexPropertyNames.Length; i++)
  156. {
  157. if (m.HasProperty(shaderTexPropertyNames[i].name))
  158. {
  159. shaderPropStr += ", " + shaderTexPropertyNames[i].name;
  160. if (!data.texPropertyNames.Contains(shaderTexPropertyNames[i])) data.texPropertyNames.Add(shaderTexPropertyNames[i]);
  161. if (m.GetTextureOffset(shaderTexPropertyNames[i].name) != new Vector2(0f, 0f))
  162. {
  163. if (LOG_LEVEL >= MB2_LogLevel.warn) Debug.LogWarning("Result material has non-zero offset. This is may be incorrect.");
  164. }
  165. if (m.GetTextureScale(shaderTexPropertyNames[i].name) != new Vector2(1f, 1f))
  166. {
  167. if (LOG_LEVEL >= MB2_LogLevel.warn) Debug.LogWarning("Result material should have tiling of 1,1");
  168. }
  169. }
  170. }
  171. for (int i = 0; i < data._customShaderPropNames.Count; i++)
  172. {
  173. if (m.HasProperty(data._customShaderPropNames[i].name))
  174. {
  175. shaderPropStr += ", " + data._customShaderPropNames[i].name;
  176. data.texPropertyNames.Add(data._customShaderPropNames[i]);
  177. if (m.GetTextureOffset(data._customShaderPropNames[i].name) != new Vector2(0f, 0f))
  178. {
  179. if (LOG_LEVEL >= MB2_LogLevel.warn) Debug.LogWarning("Result material has non-zero offset. This is probably incorrect.");
  180. }
  181. if (m.GetTextureScale(data._customShaderPropNames[i].name) != new Vector2(1f, 1f))
  182. {
  183. if (LOG_LEVEL >= MB2_LogLevel.warn) Debug.LogWarning("Result material should probably have tiling of 1,1.");
  184. }
  185. }
  186. else
  187. {
  188. if (LOG_LEVEL >= MB2_LogLevel.warn) Debug.LogWarning("Result material shader does not use property " + data._customShaderPropNames[i].name + " in the list of custom shader property names");
  189. }
  190. }
  191. return true;
  192. }
  193. internal static bool _ShouldWeCreateAtlasForThisProperty(int propertyIndex, CreateAtlasForProperty[] allTexturesAreNullAndSameColor, TexturePipelineData data)
  194. {
  195. CreateAtlasForProperty v = allTexturesAreNullAndSameColor[propertyIndex];
  196. if (data._considerNonTextureProperties)
  197. {
  198. if (!v.allNonTexturePropsAreSame || !v.allTexturesAreNull)
  199. {
  200. return true;
  201. }
  202. else
  203. {
  204. return false;
  205. }
  206. }
  207. else
  208. {
  209. if (!v.allTexturesAreNull)
  210. {
  211. return true;
  212. }
  213. else
  214. {
  215. return false;
  216. }
  217. }
  218. }
  219. /// <summary>
  220. /// Some shaders like the Standard shader have texture properties like Emission which can be set on the material
  221. /// but are disabled using keywords. In these cases the textures should not be returned.
  222. /// </summary>
  223. public static Texture GetTextureConsideringStandardShaderKeywords(string shaderName, Material mat, string propertyName)
  224. {
  225. if (shaderName.Equals("Standard") || shaderName.Equals("Standard (Specular setup)") || shaderName.Equals("Standard (Roughness setup"))
  226. {
  227. if (propertyName.Equals("_EmissionMap"))
  228. {
  229. if (mat.IsKeywordEnabled("_EMISSION"))
  230. {
  231. return mat.GetTexture(propertyName);
  232. } else
  233. {
  234. return null;
  235. }
  236. }
  237. }
  238. return mat.GetTexture(propertyName);
  239. }
  240. /// <summary>
  241. /// Fills distinctMaterialTextures (a list of TexSets) and usedObjsToMesh. Each TexSet is a rectangle in the set of atlases.
  242. /// If allowedMaterialsFilter is empty then all materials on allObjsToMesh will be collected and usedObjsToMesh will be same as allObjsToMesh
  243. /// else only materials in allowedMaterialsFilter will be included and usedObjsToMesh will be objs that use those materials.
  244. /// bool __step1_CollectDistinctMatTexturesAndUsedObjects;
  245. /// </summary>
  246. internal static IEnumerator __Step1_CollectDistinctMatTexturesAndUsedObjects(ProgressUpdateDelegate progressInfo,
  247. MB3_TextureCombiner.CombineTexturesIntoAtlasesCoroutineResult result,
  248. TexturePipelineData data,
  249. MB3_TextureCombiner combiner,
  250. MB2_EditorMethodsInterface textureEditorMethods,
  251. List<GameObject> usedObjsToMesh,
  252. MB2_LogLevel LOG_LEVEL
  253. )
  254. {
  255. System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
  256. sw.Start();
  257. // Collect distinct list of textures to combine from the materials on objsToCombine
  258. bool outOfBoundsUVs = false;
  259. Dictionary<int, MB_Utility.MeshAnalysisResult[]> meshAnalysisResultsCache = new Dictionary<int, MB_Utility.MeshAnalysisResult[]>(); //cache results
  260. for (int i = 0; i < data.allObjsToMesh.Count; i++)
  261. {
  262. GameObject obj = data.allObjsToMesh[i];
  263. if (progressInfo != null) progressInfo("Collecting textures for " + obj, ((float)i) / data.allObjsToMesh.Count / 2f);
  264. if (LOG_LEVEL >= MB2_LogLevel.debug) Debug.Log("Collecting textures for object " + obj);
  265. if (obj == null)
  266. {
  267. Debug.LogError("The list of objects to mesh contained nulls.");
  268. result.success = false;
  269. yield break;
  270. }
  271. Mesh sharedMesh = MB_Utility.GetMesh(obj);
  272. if (sharedMesh == null)
  273. {
  274. Debug.LogError("Object " + obj.name + " in the list of objects to mesh has no mesh.");
  275. result.success = false;
  276. yield break;
  277. }
  278. Material[] sharedMaterials = MB_Utility.GetGOMaterials(obj);
  279. if (sharedMaterials.Length == 0)
  280. {
  281. Debug.LogError("Object " + obj.name + " in the list of objects has no materials.");
  282. result.success = false;
  283. yield break;
  284. }
  285. //analyze mesh or grab cached result of previous analysis, stores one result for each submesh
  286. MB_Utility.MeshAnalysisResult[] mar;
  287. if (!meshAnalysisResultsCache.TryGetValue(sharedMesh.GetInstanceID(), out mar))
  288. {
  289. mar = new MB_Utility.MeshAnalysisResult[sharedMesh.subMeshCount];
  290. for (int j = 0; j < sharedMesh.subMeshCount; j++)
  291. {
  292. MB_Utility.hasOutOfBoundsUVs(sharedMesh, ref mar[j], j);
  293. if (data._normalizeTexelDensity)
  294. {
  295. mar[j].submeshArea = GetSubmeshArea(sharedMesh, j);
  296. }
  297. if (data._fixOutOfBoundsUVs && !mar[j].hasUVs)
  298. {
  299. //assume UVs will be generated if this feature is being used and generated UVs will be 0,0,1,1
  300. mar[j].uvRect = new Rect(0, 0, 1, 1);
  301. Debug.LogWarning("Mesh for object " + obj + " has no UV channel but 'consider UVs' is enabled. Assuming UVs will be generated filling 0,0,1,1 rectangle.");
  302. }
  303. }
  304. meshAnalysisResultsCache.Add(sharedMesh.GetInstanceID(), mar);
  305. }
  306. if (data._fixOutOfBoundsUVs && LOG_LEVEL >= MB2_LogLevel.trace)
  307. {
  308. Debug.Log("Mesh Analysis for object " + obj + " numSubmesh=" + mar.Length + " HasOBUV=" + mar[0].hasOutOfBoundsUVs + " UVrectSubmesh0=" + mar[0].uvRect);
  309. }
  310. for (int matIdx = 0; matIdx < sharedMaterials.Length; matIdx++)
  311. { //for each submesh
  312. if (progressInfo != null) progressInfo(String.Format("Collecting textures for {0} submesh {1}", obj, matIdx), ((float)i) / data.allObjsToMesh.Count / 2f);
  313. Material mat = sharedMaterials[matIdx];
  314. //check if this material is in the list of source materaials
  315. if (data.allowedMaterialsFilter != null && !data.allowedMaterialsFilter.Contains(mat))
  316. {
  317. continue;
  318. }
  319. //Rect uvBounds = mar[matIdx].sourceUVRect;
  320. outOfBoundsUVs = outOfBoundsUVs || mar[matIdx].hasOutOfBoundsUVs;
  321. if (mat.name.Contains("(Instance)"))
  322. {
  323. Debug.LogError("The sharedMaterial on object " + obj.name + " has been 'Instanced'. This was probably caused by a script accessing the meshRender.material property in the editor. " +
  324. " The material to UV Rectangle mapping will be incorrect. To fix this recreate the object from its prefab or re-assign its material from the correct asset.");
  325. result.success = false;
  326. yield break;
  327. }
  328. if (data._fixOutOfBoundsUVs)
  329. {
  330. if (!MB_Utility.AreAllSharedMaterialsDistinct(sharedMaterials))
  331. {
  332. if (LOG_LEVEL >= MB2_LogLevel.warn) Debug.LogWarning("Object " + obj.name + " uses the same material on multiple submeshes. This may generate strange resultAtlasesAndRects especially when used with fix out of bounds uvs. Try duplicating the material.");
  333. }
  334. }
  335. //need to set up procedural material before converting its texs to texture2D
  336. /*
  337. if (mat is ProceduralMaterial)
  338. {
  339. combiner._addProceduralMaterial((ProceduralMaterial)mat);
  340. }
  341. */
  342. //collect textures scale and offset for each texture in objects material
  343. MeshBakerMaterialTexture[] mts = new MeshBakerMaterialTexture[data.texPropertyNames.Count];
  344. for (int propIdx = 0; propIdx < data.texPropertyNames.Count; propIdx++)
  345. {
  346. Texture tx = null;
  347. Vector2 scale = Vector2.one;
  348. Vector2 offset = Vector2.zero;
  349. float texelDensity = 0f;
  350. if (mat.HasProperty(data.texPropertyNames[propIdx].name))
  351. {
  352. Texture txx = GetTextureConsideringStandardShaderKeywords(data.resultMaterial.shader.name, mat, data.texPropertyNames[propIdx].name);
  353. if (txx != null)
  354. {
  355. if (txx is Texture2D)
  356. {
  357. tx = txx;
  358. TextureFormat f = ((Texture2D)tx).format;
  359. bool isNormalMap = false;
  360. if (!Application.isPlaying && textureEditorMethods != null) isNormalMap = textureEditorMethods.IsNormalMap((Texture2D)tx);
  361. if ((f == TextureFormat.ARGB32 ||
  362. f == TextureFormat.RGBA32 ||
  363. f == TextureFormat.BGRA32 ||
  364. f == TextureFormat.RGB24 ||
  365. f == TextureFormat.Alpha8) && !isNormalMap) //DXT5 does not work
  366. {
  367. //good
  368. }
  369. else
  370. {
  371. //TRIED to copy texture using tex2.SetPixels(tex1.GetPixels()) but bug in 3.5 means DTX1 and 5 compressed textures come out skewe
  372. if (Application.isPlaying && data._packingAlgorithm != MB2_PackingAlgorithmEnum.MeshBakerTexturePacker_Fast)
  373. {
  374. Debug.LogError("Object " + obj.name + " in the list of objects to mesh uses Texture " + tx.name + " uses format " + f + " that is not in: ARGB32, RGBA32, BGRA32, RGB24, Alpha8 or DXT. These textures cannot be resized at runtime. Try changing texture format. If format says 'compressed' try changing it to 'truecolor'");
  375. result.success = false;
  376. yield break;
  377. }
  378. else
  379. {
  380. tx = (Texture2D)mat.GetTexture(data.texPropertyNames[propIdx].name);
  381. }
  382. }
  383. }
  384. /*
  385. else if (txx is ProceduralTexture)
  386. {
  387. //if (!MBVersion.IsTextureFormatRaw(((ProceduralTexture)txx).format))
  388. //{
  389. // Debug.LogError("Object " + obj.name + " in the list of objects to mesh uses a ProceduarlTexture that is not in a RAW format. Convert textures to RAW.");
  390. // result.success = false;
  391. // yield break;
  392. //}
  393. tx = txx;
  394. }
  395. */
  396. else
  397. {
  398. Debug.LogError("Object " + obj.name + " in the list of objects to mesh uses a Texture that is not a Texture2D. Cannot build atlases.");
  399. result.success = false;
  400. yield break;
  401. }
  402. }
  403. if (tx != null && data._normalizeTexelDensity)
  404. {
  405. //todo this doesn't take into account tiling and out of bounds UV sampling
  406. if (mar[propIdx].submeshArea == 0)
  407. {
  408. texelDensity = 0f;
  409. }
  410. else
  411. {
  412. texelDensity = (tx.width * tx.height) / (mar[propIdx].submeshArea);
  413. }
  414. }
  415. scale = mat.GetTextureScale(data.texPropertyNames[propIdx].name);
  416. offset = mat.GetTextureOffset(data.texPropertyNames[propIdx].name);
  417. }
  418. mts[propIdx] = new MeshBakerMaterialTexture(tx, offset, scale, texelDensity);
  419. }
  420. data.nonTexturePropertyBlender.CollectAverageValuesOfNonTextureProperties(data.resultMaterial, mat);
  421. Vector2 obUVscale = new Vector2(mar[matIdx].uvRect.width, mar[matIdx].uvRect.height);
  422. Vector2 obUVoffset = new Vector2(mar[matIdx].uvRect.x, mar[matIdx].uvRect.y);
  423. //Add to distinct set of textures if not already there
  424. MB_TextureTilingTreatment tilingTreatment = MB_TextureTilingTreatment.none;
  425. if (data._fixOutOfBoundsUVs)
  426. {
  427. tilingTreatment = MB_TextureTilingTreatment.considerUVs;
  428. }
  429. MB_TexSet setOfTexs = new MB_TexSet(mts, obUVoffset, obUVscale, tilingTreatment); //one of these per submesh
  430. MatAndTransformToMerged matt = new MatAndTransformToMerged(new DRect(obUVoffset, obUVscale), data._fixOutOfBoundsUVs, mat);
  431. setOfTexs.matsAndGOs.mats.Add(matt);
  432. MB_TexSet setOfTexs2 = data.distinctMaterialTextures.Find(x => x.IsEqual(setOfTexs, data._fixOutOfBoundsUVs, data.nonTexturePropertyBlender));
  433. if (setOfTexs2 != null)
  434. {
  435. setOfTexs = setOfTexs2;
  436. }
  437. else
  438. {
  439. data.distinctMaterialTextures.Add(setOfTexs);
  440. }
  441. if (!setOfTexs.matsAndGOs.mats.Contains(matt))
  442. {
  443. setOfTexs.matsAndGOs.mats.Add(matt);
  444. }
  445. if (!setOfTexs.matsAndGOs.gos.Contains(obj))
  446. {
  447. setOfTexs.matsAndGOs.gos.Add(obj);
  448. if (!usedObjsToMesh.Contains(obj)) usedObjsToMesh.Add(obj);
  449. }
  450. }
  451. }
  452. if (LOG_LEVEL >= MB2_LogLevel.debug) Debug.Log(String.Format("Step1_CollectDistinctTextures collected {0} sets of textures fixOutOfBoundsUV={1} considerNonTextureProperties={2}", data.distinctMaterialTextures.Count, data._fixOutOfBoundsUVs, data._considerNonTextureProperties));
  453. if (data.distinctMaterialTextures.Count == 0)
  454. {
  455. Debug.LogError("None of the source object materials matched any of the allowed materials for this submesh.");
  456. result.success = false;
  457. yield break;
  458. }
  459. MB3_TextureCombinerMerging merger = new MB3_TextureCombinerMerging(data._considerNonTextureProperties, data.nonTexturePropertyBlender, data._fixOutOfBoundsUVs, LOG_LEVEL);
  460. merger.MergeOverlappingDistinctMaterialTexturesAndCalcMaterialSubrects(data.distinctMaterialTextures);
  461. if (LOG_LEVEL >= MB2_LogLevel.debug) Debug.Log("Total time Step1_CollectDistinctTextures " + (sw.ElapsedMilliseconds).ToString("f5"));
  462. yield break;
  463. }
  464. private static CreateAtlasForProperty[] CalculateAllTexturesAreNullAndSameColor(MB3_TextureCombinerPipeline.TexturePipelineData data, MB2_LogLevel LOG_LEVEL)
  465. {
  466. // check if all textures are null and use same color for each atlas
  467. // will not generate an atlas if so
  468. CreateAtlasForProperty[] shouldWeCreateAtlasForProp = new CreateAtlasForProperty[data.texPropertyNames.Count];
  469. for (int propIdx = 0; propIdx < data.texPropertyNames.Count; propIdx++)
  470. {
  471. MeshBakerMaterialTexture firstTexture = data.distinctMaterialTextures[0].ts[propIdx];
  472. Color firstColor = Color.black;
  473. if (data._considerNonTextureProperties)
  474. {
  475. firstColor = data.nonTexturePropertyBlender.GetColorAsItWouldAppearInAtlasIfNoTexture(data.distinctMaterialTextures[0].matsAndGOs.mats[0].mat, data.texPropertyNames[propIdx]);
  476. }
  477. int numTexturesExisting = 0;
  478. int numTexturesMatchinFirst = 0;
  479. int numNonTexturePropertiesMatchingFirst = 0;
  480. for (int j = 0; j < data.distinctMaterialTextures.Count; j++)
  481. {
  482. if (!data.distinctMaterialTextures[j].ts[propIdx].isNull)
  483. {
  484. numTexturesExisting++;
  485. }
  486. if (firstTexture.AreTexturesEqual(data.distinctMaterialTextures[j].ts[propIdx]))
  487. {
  488. numTexturesMatchinFirst++;
  489. }
  490. if (data._considerNonTextureProperties)
  491. {
  492. Color colJ = data.nonTexturePropertyBlender.GetColorAsItWouldAppearInAtlasIfNoTexture(data.distinctMaterialTextures[j].matsAndGOs.mats[0].mat, data.texPropertyNames[propIdx]);
  493. if (colJ == firstColor)
  494. {
  495. numNonTexturePropertiesMatchingFirst++;
  496. }
  497. }
  498. }
  499. shouldWeCreateAtlasForProp[propIdx].allTexturesAreNull = numTexturesExisting == 0;
  500. shouldWeCreateAtlasForProp[propIdx].allTexturesAreSame = numTexturesMatchinFirst == data.distinctMaterialTextures.Count;
  501. shouldWeCreateAtlasForProp[propIdx].allNonTexturePropsAreSame = numNonTexturePropertiesMatchingFirst == data.distinctMaterialTextures.Count;
  502. if (LOG_LEVEL >= MB2_LogLevel.trace) Debug.Log(String.Format("AllTexturesAreNullAndSameColor prop: {0} createAtlas:{1} val:{2}", data.texPropertyNames[propIdx].name, MB3_TextureCombinerPipeline._ShouldWeCreateAtlasForThisProperty(propIdx, data._considerNonTextureProperties, shouldWeCreateAtlasForProp), shouldWeCreateAtlasForProp[propIdx]));
  503. }
  504. return shouldWeCreateAtlasForProp;
  505. }
  506. //Textures in each material (_mainTex, Bump, Spec ect...) must be same size
  507. //Calculate the best sized to use. Takes into account tiling
  508. //if only one texture in atlas re-uses original sizes
  509. internal static IEnumerator CalculateIdealSizesForTexturesInAtlasAndPadding(ProgressUpdateDelegate progressInfo,
  510. MB3_TextureCombiner.CombineTexturesIntoAtlasesCoroutineResult result,
  511. MB3_TextureCombinerPipeline.TexturePipelineData data,
  512. MB3_TextureCombiner combiner,
  513. MB2_EditorMethodsInterface textureEditorMethods,
  514. MB2_LogLevel LOG_LEVEL)
  515. {
  516. System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
  517. sw.Start();
  518. MeshBakerMaterialTexture.readyToBuildAtlases = true;
  519. data.allTexturesAreNullAndSameColor = CalculateAllTexturesAreNullAndSameColor(data, LOG_LEVEL);
  520. //calculate size of rectangles in atlas
  521. int _padding = data._atlasPadding;
  522. if (data.distinctMaterialTextures.Count == 1 && data._fixOutOfBoundsUVs == false && data._considerNonTextureProperties == false)
  523. {
  524. if (LOG_LEVEL >= MB2_LogLevel.info) Debug.Log("All objects use the same textures in this set of atlases. Original textures will be reused instead of creating atlases.");
  525. _padding = 0;
  526. data.distinctMaterialTextures[0].SetThisIsOnlyTexSetInAtlasTrue();
  527. data.distinctMaterialTextures[0].SetTilingTreatmentAndAdjustEncapsulatingSamplingRect(MB_TextureTilingTreatment.edgeToEdgeXY);
  528. }
  529. Debug.Assert(data.allTexturesAreNullAndSameColor.Length == data.texPropertyNames.Count, "allTexturesAreNullAndSameColor array must be the same length of texPropertyNames.");
  530. for (int i = 0; i < data.distinctMaterialTextures.Count; i++)
  531. {
  532. if (LOG_LEVEL >= MB2_LogLevel.debug) Debug.Log("Calculating ideal sizes for texSet TexSet " + i + " of " + data.distinctMaterialTextures.Count);
  533. MB_TexSet txs = data.distinctMaterialTextures[i];
  534. txs.idealWidth = 1;
  535. txs.idealHeight = 1;
  536. int tWidth = 1;
  537. int tHeight = 1;
  538. Debug.Assert(txs.ts.Length == data.texPropertyNames.Count, "length of arrays in each element of distinctMaterialTextures must be texPropertyNames.Count");
  539. //get the best size all textures in a TexSet must be the same size.
  540. for (int propIdx = 0; propIdx < data.texPropertyNames.Count; propIdx++)
  541. {
  542. if (MB3_TextureCombinerPipeline._ShouldWeCreateAtlasForThisProperty(propIdx, data._considerNonTextureProperties, data.allTexturesAreNullAndSameColor))
  543. {
  544. MeshBakerMaterialTexture matTex = txs.ts[propIdx];
  545. if (LOG_LEVEL >= MB2_LogLevel.trace) Debug.Log(string.Format("Calculating ideal size for texSet {0} property {1}", i, data.texPropertyNames[propIdx].name));
  546. if (!matTex.matTilingRect.size.Equals(Vector2.one) && data.distinctMaterialTextures.Count > 1)
  547. {
  548. if (LOG_LEVEL >= MB2_LogLevel.warn) Debug.LogWarning("Texture " + matTex.GetTexName() + "is tiled by " + matTex.matTilingRect.size + " tiling will be baked into a texture with maxSize:" + data._maxTilingBakeSize);
  549. }
  550. if (!txs.obUVscale.Equals(Vector2.one) && data.distinctMaterialTextures.Count > 1 && data._fixOutOfBoundsUVs)
  551. {
  552. if (LOG_LEVEL >= MB2_LogLevel.warn) Debug.LogWarning("Texture " + matTex.GetTexName() + " has out of bounds UVs that effectively tile by " + txs.obUVscale + " tiling will be baked into a texture with maxSize:" + data._maxTilingBakeSize);
  553. }
  554. if (matTex.isNull)
  555. {
  556. txs.SetEncapsulatingRect(propIdx, data._fixOutOfBoundsUVs);
  557. if (LOG_LEVEL >= MB2_LogLevel.trace) Debug.Log(String.Format("No source texture creating a 16x16 texture for {0} texSet {1} srcMat {2}", data.texPropertyNames[propIdx].name, i, txs.matsAndGOs.mats[0].GetMaterialName()));
  558. }
  559. if (!matTex.isNull)
  560. {
  561. Vector2 dim = MB3_TextureCombinerPipeline.GetAdjustedForScaleAndOffset2Dimensions(matTex, txs.obUVoffset, txs.obUVscale, data, LOG_LEVEL);
  562. if ((int)(dim.x * dim.y) > tWidth * tHeight)
  563. {
  564. if (LOG_LEVEL >= MB2_LogLevel.trace) Debug.Log(" matTex " + matTex.GetTexName() + " " + dim + " has a bigger size than " + tWidth + " " + tHeight);
  565. tWidth = (int)dim.x;
  566. tHeight = (int)dim.y;
  567. }
  568. }
  569. }
  570. }
  571. if (data._resizePowerOfTwoTextures)
  572. {
  573. if (tWidth <= _padding * 5)
  574. {
  575. Debug.LogWarning(String.Format("Some of the textures have widths close to the size of the padding. It is not recommended to use _resizePowerOfTwoTextures with widths this small.", txs.ToString()));
  576. }
  577. if (tHeight <= _padding * 5)
  578. {
  579. Debug.LogWarning(String.Format("Some of the textures have heights close to the size of the padding. It is not recommended to use _resizePowerOfTwoTextures with heights this small.", txs.ToString()));
  580. }
  581. if (IsPowerOfTwo(tWidth))
  582. {
  583. tWidth -= _padding * 2;
  584. }
  585. if (IsPowerOfTwo(tHeight))
  586. {
  587. tHeight -= _padding * 2;
  588. }
  589. if (tWidth < 1) tWidth = 1;
  590. if (tHeight < 1) tHeight = 1;
  591. }
  592. if (LOG_LEVEL >= MB2_LogLevel.trace) Debug.Log(" Ideal size is " + tWidth + " " + tHeight);
  593. txs.idealWidth = tWidth;
  594. txs.idealHeight = tHeight;
  595. }
  596. data._atlasPadding = _padding;
  597. if (LOG_LEVEL >= MB2_LogLevel.debug) Debug.Log("Total time Step2 Calculate Ideal Sizes part1: " + sw.Elapsed.ToString());
  598. yield break;
  599. }
  600. internal static AtlasPackingResult[] __Step3_RunTexturePacker(TexturePipelineData data, MB_ITextureCombinerPacker texturePacker, MB2_LogLevel LOG_LEVEL)
  601. {
  602. AtlasPackingResult[] apr = texturePacker.CalculateAtlasRectangles(data, true, LOG_LEVEL); // __RuntTexturePackerOnly(data, texturePacker, LOG_LEVEL);
  603. //copy materials PackingResults
  604. for (int i = 0; i < apr.Length; i++)
  605. {
  606. List<MatsAndGOs> matsList = new List<MatsAndGOs>();
  607. apr[i].data = matsList;
  608. for (int j = 0; j < apr[i].srcImgIdxs.Length; j++)
  609. {
  610. MB_TexSet ts = data.distinctMaterialTextures[apr[i].srcImgIdxs[j]];
  611. matsList.Add(ts.matsAndGOs);
  612. }
  613. }
  614. return apr;
  615. }
  616. internal static MB_ITextureCombinerPacker CreatePacker(bool onlyOneTextureInAtlasReuseTextures, MB2_PackingAlgorithmEnum packingAlgorithm)
  617. {
  618. if (onlyOneTextureInAtlasReuseTextures)
  619. {
  620. return new MB3_TextureCombinerPackerOneTextureInAtlas();
  621. }
  622. else if (packingAlgorithm == MB2_PackingAlgorithmEnum.UnitysPackTextures)
  623. {
  624. return new MB3_TextureCombinerPackerUnity();
  625. }
  626. else if (packingAlgorithm == MB2_PackingAlgorithmEnum.MeshBakerTexturePacker_Horizontal)
  627. {
  628. if (USE_EXPERIMENTAL_HOIZONTALVERTICAL)
  629. {
  630. return new MB3_TextureCombinerPackerMeshBakerHorizontalVertical(MB3_TextureCombinerPackerMeshBakerHorizontalVertical.AtlasDirection.horizontal);
  631. } else
  632. {
  633. return new MB3_TextureCombinerPackerMeshBaker();
  634. }
  635. }
  636. else if (packingAlgorithm == MB2_PackingAlgorithmEnum.MeshBakerTexturePacker_Vertical)
  637. {
  638. if (USE_EXPERIMENTAL_HOIZONTALVERTICAL)
  639. {
  640. return new MB3_TextureCombinerPackerMeshBakerHorizontalVertical(MB3_TextureCombinerPackerMeshBakerHorizontalVertical.AtlasDirection.vertical);
  641. } else
  642. {
  643. return new MB3_TextureCombinerPackerMeshBaker();
  644. }
  645. }
  646. else if (packingAlgorithm == MB2_PackingAlgorithmEnum.MeshBakerTexturePacker)
  647. {
  648. return new MB3_TextureCombinerPackerMeshBaker();
  649. }
  650. else
  651. {
  652. return new MB3_TextureCombinerPackerMeshBakerFast();
  653. }
  654. }
  655. internal static IEnumerator __Step3_BuildAndSaveAtlasesAndStoreResults(MB3_TextureCombiner.CombineTexturesIntoAtlasesCoroutineResult result,
  656. ProgressUpdateDelegate progressInfo,
  657. TexturePipelineData data,
  658. MB3_TextureCombiner combiner,
  659. MB_ITextureCombinerPacker packer,
  660. AtlasPackingResult atlasPackingResult,
  661. MB2_EditorMethodsInterface textureEditorMethods, MB_AtlasesAndRects resultAtlasesAndRects,
  662. StringBuilder report,
  663. MB2_LogLevel LOG_LEVEL)
  664. {
  665. System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
  666. sw.Start();
  667. //run the garbage collector to free up as much memory as possible before bake to reduce MissingReferenceException problems
  668. GC.Collect();
  669. Texture2D[] atlases = new Texture2D[data.numAtlases];
  670. if (LOG_LEVEL >= MB2_LogLevel.debug) Debug.Log("time Step 3 Create And Save Atlases part 1 " + sw.Elapsed.ToString());
  671. yield return packer.CreateAtlases(progressInfo, data, combiner, atlasPackingResult, atlases, textureEditorMethods, LOG_LEVEL);
  672. float t3 = sw.ElapsedMilliseconds;
  673. data.nonTexturePropertyBlender.AdjustNonTextureProperties(data.resultMaterial, data.texPropertyNames, data.distinctMaterialTextures, textureEditorMethods);
  674. if (data.distinctMaterialTextures.Count > 0) data.distinctMaterialTextures[0].AdjustResultMaterialNonTextureProperties(data.resultMaterial, data.texPropertyNames);
  675. if (progressInfo != null) progressInfo("Building Report", .7f);
  676. //report on atlases created
  677. StringBuilder atlasMessage = new StringBuilder();
  678. atlasMessage.AppendLine("---- Atlases ------");
  679. for (int i = 0; i < data.numAtlases; i++)
  680. {
  681. if (atlases[i] != null)
  682. {
  683. atlasMessage.AppendLine("Created Atlas For: " + data.texPropertyNames[i].name + " h=" + atlases[i].height + " w=" + atlases[i].width);
  684. }
  685. else if (!_ShouldWeCreateAtlasForThisProperty(i, data._considerNonTextureProperties, data.allTexturesAreNullAndSameColor))
  686. {
  687. atlasMessage.AppendLine("Did not create atlas for " + data.texPropertyNames[i].name + " because all source textures were null.");
  688. }
  689. }
  690. report.Append(atlasMessage.ToString());
  691. List<MB_MaterialAndUVRect> mat2rect_map = new List<MB_MaterialAndUVRect>();
  692. for (int i = 0; i < data.distinctMaterialTextures.Count; i++)
  693. {
  694. MB_TexSet texSet = data.distinctMaterialTextures[i];
  695. List<MatAndTransformToMerged> mats = texSet.matsAndGOs.mats;
  696. Rect allPropsUseSameTiling_encapsulatingSamplingRect, propsUseDifferntTiling_obUVRect;
  697. texSet.GetRectsForTextureBakeResults(out allPropsUseSameTiling_encapsulatingSamplingRect, out propsUseDifferntTiling_obUVRect);
  698. for (int j = 0; j < mats.Count; j++)
  699. {
  700. Rect allPropsUseSameTiling_sourceMaterialTiling = texSet.GetMaterialTilingRectForTextureBakerResults(j);
  701. MB_MaterialAndUVRect key = new MB_MaterialAndUVRect(
  702. mats[j].mat,
  703. atlasPackingResult.rects[i],
  704. texSet.allTexturesUseSameMatTiling,
  705. allPropsUseSameTiling_sourceMaterialTiling,
  706. allPropsUseSameTiling_encapsulatingSamplingRect,
  707. propsUseDifferntTiling_obUVRect,
  708. texSet.tilingTreatment,
  709. mats[j].objName);
  710. if (!mat2rect_map.Contains(key))
  711. {
  712. mat2rect_map.Add(key);
  713. }
  714. }
  715. }
  716. resultAtlasesAndRects.atlases = atlases; // one per texture on result shader
  717. resultAtlasesAndRects.texPropertyNames = ShaderTextureProperty.GetNames(data.texPropertyNames); // one per texture on source shader
  718. resultAtlasesAndRects.mat2rect_map = mat2rect_map;
  719. if (progressInfo != null) progressInfo("Restoring Texture Formats & Read Flags", .8f);
  720. combiner._destroyAllTemporaryTextures();
  721. if (textureEditorMethods != null) textureEditorMethods.RestoreReadFlagsAndFormats(progressInfo);
  722. if (report != null && LOG_LEVEL >= MB2_LogLevel.info) Debug.Log(report.ToString());
  723. if (LOG_LEVEL >= MB2_LogLevel.debug) Debug.Log("Time Step 3 Create And Save Atlases part 3 " + (sw.ElapsedMilliseconds - t3).ToString("f5"));
  724. if (LOG_LEVEL >= MB2_LogLevel.debug) Debug.Log("Total time Step 3 Create And Save Atlases " + sw.Elapsed.ToString());
  725. yield break;
  726. }
  727. internal static StringBuilder GenerateReport(MB3_TextureCombinerPipeline.TexturePipelineData data)
  728. {
  729. //generate report want to do this before
  730. StringBuilder report = new StringBuilder();
  731. if (data.numAtlases > 0)
  732. {
  733. report = new StringBuilder();
  734. report.AppendLine("Report");
  735. for (int i = 0; i < data.distinctMaterialTextures.Count; i++)
  736. {
  737. MB_TexSet txs = data.distinctMaterialTextures[i];
  738. report.AppendLine("----------");
  739. report.Append("This set of textures will be resized to:" + txs.idealWidth + "x" + txs.idealHeight + "\n");
  740. for (int j = 0; j < txs.ts.Length; j++)
  741. {
  742. if (!txs.ts[j].isNull)
  743. {
  744. report.Append(" [" + data.texPropertyNames[j].name + " " + txs.ts[j].GetTexName() + " " + txs.ts[j].width + "x" + txs.ts[j].height + "]");
  745. if (txs.ts[j].matTilingRect.size != Vector2.one || txs.ts[j].matTilingRect.min != Vector2.zero) report.AppendFormat(" material scale {0} offset{1} ", txs.ts[j].matTilingRect.size.ToString("G4"), txs.ts[j].matTilingRect.min.ToString("G4"));
  746. if (txs.obUVscale != Vector2.one || txs.obUVoffset != Vector2.zero) report.AppendFormat(" obUV scale {0} offset{1} ", txs.obUVscale.ToString("G4"), txs.obUVoffset.ToString("G4"));
  747. report.AppendLine("");
  748. }
  749. else
  750. {
  751. report.Append(" [" + data.texPropertyNames[j].name + " null ");
  752. if (!MB3_TextureCombinerPipeline._ShouldWeCreateAtlasForThisProperty(j, data._considerNonTextureProperties, data.allTexturesAreNullAndSameColor))
  753. {
  754. report.Append("no atlas will be created all textures null]\n");
  755. }
  756. else
  757. {
  758. report.AppendFormat("a 16x16 texture will be created]\n");
  759. }
  760. }
  761. }
  762. report.AppendLine("");
  763. report.Append("Materials using:");
  764. for (int j = 0; j < txs.matsAndGOs.mats.Count; j++)
  765. {
  766. report.Append(txs.matsAndGOs.mats[j].mat.name + ", ");
  767. }
  768. report.AppendLine("");
  769. }
  770. }
  771. return report;
  772. }
  773. /*
  774. internal static AtlasPackingResult[] __RuntTexturePackerOnly(TexturePipelineData data, MB_ITextureCombinerPacker texturePacker, MB2_LogLevel LOG_LEVEL)
  775. {
  776. AtlasPackingResult[] packerRects;
  777. if (data.OnlyOneTextureInAtlasReuseTextures())
  778. {
  779. if (LOG_LEVEL >= MB2_LogLevel.debug) Debug.Log("Only one image per atlas. Will re-use original texture");
  780. packerRects = new AtlasPackingResult[1];
  781. AtlasPadding[] paddings = new AtlasPadding[] { new AtlasPadding(data._atlasPadding) };
  782. packerRects[0] = new AtlasPackingResult(paddings);
  783. packerRects[0].rects = new Rect[1];
  784. packerRects[0].srcImgIdxs = new int[] { 0 };
  785. packerRects[0].rects[0] = new Rect(0f, 0f, 1f, 1f);
  786. MeshBakerMaterialTexture dmt = null;
  787. if (data.distinctMaterialTextures[0].ts.Length > 0)
  788. {
  789. dmt = data.distinctMaterialTextures[0].ts[0];
  790. }
  791. packerRects[0].atlasX = dmt.isNull ? 16 : dmt.width;
  792. packerRects[0].atlasY = dmt.isNull ? 16 : dmt.height;
  793. packerRects[0].usedW = dmt.isNull ? 16 : dmt.width;
  794. packerRects[0].usedH = dmt.isNull ? 16 : dmt.height;
  795. }
  796. else
  797. {
  798. List<Vector2> imageSizes = new List<Vector2>();
  799. List<AtlasPadding> paddings = new List<AtlasPadding>();
  800. for (int i = 0; i < data.distinctMaterialTextures.Count; i++)
  801. {
  802. imageSizes.Add(new Vector2(data.distinctMaterialTextures[i].idealWidth, data.distinctMaterialTextures[i].idealHeight));
  803. paddings.Add(new AtlasPadding(data._atlasPadding));
  804. }
  805. MB2_TexturePacker tp = CreateTexturePacker(data._packingAlgorithm);
  806. tp.atlasMustBePowerOfTwo = data._meshBakerTexturePackerForcePowerOfTwo;
  807. packerRects = tp.GetRects(imageSizes, paddings, data._maxAtlasSize, data._maxAtlasSize, true);
  808. //Debug.Assert(packerRects.Length != 0);
  809. }
  810. return packerRects;
  811. }
  812. */
  813. internal static MB2_TexturePacker CreateTexturePacker(MB2_PackingAlgorithmEnum _packingAlgorithm)
  814. {
  815. if (_packingAlgorithm == MB2_PackingAlgorithmEnum.MeshBakerTexturePacker)
  816. {
  817. return new MB2_TexturePackerRegular();
  818. }
  819. else if (_packingAlgorithm == MB2_PackingAlgorithmEnum.MeshBakerTexturePacker_Fast)
  820. {
  821. return new MB2_TexturePackerRegular();
  822. }
  823. else if (_packingAlgorithm == MB2_PackingAlgorithmEnum.MeshBakerTexturePacker_Horizontal)
  824. {
  825. MB2_TexturePackerHorizontalVert tp = new MB2_TexturePackerHorizontalVert();
  826. tp.packingOrientation = MB2_TexturePackerHorizontalVert.TexturePackingOrientation.horizontal;
  827. return tp;
  828. }
  829. else if (_packingAlgorithm == MB2_PackingAlgorithmEnum.MeshBakerTexturePacker_Vertical)
  830. {
  831. MB2_TexturePackerHorizontalVert tp = new MB2_TexturePackerHorizontalVert();
  832. tp.packingOrientation = MB2_TexturePackerHorizontalVert.TexturePackingOrientation.vertical;
  833. return tp;
  834. }
  835. else
  836. {
  837. Debug.LogError("packing algorithm must be one of the MeshBaker options to create a Texture Packer");
  838. }
  839. return null;
  840. }
  841. internal static Vector2 GetAdjustedForScaleAndOffset2Dimensions(MeshBakerMaterialTexture source, Vector2 obUVoffset, Vector2 obUVscale, TexturePipelineData data, MB2_LogLevel LOG_LEVEL)
  842. {
  843. if (source.matTilingRect.x == 0f && source.matTilingRect.y == 0f && source.matTilingRect.width == 1f && source.matTilingRect.height == 1f)
  844. {
  845. if (data._fixOutOfBoundsUVs)
  846. {
  847. if (obUVoffset.x == 0f && obUVoffset.y == 0f && obUVscale.x == 1f && obUVscale.y == 1f)
  848. {
  849. return new Vector2(source.width, source.height); //no adjustment necessary
  850. }
  851. }
  852. else
  853. {
  854. return new Vector2(source.width, source.height); //no adjustment necessary
  855. }
  856. }
  857. if (LOG_LEVEL >= MB2_LogLevel.debug) Debug.Log("GetAdjustedForScaleAndOffset2Dimensions: " + source.GetTexName() + " " + obUVoffset + " " + obUVscale);
  858. Rect encapsulatingSamplingRect = source.GetEncapsulatingSamplingRect().GetRect();
  859. float newWidth = encapsulatingSamplingRect.width * source.width;
  860. float newHeight = encapsulatingSamplingRect.height * source.height;
  861. if (newWidth > data._maxTilingBakeSize) newWidth = data._maxTilingBakeSize;
  862. if (newHeight > data._maxTilingBakeSize) newHeight = data._maxTilingBakeSize;
  863. if (newWidth < 1f) newWidth = 1f;
  864. if (newHeight < 1f) newHeight = 1f;
  865. return new Vector2(newWidth, newHeight);
  866. }
  867. /*
  868. Unity uses a non-standard format for storing normals for some platforms. Imagine the standard format is English, Unity's is French
  869. When the normal-map checkbox is ticked on the asset importer the normal map is translated into french. When we build the normal atlas
  870. we are reading the french. When we save and click the normal map tickbox we are translating french -> french. A double transladion that
  871. breaks the normal map. To fix this we need to "unconvert" the normal map to english when saving the atlas as a texture so that unity importer
  872. can do its thing properly.
  873. */
  874. internal static Color32 ConvertNormalFormatFromUnity_ToStandard(Color32 c)
  875. {
  876. Vector3 n = Vector3.zero;
  877. n.x = c.a * 2f - 1f;
  878. n.y = c.g * 2f - 1f;
  879. n.z = Mathf.Sqrt(1 - n.x * n.x - n.y * n.y);
  880. //now repack in the regular format
  881. Color32 cc = new Color32();
  882. cc.a = 1;
  883. cc.r = (byte)((n.x + 1f) * .5f);
  884. cc.g = (byte)((n.y + 1f) * .5f);
  885. cc.b = (byte)((n.z + 1f) * .5f);
  886. return cc;
  887. }
  888. internal static float GetSubmeshArea(Mesh m, int submeshIdx)
  889. {
  890. if (submeshIdx >= m.subMeshCount || submeshIdx < 0)
  891. {
  892. return 0f;
  893. }
  894. Vector3[] vs = m.vertices;
  895. int[] tris = m.GetIndices(submeshIdx);
  896. float area = 0f;
  897. for (int i = 0; i < tris.Length; i += 3)
  898. {
  899. Vector3 v0 = vs[tris[i]];
  900. Vector3 v1 = vs[tris[i + 1]];
  901. Vector3 v2 = vs[tris[i + 2]];
  902. Vector3 cross = Vector3.Cross(v1 - v0, v2 - v0);
  903. area += cross.magnitude / 2f;
  904. }
  905. return area;
  906. }
  907. internal static bool IsPowerOfTwo(int x)
  908. {
  909. return (x & (x - 1)) == 0;
  910. }
  911. }
  912. }