MB3_TextureBaker.cs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573
  1. //----------------------------------------------
  2. // MeshBaker
  3. // Copyright © 2011-2012 Ian Deane
  4. //----------------------------------------------
  5. using UnityEngine;
  6. using System.Collections;
  7. using System.Collections.Specialized;
  8. using System;
  9. using System.Collections.Generic;
  10. using System.Text;
  11. using System.IO;
  12. using DigitalOpus.MB.Core;
  13. /// <summary>
  14. /// Component that handles baking materials into a combined material.
  15. ///
  16. /// The result of the material baking process is a MB2_TextureBakeResults object, which
  17. /// becomes the input for the mesh baking.
  18. ///
  19. /// This class uses the MB_TextureCombiner to do the combining.
  20. ///
  21. /// This class is a Component (MonoBehavior) so it is serialized and found using GetComponent. If
  22. /// you want to access the texture baking functionality without creating a Component then use MB_TextureCombiner
  23. /// directly.
  24. /// </summary>
  25. public class MB3_TextureBaker : MB3_MeshBakerRoot
  26. {
  27. public MB2_LogLevel LOG_LEVEL = MB2_LogLevel.info;
  28. [SerializeField]
  29. protected MB2_TextureBakeResults _textureBakeResults;
  30. public override MB2_TextureBakeResults textureBakeResults
  31. {
  32. get { return _textureBakeResults; }
  33. set { _textureBakeResults = value; }
  34. }
  35. [SerializeField]
  36. protected int _atlasPadding = 1;
  37. public virtual int atlasPadding
  38. {
  39. get { return _atlasPadding; }
  40. set { _atlasPadding = value; }
  41. }
  42. [SerializeField]
  43. protected int _maxAtlasSize = 4096;
  44. public virtual int maxAtlasSize
  45. {
  46. get { return _maxAtlasSize; }
  47. set { _maxAtlasSize = value; }
  48. }
  49. [SerializeField]
  50. protected bool _useMaxAtlasWidthOverride = false;
  51. public virtual bool useMaxAtlasWidthOverride
  52. {
  53. get { return _useMaxAtlasWidthOverride; }
  54. set { _useMaxAtlasWidthOverride = value; }
  55. }
  56. [SerializeField]
  57. protected int _maxAtlasWidthOverride = 4096;
  58. public virtual int maxAtlasWidthOverride
  59. {
  60. get { return _maxAtlasWidthOverride; }
  61. set { _maxAtlasWidthOverride = value; }
  62. }
  63. [SerializeField]
  64. protected bool _useMaxAtlasHeightOverride = false;
  65. public virtual bool useMaxAtlasHeightOverride
  66. {
  67. get { return _useMaxAtlasHeightOverride; }
  68. set { _useMaxAtlasHeightOverride = value; }
  69. }
  70. [SerializeField]
  71. protected int _maxAtlasHeightOverride = 4096;
  72. public virtual int maxAtlasHeightOverride
  73. {
  74. get { return _maxAtlasHeightOverride; }
  75. set { _maxAtlasHeightOverride = value; }
  76. }
  77. [SerializeField]
  78. protected bool _resizePowerOfTwoTextures = false;
  79. public virtual bool resizePowerOfTwoTextures
  80. {
  81. get { return _resizePowerOfTwoTextures; }
  82. set { _resizePowerOfTwoTextures = value; }
  83. }
  84. [SerializeField]
  85. protected bool _fixOutOfBoundsUVs = false; //is considerMeshUVs but can't change because it would break all existing bakers
  86. public virtual bool fixOutOfBoundsUVs
  87. {
  88. get { return _fixOutOfBoundsUVs; }
  89. set { _fixOutOfBoundsUVs = value; }
  90. }
  91. [SerializeField]
  92. protected int _maxTilingBakeSize = 1024;
  93. public virtual int maxTilingBakeSize
  94. {
  95. get { return _maxTilingBakeSize; }
  96. set { _maxTilingBakeSize = value; }
  97. }
  98. [SerializeField]
  99. protected MB2_PackingAlgorithmEnum _packingAlgorithm = MB2_PackingAlgorithmEnum.MeshBakerTexturePacker;
  100. public virtual MB2_PackingAlgorithmEnum packingAlgorithm
  101. {
  102. get { return _packingAlgorithm; }
  103. set { _packingAlgorithm = value; }
  104. }
  105. [SerializeField]
  106. protected bool _meshBakerTexturePackerForcePowerOfTwo = true;
  107. public bool meshBakerTexturePackerForcePowerOfTwo
  108. {
  109. get { return _meshBakerTexturePackerForcePowerOfTwo; }
  110. set { _meshBakerTexturePackerForcePowerOfTwo = value; }
  111. }
  112. [SerializeField]
  113. protected List<ShaderTextureProperty> _customShaderProperties = new List<ShaderTextureProperty>();
  114. public virtual List<ShaderTextureProperty> customShaderProperties
  115. {
  116. get { return _customShaderProperties; }
  117. set { _customShaderProperties = value; }
  118. }
  119. //this is depricated
  120. [SerializeField]
  121. protected List<string> _customShaderPropNames_Depricated = new List<string>();
  122. public virtual List<string> customShaderPropNames
  123. {
  124. get { return _customShaderPropNames_Depricated; }
  125. set { _customShaderPropNames_Depricated = value; }
  126. }
  127. [SerializeField]
  128. protected bool _doMultiMaterial;
  129. public virtual bool doMultiMaterial
  130. {
  131. get { return _doMultiMaterial; }
  132. set { _doMultiMaterial = value; }
  133. }
  134. [SerializeField]
  135. protected bool _doMultiMaterialSplitAtlasesIfTooBig = true;
  136. public virtual bool doMultiMaterialSplitAtlasesIfTooBig
  137. {
  138. get { return _doMultiMaterialSplitAtlasesIfTooBig; }
  139. set { _doMultiMaterialSplitAtlasesIfTooBig = value; }
  140. }
  141. [SerializeField]
  142. protected bool _doMultiMaterialSplitAtlasesIfOBUVs = true;
  143. public virtual bool doMultiMaterialSplitAtlasesIfOBUVs
  144. {
  145. get { return _doMultiMaterialSplitAtlasesIfOBUVs; }
  146. set { _doMultiMaterialSplitAtlasesIfOBUVs = value; }
  147. }
  148. [SerializeField]
  149. protected Material _resultMaterial;
  150. public virtual Material resultMaterial
  151. {
  152. get { return _resultMaterial; }
  153. set { _resultMaterial = value; }
  154. }
  155. [SerializeField]
  156. protected bool _considerNonTextureProperties = false;
  157. public bool considerNonTextureProperties
  158. {
  159. get { return _considerNonTextureProperties; }
  160. set { _considerNonTextureProperties = value; }
  161. }
  162. [SerializeField]
  163. protected bool _doSuggestTreatment = true;
  164. public bool doSuggestTreatment
  165. {
  166. get { return _doSuggestTreatment; }
  167. set { _doSuggestTreatment = value; }
  168. }
  169. private CreateAtlasesCoroutineResult _coroutineResult;
  170. public CreateAtlasesCoroutineResult CoroutineResult
  171. {
  172. get
  173. {
  174. return _coroutineResult;
  175. }
  176. }
  177. public MB_MultiMaterial[] resultMaterials = new MB_MultiMaterial[0];
  178. public List<GameObject> objsToMesh; //todo make this Renderer
  179. public override List<GameObject> GetObjectsToCombine()
  180. {
  181. if (objsToMesh == null) objsToMesh = new List<GameObject>();
  182. return objsToMesh;
  183. }
  184. public MB_AtlasesAndRects[] CreateAtlases()
  185. {
  186. return CreateAtlases(null, false, null);
  187. }
  188. public delegate void OnCombinedTexturesCoroutineSuccess();
  189. public delegate void OnCombinedTexturesCoroutineFail();
  190. public OnCombinedTexturesCoroutineSuccess onBuiltAtlasesSuccess;
  191. public OnCombinedTexturesCoroutineFail onBuiltAtlasesFail;
  192. public MB_AtlasesAndRects[] OnCombinedTexturesCoroutineAtlasesAndRects;
  193. /*
  194. bool _CreateAtlasesCoroutineSuccess = false;
  195. public bool CreateAtlasesCoroutineSuccess
  196. {
  197. get { return _CreateAtlasesCoroutineSuccess; }
  198. }
  199. bool _CreateAtlasesCoroutineIsFinished = false;
  200. public bool CreateAtlasesCoroutineIsFinished
  201. {
  202. get { return _CreateAtlasesCoroutineIsFinished; }
  203. }
  204. */
  205. public class CreateAtlasesCoroutineResult
  206. {
  207. public bool success = true;
  208. public bool isFinished = false;
  209. }
  210. public IEnumerator CreateAtlasesCoroutine(ProgressUpdateDelegate progressInfo, CreateAtlasesCoroutineResult coroutineResult, bool saveAtlasesAsAssets = false, MB2_EditorMethodsInterface editorMethods = null, float maxTimePerFrame = .01f)
  211. {
  212. MBVersionConcrete mbv = new MBVersionConcrete ();
  213. if (!MB3_TextureCombiner._RunCorutineWithoutPauseIsRunning &&( mbv.GetMajorVersion() < 5 ||(mbv.GetMajorVersion() == 5 && mbv.GetMinorVersion() < 3))){
  214. Debug.LogError("Running the texture combiner as a coroutine only works in Unity 5.3 and higher");
  215. coroutineResult.success = false;
  216. yield break;
  217. }
  218. this.OnCombinedTexturesCoroutineAtlasesAndRects = null;
  219. if (maxTimePerFrame <= 0f)
  220. {
  221. Debug.LogError("maxTimePerFrame must be a value greater than zero");
  222. coroutineResult.isFinished = true;
  223. yield break;
  224. }
  225. MB2_ValidationLevel vl = Application.isPlaying ? MB2_ValidationLevel.quick : MB2_ValidationLevel.robust;
  226. if (!DoCombinedValidate(this, MB_ObjsToCombineTypes.dontCare, null, vl))
  227. {
  228. coroutineResult.isFinished = true;
  229. yield break;
  230. }
  231. if (_doMultiMaterial && !_ValidateResultMaterials())
  232. {
  233. coroutineResult.isFinished = true;
  234. yield break;
  235. }
  236. else if (!_doMultiMaterial)
  237. {
  238. if (_resultMaterial == null)
  239. {
  240. Debug.LogError("Combined Material is null please create and assign a result material.");
  241. coroutineResult.isFinished = true;
  242. yield break;
  243. }
  244. Shader targShader = _resultMaterial.shader;
  245. for (int i = 0; i < objsToMesh.Count; i++)
  246. {
  247. Material[] ms = MB_Utility.GetGOMaterials(objsToMesh[i]);
  248. for (int j = 0; j < ms.Length; j++)
  249. {
  250. Material m = ms[j];
  251. if (m != null && m.shader != targShader)
  252. {
  253. Debug.LogWarning("Game object " + objsToMesh[i] + " does not use shader " + targShader + " it may not have the required textures. If not small solid color textures will be generated.");
  254. }
  255. }
  256. }
  257. }
  258. MB3_TextureCombiner combiner = CreateAndConfigureTextureCombiner();
  259. combiner.saveAtlasesAsAssets = saveAtlasesAsAssets;
  260. //initialize structure to store results
  261. int numResults = 1;
  262. if (_doMultiMaterial) numResults = resultMaterials.Length;
  263. OnCombinedTexturesCoroutineAtlasesAndRects = new MB_AtlasesAndRects[numResults];
  264. for (int i = 0; i < OnCombinedTexturesCoroutineAtlasesAndRects.Length; i++)
  265. {
  266. OnCombinedTexturesCoroutineAtlasesAndRects[i] = new MB_AtlasesAndRects();
  267. }
  268. //Do the material combining.
  269. for (int i = 0; i < OnCombinedTexturesCoroutineAtlasesAndRects.Length; i++)
  270. {
  271. Material resMatToPass = null;
  272. List<Material> sourceMats = null;
  273. if (_doMultiMaterial)
  274. {
  275. sourceMats = resultMaterials[i].sourceMaterials;
  276. resMatToPass = resultMaterials[i].combinedMaterial;
  277. combiner.fixOutOfBoundsUVs = resultMaterials[i].considerMeshUVs;
  278. }
  279. else
  280. {
  281. resMatToPass = _resultMaterial;
  282. }
  283. MB3_TextureCombiner.CombineTexturesIntoAtlasesCoroutineResult coroutineResult2 = new MB3_TextureCombiner.CombineTexturesIntoAtlasesCoroutineResult();
  284. yield return combiner.CombineTexturesIntoAtlasesCoroutine(progressInfo, OnCombinedTexturesCoroutineAtlasesAndRects[i], resMatToPass, objsToMesh, sourceMats, editorMethods, coroutineResult2, maxTimePerFrame);
  285. coroutineResult.success = coroutineResult2.success;
  286. if (!coroutineResult.success)
  287. {
  288. coroutineResult.isFinished = true;
  289. yield break;
  290. }
  291. }
  292. unpackMat2RectMap(textureBakeResults);
  293. //Save the results
  294. textureBakeResults.doMultiMaterial = _doMultiMaterial;
  295. //textureBakeResults.resultMaterial = _resultMaterial;
  296. if (_doMultiMaterial)
  297. {
  298. textureBakeResults.resultMaterials = resultMaterials;
  299. } else
  300. {
  301. MB_MultiMaterial[] resMats = new MB_MultiMaterial[1];
  302. resMats[0] = new MB_MultiMaterial();
  303. resMats[0].combinedMaterial = _resultMaterial;
  304. resMats[0].considerMeshUVs = _fixOutOfBoundsUVs;
  305. resMats[0].sourceMaterials = new List<Material>();
  306. for (int i = 0; i < textureBakeResults.materialsAndUVRects.Length; i++)
  307. {
  308. resMats[0].sourceMaterials.Add(textureBakeResults.materialsAndUVRects[i].material);
  309. }
  310. textureBakeResults.resultMaterials = resMats;
  311. }
  312. //textureBakeResults.fixOutOfBoundsUVs = combiner.fixOutOfBoundsUVs;
  313. //set the texture bake resultAtlasesAndRects on the Mesh Baker component if it exists
  314. MB3_MeshBakerCommon[] mb = GetComponentsInChildren<MB3_MeshBakerCommon>();
  315. for (int i = 0; i < mb.Length; i++)
  316. {
  317. mb[i].textureBakeResults = textureBakeResults;
  318. }
  319. if (LOG_LEVEL >= MB2_LogLevel.info) Debug.Log("Created Atlases");
  320. coroutineResult.isFinished = true;
  321. if (coroutineResult.success && onBuiltAtlasesSuccess != null)
  322. {
  323. onBuiltAtlasesSuccess();
  324. }
  325. if (!coroutineResult.success && onBuiltAtlasesFail != null)
  326. {
  327. onBuiltAtlasesFail();
  328. }
  329. }
  330. /// <summary>
  331. /// Creates the atlases.
  332. /// </summary>
  333. /// <returns>
  334. /// The atlases.
  335. /// </returns>
  336. /// <param name='progressInfo'>
  337. /// Progress info is a delegate function that displays a progress dialog. Can be null
  338. /// </param>
  339. /// <param name='saveAtlasesAsAssets'>
  340. /// if true atlases are saved as assets in the project folder. Othersise they are instances in memory
  341. /// </param>
  342. /// <param name='editorMethods'>
  343. /// Texture format tracker. Contains editor functionality such as save assets. Can be null.
  344. /// </param>
  345. public MB_AtlasesAndRects[] CreateAtlases(ProgressUpdateDelegate progressInfo, bool saveAtlasesAsAssets = false, MB2_EditorMethodsInterface editorMethods = null)
  346. {
  347. MB_AtlasesAndRects[] mAndAs = null;
  348. try
  349. {
  350. //mAndAs = _CreateAtlases(progressInfo, saveAtlasesAsAssets, editorMethods);
  351. _coroutineResult = new CreateAtlasesCoroutineResult();
  352. MB3_TextureCombiner.RunCorutineWithoutPause(CreateAtlasesCoroutine(progressInfo, _coroutineResult, saveAtlasesAsAssets, editorMethods, 1000f), 0);
  353. if (_coroutineResult.success && textureBakeResults != null) {
  354. mAndAs = this.OnCombinedTexturesCoroutineAtlasesAndRects;
  355. }
  356. }
  357. catch (Exception e)
  358. {
  359. Debug.LogError(e);
  360. }
  361. finally
  362. {
  363. if (saveAtlasesAsAssets)
  364. { //Atlases were saved to project so we don't need these ones
  365. if (mAndAs != null)
  366. {
  367. for (int j = 0; j < mAndAs.Length; j++)
  368. {
  369. MB_AtlasesAndRects mAndA = mAndAs[j];
  370. if (mAndA != null && mAndA.atlases != null)
  371. {
  372. for (int i = 0; i < mAndA.atlases.Length; i++)
  373. {
  374. if (mAndA.atlases[i] != null)
  375. {
  376. if (editorMethods != null) editorMethods.Destroy(mAndA.atlases[i]);
  377. else MB_Utility.Destroy(mAndA.atlases[i]);
  378. }
  379. }
  380. }
  381. }
  382. }
  383. }
  384. }
  385. return mAndAs;
  386. }
  387. void unpackMat2RectMap(MB2_TextureBakeResults tbr)
  388. {
  389. List<Material> ms = new List<Material>();
  390. List<MB_MaterialAndUVRect> mss = new List<MB_MaterialAndUVRect>();
  391. List<Rect> rs = new List<Rect>();
  392. for (int i = 0; i < OnCombinedTexturesCoroutineAtlasesAndRects.Length; i++)
  393. {
  394. MB_AtlasesAndRects newMesh = this.OnCombinedTexturesCoroutineAtlasesAndRects[i];
  395. List<MB_MaterialAndUVRect> map = newMesh.mat2rect_map;
  396. if (map != null)
  397. {
  398. for (int j = 0; j < map.Count; j++)
  399. {
  400. mss.Add(map[j]);
  401. ms.Add(map[j].material);
  402. rs.Add(map[j].atlasRect);
  403. }
  404. }
  405. }
  406. tbr.version = MB2_TextureBakeResults.VERSION;
  407. tbr.materialsAndUVRects = mss.ToArray();
  408. }
  409. public MB3_TextureCombiner CreateAndConfigureTextureCombiner()
  410. {
  411. MB3_TextureCombiner combiner = new MB3_TextureCombiner();
  412. combiner.LOG_LEVEL = LOG_LEVEL;
  413. combiner.atlasPadding = _atlasPadding;
  414. combiner.maxAtlasSize = _maxAtlasSize;
  415. combiner.maxAtlasHeightOverride = _maxAtlasHeightOverride;
  416. combiner.maxAtlasWidthOverride = _maxAtlasWidthOverride;
  417. combiner.useMaxAtlasHeightOverride = _useMaxAtlasHeightOverride;
  418. combiner.useMaxAtlasWidthOverride = _useMaxAtlasWidthOverride;
  419. combiner.customShaderPropNames = _customShaderProperties;
  420. combiner.fixOutOfBoundsUVs = _fixOutOfBoundsUVs;
  421. combiner.maxTilingBakeSize = _maxTilingBakeSize;
  422. combiner.packingAlgorithm = _packingAlgorithm;
  423. combiner.meshBakerTexturePackerForcePowerOfTwo = _meshBakerTexturePackerForcePowerOfTwo;
  424. combiner.resizePowerOfTwoTextures = _resizePowerOfTwoTextures;
  425. combiner.considerNonTextureProperties = _considerNonTextureProperties;
  426. return combiner;
  427. }
  428. public static void ConfigureNewMaterialToMatchOld(Material newMat, Material original)
  429. {
  430. if (original == null)
  431. {
  432. Debug.LogWarning("Original material is null, could not copy properties to " + newMat + ". Setting shader to " + newMat.shader);
  433. return;
  434. }
  435. newMat.shader = original.shader;
  436. newMat.CopyPropertiesFromMaterial(original);
  437. ShaderTextureProperty[] texPropertyNames = MB3_TextureCombinerPipeline.shaderTexPropertyNames;
  438. for (int j = 0; j < texPropertyNames.Length; j++)
  439. {
  440. Vector2 scale = Vector2.one;
  441. Vector2 offset = Vector2.zero;
  442. if (newMat.HasProperty(texPropertyNames[j].name))
  443. {
  444. newMat.SetTextureOffset(texPropertyNames[j].name, offset);
  445. newMat.SetTextureScale(texPropertyNames[j].name, scale);
  446. }
  447. }
  448. }
  449. string PrintSet(HashSet<Material> s)
  450. {
  451. StringBuilder sb = new StringBuilder();
  452. foreach (Material m in s)
  453. {
  454. sb.Append(m + ",");
  455. }
  456. return sb.ToString();
  457. }
  458. bool _ValidateResultMaterials()
  459. {
  460. HashSet<Material> allMatsOnObjs = new HashSet<Material>();
  461. for (int i = 0; i < objsToMesh.Count; i++)
  462. {
  463. if (objsToMesh[i] != null)
  464. {
  465. Material[] ms = MB_Utility.GetGOMaterials(objsToMesh[i]);
  466. for (int j = 0; j < ms.Length; j++)
  467. {
  468. if (ms[j] != null) allMatsOnObjs.Add(ms[j]);
  469. }
  470. }
  471. }
  472. HashSet<Material> allMatsInMapping = new HashSet<Material>();
  473. for (int i = 0; i < resultMaterials.Length; i++)
  474. {
  475. for (int j = i+1; j < resultMaterials.Length; j++)
  476. {
  477. if (resultMaterials[i].combinedMaterial == resultMaterials[j].combinedMaterial)
  478. {
  479. Debug.LogError(String.Format("Source To Combined Mapping: Submesh {0} and Submesh {1} use the same combined material. These should be different", i, j));
  480. return false;
  481. }
  482. }
  483. MB_MultiMaterial mm = resultMaterials[i];
  484. if (mm.combinedMaterial == null)
  485. {
  486. Debug.LogError("Combined Material is null please create and assign a result material.");
  487. return false;
  488. }
  489. Shader targShader = mm.combinedMaterial.shader;
  490. for (int j = 0; j < mm.sourceMaterials.Count; j++)
  491. {
  492. if (mm.sourceMaterials[j] == null)
  493. {
  494. Debug.LogError("There are null entries in the list of Source Materials");
  495. return false;
  496. }
  497. if (targShader != mm.sourceMaterials[j].shader)
  498. {
  499. Debug.LogWarning("Source material " + mm.sourceMaterials[j] + " does not use shader " + targShader + " it may not have the required textures. If not empty textures will be generated.");
  500. }
  501. if (allMatsInMapping.Contains(mm.sourceMaterials[j]))
  502. {
  503. Debug.LogError("A Material " + mm.sourceMaterials[j] + " appears more than once in the list of source materials in the source material to combined mapping. Each source material must be unique.");
  504. return false;
  505. }
  506. allMatsInMapping.Add(mm.sourceMaterials[j]);
  507. }
  508. }
  509. if (allMatsOnObjs.IsProperSubsetOf(allMatsInMapping))
  510. {
  511. allMatsInMapping.ExceptWith(allMatsOnObjs);
  512. Debug.LogWarning("There are materials in the mapping that are not used on your source objects: " + PrintSet(allMatsInMapping));
  513. }
  514. if (resultMaterials != null && resultMaterials.Length > 0 && allMatsInMapping.IsProperSubsetOf(allMatsOnObjs))
  515. {
  516. allMatsOnObjs.ExceptWith(allMatsInMapping);
  517. Debug.LogError("There are materials on the objects to combine that are not in the mapping: " + PrintSet(allMatsOnObjs));
  518. return false;
  519. }
  520. return true;
  521. }
  522. }