MB3_TextureCombinerPipeline.cs 57 KB

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