MB3_TextureCombinerMerging.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. using UnityEngine;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Text;
  5. namespace DigitalOpus.MB.Core
  6. {
  7. public class MB3_TextureCombinerMerging
  8. {
  9. public static Rect BuildTransformMeshUV2AtlasRect(
  10. bool considerMeshUVs,
  11. Rect _atlasRect,
  12. Rect _obUVRect,
  13. Rect _sourceMaterialTiling,
  14. Rect _encapsulatingRect)
  15. {
  16. DRect atlasRect = new DRect(_atlasRect);
  17. DRect obUVRect;
  18. if (considerMeshUVs)
  19. {
  20. obUVRect = new DRect(_obUVRect); //this is the uvRect in src mesh
  21. }
  22. else
  23. {
  24. obUVRect = new DRect(0.0, 0.0, 1.0, 1.0);
  25. }
  26. DRect sourceMaterialTiling = new DRect(_sourceMaterialTiling);
  27. DRect encapsulatingRectMatAndUVTiling = new DRect(_encapsulatingRect);
  28. DRect encapsulatingRectMatAndUVTilingInverse = MB3_UVTransformUtility.InverseTransform(ref encapsulatingRectMatAndUVTiling);
  29. DRect toNormalizedUVs = MB3_UVTransformUtility.InverseTransform(ref obUVRect);
  30. DRect meshFullSamplingRect = MB3_UVTransformUtility.CombineTransforms(ref obUVRect, ref sourceMaterialTiling);
  31. DRect shiftToFitInEncapsulating = MB3_UVTransformUtility.GetShiftTransformToFitBinA(ref encapsulatingRectMatAndUVTiling, ref meshFullSamplingRect);
  32. meshFullSamplingRect = MB3_UVTransformUtility.CombineTransforms(ref meshFullSamplingRect, ref shiftToFitInEncapsulating);
  33. //transform between full sample rect and encapsulating rect
  34. DRect relativeTrans = MB3_UVTransformUtility.CombineTransforms(ref meshFullSamplingRect, ref encapsulatingRectMatAndUVTilingInverse);
  35. // [transform] = [toNormalizedUVs][relativeTrans][uvSubRectInAtlas]
  36. DRect trans = MB3_UVTransformUtility.CombineTransforms(ref toNormalizedUVs, ref relativeTrans);
  37. trans = MB3_UVTransformUtility.CombineTransforms(ref trans, ref atlasRect);
  38. Rect rr = trans.GetRect();
  39. return rr;
  40. }
  41. bool _considerNonTextureProperties = false;
  42. MB3_TextureCombinerNonTextureProperties resultMaterialTextureBlender;
  43. bool fixOutOfBoundsUVs = true;
  44. public MB2_LogLevel LOG_LEVEL = MB2_LogLevel.info;
  45. private static bool LOG_LEVEL_TRACE_MERGE_MAT_SUBRECTS = false;
  46. public MB3_TextureCombinerMerging(bool considerNonTextureProps, MB3_TextureCombinerNonTextureProperties resultMaterialTexBlender, bool fixObUVs, MB2_LogLevel logLevel)
  47. {
  48. LOG_LEVEL = logLevel;
  49. _considerNonTextureProperties = considerNonTextureProps;
  50. resultMaterialTextureBlender = resultMaterialTexBlender;
  51. fixOutOfBoundsUVs = fixObUVs;
  52. }
  53. public void MergeOverlappingDistinctMaterialTexturesAndCalcMaterialSubrects(List<MB_TexSet> distinctMaterialTextures)
  54. {
  55. if (LOG_LEVEL >= MB2_LogLevel.debug)
  56. {
  57. Debug.Log("MergeOverlappingDistinctMaterialTexturesAndCalcMaterialSubrects");
  58. }
  59. int numMerged = 0;
  60. // IMPORTANT: Note that the verts stored in the mesh are NOT Normalized UV Coords. They are normalized * [UVTrans]. To get normalized UV
  61. // coords we must multiply them by [invUVTrans]. Need to do this to the verts in the mesh before we do any transforms with them.
  62. // Also check that all textures use same tiling. This is a prerequisite for merging.
  63. // Mark MB3_TexSet that are mergable (allTexturesUseSameMatTiling)
  64. for (int i = 0; i < distinctMaterialTextures.Count; i++)
  65. {
  66. MB_TexSet tx = distinctMaterialTextures[i];
  67. int idxOfFirstNotNull = -1;
  68. bool allAreSame = true;
  69. DRect firstRect = new DRect();
  70. for (int propIdx = 0; propIdx < tx.ts.Length; propIdx++)
  71. {
  72. if (idxOfFirstNotNull != -1)
  73. {
  74. if (!tx.ts[propIdx].isNull && firstRect != tx.ts[propIdx].matTilingRect)
  75. {
  76. allAreSame = false;
  77. }
  78. }
  79. else if (!tx.ts[propIdx].isNull)
  80. {
  81. idxOfFirstNotNull = propIdx;
  82. firstRect = tx.ts[propIdx].matTilingRect;
  83. }
  84. }
  85. if (LOG_LEVEL >= MB2_LogLevel.debug || LOG_LEVEL_TRACE_MERGE_MAT_SUBRECTS == true)
  86. {
  87. if (allAreSame)
  88. {
  89. Debug.LogFormat("TextureSet {0} allTexturesUseSameMatTiling = {1}", i, allAreSame);
  90. }
  91. else
  92. {
  93. Debug.Log(string.Format("Textures in material(s) do not all use the same material tiling. This set of textures will not be considered for merge: {0} ", tx.GetDescription()));
  94. }
  95. }
  96. if (allAreSame)
  97. {
  98. tx.SetAllTexturesUseSameMatTilingTrue();
  99. }
  100. }
  101. for (int i = 0; i < distinctMaterialTextures.Count; i++)
  102. {
  103. MB_TexSet tx = distinctMaterialTextures[i];
  104. for (int matIdx = 0; matIdx < tx.matsAndGOs.mats.Count; matIdx++)
  105. {
  106. if (tx.matsAndGOs.gos.Count > 0) {
  107. tx.matsAndGOs.mats[matIdx].objName = tx.matsAndGOs.gos[0].name;
  108. } else if (tx.ts[0] != null) {
  109. tx.matsAndGOs.mats[matIdx].objName = string.Format("[objWithTx:{0} atlasBlock:{1} matIdx{2}]",tx.ts[0].GetTexName(),i,matIdx);
  110. } else {
  111. tx.matsAndGOs.mats[matIdx].objName = string.Format("[objWithTx:{0} atlasBlock:{1} matIdx{2}]", "Unknown", i, matIdx);
  112. }
  113. }
  114. tx.CalcInitialFullSamplingRects(fixOutOfBoundsUVs);
  115. tx.CalcMatAndUVSamplingRects();
  116. }
  117. // need to calculate the srcSampleRect for the complete tiling in the atlas
  118. // for each material need to know what the subrect would be in the atlas if material UVRect was 0,0,1,1 and Merged uvRect was full tiling
  119. List<int> MarkedForDeletion = new List<int>();
  120. for (int i = 0; i < distinctMaterialTextures.Count; i++)
  121. {
  122. MB_TexSet tx2 = distinctMaterialTextures[i];
  123. for (int j = i + 1; j < distinctMaterialTextures.Count; j++)
  124. {
  125. MB_TexSet tx1 = distinctMaterialTextures[j];
  126. if (tx1.AllTexturesAreSameForMerge(tx2, _considerNonTextureProperties, resultMaterialTextureBlender))
  127. {
  128. double accumulatedAreaCombined = 0f;
  129. double accumulatedAreaNotCombined = 0f;
  130. DRect encapsulatingRectMerged = new DRect();
  131. int idxOfFirstNotNull = -1;
  132. for (int propIdx = 0; propIdx < tx2.ts.Length; propIdx++)
  133. {
  134. if (!tx2.ts[propIdx].isNull)
  135. {
  136. if (idxOfFirstNotNull == -1) idxOfFirstNotNull = propIdx;
  137. }
  138. }
  139. if (idxOfFirstNotNull != -1)
  140. {
  141. // only in here if all properties use the same tiling so don't need to worry about which propIdx we are dealing with
  142. //Get the rect that encapsulates all material and UV tiling for materials and meshes in tx1
  143. DRect encapsulatingRect1 = tx1.matsAndGOs.mats[0].samplingRectMatAndUVTiling;
  144. for (int matIdx = 1; matIdx < tx1.matsAndGOs.mats.Count; matIdx++)
  145. {
  146. DRect tmpSsamplingRectMatAndUVTilingTx1 = tx1.matsAndGOs.mats[matIdx].samplingRectMatAndUVTiling;
  147. encapsulatingRect1 = MB3_UVTransformUtility.GetEncapsulatingRectShifted(ref encapsulatingRect1, ref tmpSsamplingRectMatAndUVTilingTx1);
  148. }
  149. //same for tx2
  150. DRect encapsulatingRect2 = tx2.matsAndGOs.mats[0].samplingRectMatAndUVTiling;
  151. for (int matIdx = 1; matIdx < tx2.matsAndGOs.mats.Count; matIdx++)
  152. {
  153. DRect tmpSsamplingRectMatAndUVTilingTx2 = tx2.matsAndGOs.mats[matIdx].samplingRectMatAndUVTiling;
  154. encapsulatingRect2 = MB3_UVTransformUtility.GetEncapsulatingRectShifted(ref encapsulatingRect2, ref tmpSsamplingRectMatAndUVTilingTx2);
  155. }
  156. encapsulatingRectMerged = MB3_UVTransformUtility.GetEncapsulatingRectShifted(ref encapsulatingRect1, ref encapsulatingRect2);
  157. accumulatedAreaCombined += encapsulatingRectMerged.width * encapsulatingRectMerged.height;
  158. accumulatedAreaNotCombined += encapsulatingRect1.width * encapsulatingRect1.height + encapsulatingRect2.width * encapsulatingRect2.height;
  159. }
  160. else
  161. {
  162. encapsulatingRectMerged = new DRect(0f, 0f, 1f, 1f);
  163. }
  164. //the distinct material textures may overlap.
  165. //if the area of these rectangles combined is less than the sum of these areas of these rectangles then merge these distinctMaterialTextures
  166. if (accumulatedAreaCombined < accumulatedAreaNotCombined)
  167. {
  168. // merge tx2 into tx1
  169. numMerged++;
  170. StringBuilder sb = null;
  171. if (LOG_LEVEL >= MB2_LogLevel.info)
  172. {
  173. sb = new StringBuilder();
  174. sb.AppendFormat("About To Merge:\n TextureSet1 {0}\n TextureSet2 {1}\n", tx1.GetDescription(), tx2.GetDescription());
  175. if (LOG_LEVEL >= MB2_LogLevel.trace)
  176. {
  177. for (int matIdx = 0; matIdx < tx1.matsAndGOs.mats.Count; matIdx++)
  178. {
  179. sb.AppendFormat("tx1 Mat {0} matAndMeshUVRect {1} fullSamplingRect {2}\n",
  180. tx1.matsAndGOs.mats[matIdx].mat, tx1.matsAndGOs.mats[matIdx].samplingRectMatAndUVTiling, tx1.ts[0].GetEncapsulatingSamplingRect());
  181. }
  182. for (int matIdx = 0; matIdx < tx2.matsAndGOs.mats.Count; matIdx++)
  183. {
  184. sb.AppendFormat("tx2 Mat {0} matAndMeshUVRect {1} fullSamplingRect {2}\n",
  185. tx2.matsAndGOs.mats[matIdx].mat, tx2.matsAndGOs.mats[matIdx].samplingRectMatAndUVTiling, tx2.ts[0].GetEncapsulatingSamplingRect());
  186. }
  187. }
  188. }
  189. //copy game objects over
  190. for (int k = 0; k < tx2.matsAndGOs.gos.Count; k++)
  191. {
  192. if (!tx1.matsAndGOs.gos.Contains(tx2.matsAndGOs.gos[k]))
  193. {
  194. tx1.matsAndGOs.gos.Add(tx2.matsAndGOs.gos[k]);
  195. }
  196. }
  197. //copy materials over from tx2 to tx1
  198. for (int matIdx = 0; matIdx < tx2.matsAndGOs.mats.Count; matIdx++)
  199. {
  200. tx1.matsAndGOs.mats.Add(tx2.matsAndGOs.mats[matIdx]);
  201. }
  202. tx1.SetEncapsulatingSamplingRectWhenMergingTexSets(encapsulatingRectMerged);
  203. if (!MarkedForDeletion.Contains(i))
  204. {
  205. MarkedForDeletion.Add(i);
  206. }
  207. if (LOG_LEVEL >= MB2_LogLevel.debug)
  208. {
  209. if (LOG_LEVEL >= MB2_LogLevel.trace)
  210. {
  211. sb.AppendFormat("=== After Merge TextureSet {0}\n", tx1.GetDescription());
  212. for (int matIdx = 0; matIdx < tx1.matsAndGOs.mats.Count; matIdx++)
  213. {
  214. sb.AppendFormat("tx1 Mat {0} matAndMeshUVRect {1} fullSamplingRect {2}\n",
  215. tx1.matsAndGOs.mats[matIdx].mat, tx1.matsAndGOs.mats[matIdx].samplingRectMatAndUVTiling, tx1.ts[0].GetEncapsulatingSamplingRect());
  216. }
  217. //Integrity check that sampling rects fit into enapsulating rects
  218. if (MB3_MeshBakerRoot.DO_INTEGRITY_CHECKS)
  219. {
  220. if (MB3_MeshBakerRoot.DO_INTEGRITY_CHECKS) { DoIntegrityCheckMergedEncapsulatingSamplingRects(distinctMaterialTextures); }
  221. }
  222. }
  223. Debug.Log(sb.ToString());
  224. }
  225. break;
  226. }
  227. else
  228. {
  229. if (LOG_LEVEL >= MB2_LogLevel.debug)
  230. {
  231. Debug.Log(string.Format("Considered merging {0} and {1} but there was not enough overlap. It is more efficient to bake these to separate rectangles.", tx1.GetDescription(), tx2.GetDescription()));
  232. }
  233. }
  234. }
  235. }
  236. }
  237. //remove distinctMaterialTextures that were merged
  238. for (int j = MarkedForDeletion.Count - 1; j >= 0; j--)
  239. {
  240. distinctMaterialTextures.RemoveAt(MarkedForDeletion[j]);
  241. }
  242. MarkedForDeletion.Clear();
  243. if (LOG_LEVEL >= MB2_LogLevel.debug)
  244. {
  245. Debug.Log(string.Format("MergeOverlappingDistinctMaterialTexturesAndCalcMaterialSubrects complete merged {0} now have {1}", numMerged, distinctMaterialTextures.Count));
  246. }
  247. if (MB3_MeshBakerRoot.DO_INTEGRITY_CHECKS) { DoIntegrityCheckMergedEncapsulatingSamplingRects(distinctMaterialTextures); }
  248. }
  249. public void DoIntegrityCheckMergedEncapsulatingSamplingRects(List<MB_TexSet> distinctMaterialTextures)
  250. {
  251. if (MB3_MeshBakerRoot.DO_INTEGRITY_CHECKS)
  252. {
  253. for (int i = 0; i < distinctMaterialTextures.Count; i++)
  254. {
  255. MB_TexSet tx1 = distinctMaterialTextures[i];
  256. if (!tx1.allTexturesUseSameMatTiling)
  257. {
  258. continue;
  259. }
  260. for (int matIdx = 0; matIdx < tx1.matsAndGOs.mats.Count; matIdx++)
  261. {
  262. MatAndTransformToMerged mat = tx1.matsAndGOs.mats[matIdx];
  263. DRect uvR = mat.obUVRectIfTilingSame;
  264. DRect matR = mat.materialTiling;
  265. if (!MB2_TextureBakeResults.IsMeshAndMaterialRectEnclosedByAtlasRect(tx1.tilingTreatment, uvR.GetRect(), matR.GetRect(), tx1.ts[0].GetEncapsulatingSamplingRect().GetRect(),MB2_LogLevel.info))
  266. {
  267. Debug.LogErrorFormat("mesh " + tx1.matsAndGOs.mats[matIdx].objName + "\n" +
  268. " uv=" + uvR + "\n" +
  269. " mat=" + matR.GetRect().ToString("f5") + "\n" +
  270. " samplingRect=" + tx1.matsAndGOs.mats[matIdx].samplingRectMatAndUVTiling.GetRect().ToString("f4") + "\n" +
  271. " encapsulatingRect " + tx1.ts[0].GetEncapsulatingSamplingRect().GetRect().ToString("f4") + "\n");
  272. Debug.LogErrorFormat(string.Format("Integrity check failed. " + tx1.matsAndGOs.mats[matIdx].objName + " Encapsulating sampling rect failed to contain potentialRect\n"));
  273. MB2_TextureBakeResults.IsMeshAndMaterialRectEnclosedByAtlasRect(tx1.tilingTreatment, uvR.GetRect(), matR.GetRect(), tx1.ts[0].GetEncapsulatingSamplingRect().GetRect(), MB2_LogLevel.trace);
  274. Debug.Assert(false);
  275. }
  276. }
  277. }
  278. }
  279. }
  280. }
  281. }