MB3_AtlasPackerRenderTexture.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410
  1. using UnityEngine;
  2. using System.Collections;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.IO;
  6. using DigitalOpus.MB.Core;
  7. public class MB_TextureCombinerRenderTexture{
  8. public MB2_LogLevel LOG_LEVEL = MB2_LogLevel.info;
  9. Material mat; //container for the shader that we will use to render the texture
  10. RenderTexture _destinationTexture;
  11. Camera myCamera;
  12. int _padding;
  13. bool _isNormalMap;
  14. bool _fixOutOfBoundsUVs;
  15. //bool _considerNonTextureProperties;
  16. //only want to render once, not every frame
  17. bool _doRenderAtlas = false;
  18. Rect[] rs;
  19. List<MB_TexSet> textureSets;
  20. int indexOfTexSetToRender;
  21. ShaderTextureProperty _texPropertyName;
  22. MB3_TextureCombinerNonTextureProperties _resultMaterialTextureBlender;
  23. Texture2D targTex;
  24. public Texture2D DoRenderAtlas(GameObject gameObject, int width, int height, int padding, Rect[] rss, List<MB_TexSet> textureSetss, int indexOfTexSetToRenders, ShaderTextureProperty texPropertyname, MB3_TextureCombinerNonTextureProperties resultMaterialTextureBlender, bool isNormalMap, bool fixOutOfBoundsUVs, bool considerNonTextureProperties, MB3_TextureCombiner texCombiner, MB2_LogLevel LOG_LEV){
  25. LOG_LEVEL = LOG_LEV;
  26. textureSets = textureSetss;
  27. indexOfTexSetToRender = indexOfTexSetToRenders;
  28. _texPropertyName = texPropertyname;
  29. _padding = padding;
  30. _isNormalMap = isNormalMap;
  31. _fixOutOfBoundsUVs = fixOutOfBoundsUVs;
  32. //_considerNonTextureProperties = considerNonTextureProperties;
  33. _resultMaterialTextureBlender = resultMaterialTextureBlender;
  34. rs = rss;
  35. Shader s;
  36. if (_isNormalMap){
  37. s = Shader.Find ("MeshBaker/NormalMapShader");
  38. } else {
  39. s = Shader.Find ("MeshBaker/AlbedoShader");
  40. }
  41. if (s == null){
  42. Debug.LogError ("Could not find shader for RenderTexture. Try reimporting mesh baker");
  43. return null;
  44. }
  45. mat = new Material(s);
  46. _destinationTexture = new RenderTexture(width,height,24,RenderTextureFormat.ARGB32);
  47. _destinationTexture.filterMode = FilterMode.Point;
  48. myCamera = gameObject.GetComponent<Camera>();
  49. myCamera.orthographic = true;
  50. myCamera.orthographicSize = height >> 1;
  51. myCamera.aspect = ((float) width) / height;
  52. myCamera.targetTexture = _destinationTexture;
  53. myCamera.clearFlags = CameraClearFlags.Color;
  54. Transform camTransform = myCamera.GetComponent<Transform>();
  55. camTransform.localPosition = new Vector3(width/2.0f, height/2f, 3);
  56. camTransform.localRotation = Quaternion.Euler(0, 180, 180);
  57. _doRenderAtlas = true;
  58. if (LOG_LEVEL >= MB2_LogLevel.debug) Debug.Log(string.Format ("Begin Camera.Render destTex w={0} h={1} camPos={2} camSize={3} camAspect={4}", width, height, camTransform.localPosition, myCamera.orthographicSize, myCamera.aspect.ToString("f5")));
  59. //This triggers the OnRenderObject callback
  60. myCamera.Render();
  61. _doRenderAtlas = false;
  62. MB_Utility.Destroy(mat);
  63. MB_Utility.Destroy(_destinationTexture);
  64. if (LOG_LEVEL >= MB2_LogLevel.debug) Debug.Log ("Finished Camera.Render ");
  65. Texture2D tempTex = targTex;
  66. targTex = null;
  67. return tempTex;
  68. }
  69. public void OnRenderObject(){
  70. if (_doRenderAtlas){
  71. //assett rs must be same length as textureSets;
  72. System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
  73. sw.Start ();
  74. bool yIsFlipped = YisFlipped();
  75. for (int i = 0; i < rs.Length; i++){
  76. MeshBakerMaterialTexture texInfo = textureSets[i].ts[indexOfTexSetToRender];
  77. Texture2D tx = texInfo.GetTexture2D();
  78. if (LOG_LEVEL >= MB2_LogLevel.trace && tx != null) {
  79. Debug.Log("Added " + tx + " to atlas w=" + tx.width + " h=" + tx.height + " offset=" + texInfo.matTilingRect.min + " scale=" + texInfo.matTilingRect.size + " rect=" + rs[i] + " padding=" + _padding);
  80. //_printTexture(tx);
  81. }
  82. CopyScaledAndTiledToAtlas(textureSets[i], texInfo, textureSets[i].obUVoffset, textureSets[i].obUVscale, rs[i],_texPropertyName,_resultMaterialTextureBlender, yIsFlipped);
  83. }
  84. sw.Stop();
  85. sw.Start();
  86. if (LOG_LEVEL >= MB2_LogLevel.debug) Debug.Log ("Total time for Graphics.DrawTexture calls " + (sw.ElapsedMilliseconds).ToString("f5"));
  87. if (LOG_LEVEL >= MB2_LogLevel.debug) Debug.Log ("Copying RenderTexture to Texture2D. destW" + _destinationTexture.width + " destH" + _destinationTexture.height );
  88. //Convert the RenderTexture to a Texture2D
  89. Texture2D tempTexture;
  90. tempTexture = new Texture2D(_destinationTexture.width, _destinationTexture.height, TextureFormat.ARGB32, true);
  91. RenderTexture oldRT = RenderTexture.active;
  92. RenderTexture.active = _destinationTexture;
  93. int xblocks = Mathf.CeilToInt(((float) _destinationTexture.width) / 512);
  94. int yblocks = Mathf.CeilToInt(((float) _destinationTexture.height) / 512);
  95. if (xblocks == 0 || yblocks == 0)
  96. {
  97. if (LOG_LEVEL >= MB2_LogLevel.trace) Debug.Log ("Copying all in one shot");
  98. tempTexture.ReadPixels(new Rect(0, 0, _destinationTexture.width, _destinationTexture.height), 0, 0, true);
  99. } else {
  100. if (yIsFlipped == false)
  101. {
  102. for (int x = 0; x < xblocks; x++)
  103. {
  104. for (int y = 0; y < yblocks; y++)
  105. {
  106. int xx = x * 512;
  107. int yy = y * 512;
  108. Rect r = new Rect(xx, yy, 512, 512);
  109. tempTexture.ReadPixels(r, x * 512, y * 512, true);
  110. }
  111. }
  112. }
  113. else
  114. {
  115. if (LOG_LEVEL >= MB2_LogLevel.trace) Debug.Log ("Not OpenGL copying blocks");
  116. for (int x = 0; x < xblocks; x++)
  117. {
  118. for (int y = 0; y < yblocks; y++)
  119. {
  120. int xx = x * 512;
  121. int yy = _destinationTexture.height - 512 - y * 512;
  122. Rect r = new Rect(xx, yy, 512, 512);
  123. tempTexture.ReadPixels(r, x * 512, y * 512, true);
  124. }
  125. }
  126. }
  127. }
  128. RenderTexture.active = oldRT;
  129. tempTexture.Apply ();
  130. if (LOG_LEVEL >= MB2_LogLevel.trace)
  131. {
  132. Debug.Log("TempTexture ");
  133. if (tempTexture.height <= 16 && tempTexture.width <= 16) _printTexture(tempTexture);
  134. }
  135. myCamera.targetTexture = null;
  136. RenderTexture.active = null;
  137. targTex = tempTexture;
  138. if (LOG_LEVEL >= MB2_LogLevel.debug) Debug.Log ("Total time to copy RenderTexture to Texture2D " + (sw.ElapsedMilliseconds).ToString("f5"));
  139. }
  140. }
  141. /*
  142. Unity uses a non-standard format for storing normals for some platforms. Imagine the standard format is English, Unity's is French
  143. When the normal-map checkbox is ticked on the asset importer the normal map is translated into french. When we build the normal atlas
  144. we are reading the french. When we save and click the normal map tickbox we are translating french -> french. A double transladion that
  145. 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
  146. can do its thing properly.
  147. */
  148. Color32 ConvertNormalFormatFromUnity_ToStandard(Color32 c) {
  149. Vector3 n = Vector3.zero;
  150. n.x = c.a * 2f - 1f;
  151. n.y = c.g * 2f - 1f;
  152. n.z = Mathf.Sqrt(1 - n.x * n.x - n.y * n.y);
  153. //now repack in the regular format
  154. Color32 cc = new Color32();
  155. cc.a = 1;
  156. cc.r = (byte) ((n.x + 1f) * .5f);
  157. cc.g = (byte) ((n.y + 1f) * .5f);
  158. cc.b = (byte) ((n.z + 1f) * .5f);
  159. return cc;
  160. }
  161. public bool YisFlipped() {
  162. string graphicsDeviceVersion = SystemInfo.graphicsDeviceVersion.ToLower();
  163. bool flipY;
  164. if (!MBVersion.GraphicsUVStartsAtTop())
  165. {
  166. flipY = false;
  167. } else {
  168. // "opengl es, direct3d"
  169. flipY = true;
  170. }
  171. if (LOG_LEVEL == MB2_LogLevel.debug) Debug.Log("Graphics device version is: " + graphicsDeviceVersion + " flipY:" + flipY);
  172. return flipY;
  173. }
  174. private void CopyScaledAndTiledToAtlas(MB_TexSet texSet, MeshBakerMaterialTexture source, Vector2 obUVoffset, Vector2 obUVscale, Rect rec, ShaderTextureProperty texturePropertyName, MB3_TextureCombinerNonTextureProperties resultMatTexBlender, bool yIsFlipped){
  175. Rect r = rec;
  176. myCamera.backgroundColor = resultMatTexBlender.GetColorForTemporaryTexture(texSet.matsAndGOs.mats[0].mat, texturePropertyName);
  177. //yIsFlipped = true;
  178. //if (yIsFlipped)
  179. //{
  180. //}
  181. r.y = 1f - (r.y + r.height); // DrawTexture uses topLeft 0,0, Texture2D uses bottomLeft 0,0
  182. r.x *= _destinationTexture.width;
  183. r.y *= _destinationTexture.height;
  184. r.width *= _destinationTexture.width;
  185. r.height *= _destinationTexture.height;
  186. Rect rPadded = r;
  187. rPadded.x -= _padding;
  188. rPadded.y -= _padding;
  189. rPadded.width += _padding * 2;
  190. rPadded.height += _padding * 2;
  191. Rect targPr = new Rect();
  192. Rect srcPrTex = texSet.ts[indexOfTexSetToRender].GetEncapsulatingSamplingRect().GetRect();
  193. if (!_fixOutOfBoundsUVs) {
  194. Debug.Assert(source.matTilingRect.GetRect() == texSet.ts[indexOfTexSetToRender].GetEncapsulatingSamplingRect().GetRect());
  195. }
  196. Texture2D tex = source.GetTexture2D();
  197. /*
  198. if (_considerNonTextureProperties && resultMatTexBlender != null)
  199. {
  200. if (LOG_LEVEL >= MB2_LogLevel.trace) Debug.Log(string.Format("Blending texture {0} mat {1} with non-texture properties using TextureBlender {2}", tex.name, texSet.mats[0].mat, resultMatTexBlender));
  201. resultMatTexBlender.OnBeforeTintTexture(texSet.mats[0].mat, texturePropertyName.name);
  202. //combine the tintColor with the texture
  203. tex = combiner._createTextureCopy(tex);
  204. for (int i = 0; i < tex.height; i++)
  205. {
  206. Color[] cs = tex.GetPixels(0, i, tex.width, 1);
  207. for (int j = 0; j < cs.Length; j++)
  208. {
  209. cs[j] = resultMatTexBlender.OnBlendTexturePixel(texturePropertyName.name, cs[j]);
  210. }
  211. tex.SetPixels(0, i, tex.width, 1, cs);
  212. }
  213. tex.Apply();
  214. }
  215. */
  216. //main texture
  217. TextureWrapMode oldTexWrapMode = tex.wrapMode;
  218. if (srcPrTex.width == 1f && srcPrTex.height == 1f && srcPrTex.x == 0f && srcPrTex.y == 0f){
  219. //fixes bug where there is a dark line at the edge of the texture
  220. tex.wrapMode = TextureWrapMode.Clamp;
  221. } else {
  222. tex.wrapMode = TextureWrapMode.Repeat;
  223. }
  224. if (LOG_LEVEL >= MB2_LogLevel.trace) Debug.Log ("DrawTexture tex=" + tex.name + " destRect=" + r + " srcRect=" + srcPrTex + " Mat=" + mat);
  225. //fill the padding first
  226. Rect srcPr = new Rect();
  227. //top margin
  228. srcPr.x = srcPrTex.x;
  229. srcPr.y = srcPrTex.y + 1 - 1f / tex.height;
  230. srcPr.width = srcPrTex.width;
  231. srcPr.height = 1f / tex.height;
  232. targPr.x = r.x;
  233. targPr.y = rPadded.y;
  234. targPr.width = r.width;
  235. targPr.height = _padding;
  236. RenderTexture oldRT = RenderTexture.active;
  237. RenderTexture.active = _destinationTexture;
  238. Graphics.DrawTexture(targPr, tex, srcPr, 0, 0, 0, 0, mat);
  239. //bot margin
  240. srcPr.x = srcPrTex.x;
  241. srcPr.y = srcPrTex.y;
  242. srcPr.width = srcPrTex.width;
  243. srcPr.height = 1f / tex.height;
  244. targPr.x = r.x;
  245. targPr.y = r.y + r.height;
  246. targPr.width = r.width;
  247. targPr.height = _padding;
  248. Graphics.DrawTexture(targPr, tex, srcPr, 0, 0, 0, 0, mat);
  249. //left margin
  250. srcPr.x = srcPrTex.x;
  251. srcPr.y = srcPrTex.y;
  252. srcPr.width = 1f / tex.width;
  253. srcPr.height = srcPrTex.height;
  254. targPr.x = rPadded.x;
  255. targPr.y = r.y;
  256. targPr.width = _padding;
  257. targPr.height = r.height;
  258. Graphics.DrawTexture(targPr, tex, srcPr, 0, 0, 0, 0, mat);
  259. //right margin
  260. srcPr.x = srcPrTex.x + 1f - 1f / tex.width;
  261. srcPr.y = srcPrTex.y;
  262. srcPr.width = 1f / tex.width;
  263. srcPr.height = srcPrTex.height;
  264. targPr.x = r.x + r.width;
  265. targPr.y = r.y;
  266. targPr.width = _padding;
  267. targPr.height = r.height;
  268. Graphics.DrawTexture(targPr, tex, srcPr, 0, 0, 0, 0, mat);
  269. //top left corner
  270. srcPr.x = srcPrTex.x;
  271. srcPr.y = srcPrTex.y + 1 - 1f / tex.height ;
  272. srcPr.width = 1f / tex.width;
  273. srcPr.height = 1f / tex.height;
  274. targPr.x = rPadded.x;
  275. targPr.y = rPadded.y;
  276. targPr.width = _padding;
  277. targPr.height = _padding;
  278. Graphics.DrawTexture(targPr, tex, srcPr, 0, 0, 0, 0, mat);
  279. //top right corner
  280. srcPr.x = srcPrTex.x + 1f - 1f / tex.width;
  281. srcPr.y = srcPrTex.y + 1 - 1f / tex.height ;
  282. srcPr.width = 1f / tex.width;
  283. srcPr.height = 1f / tex.height;
  284. targPr.x = r.x + r.width;
  285. targPr.y = rPadded.y;
  286. targPr.width = _padding;
  287. targPr.height = _padding;
  288. Graphics.DrawTexture(targPr, tex, srcPr, 0, 0, 0, 0, mat);
  289. //bot left corner
  290. srcPr.x = srcPrTex.x;
  291. srcPr.y = srcPrTex.y;
  292. srcPr.width = 1f / tex.width;
  293. srcPr.height = 1f / tex.height;
  294. targPr.x = rPadded.x;
  295. targPr.y = r.y + r.height;
  296. targPr.width = _padding;
  297. targPr.height = _padding;
  298. Graphics.DrawTexture(targPr, tex, srcPr, 0, 0, 0, 0, mat);
  299. //bot right corner
  300. srcPr.x = srcPrTex.x + 1f - 1f / tex.width;
  301. srcPr.y = srcPrTex.y ;
  302. srcPr.width = 1f / tex.width;
  303. srcPr.height = 1f / tex.height;
  304. targPr.x = r.x + r.width;
  305. targPr.y = r.y + r.height;
  306. targPr.width = _padding;
  307. targPr.height = _padding;
  308. Graphics.DrawTexture(targPr, tex, srcPr, 0, 0, 0, 0, mat);
  309. //now the texture
  310. Graphics.DrawTexture(r, tex, srcPrTex, 0, 0, 0, 0, mat);
  311. RenderTexture.active = oldRT;
  312. tex.wrapMode = oldTexWrapMode;
  313. }
  314. void _printTexture(Texture2D t) {
  315. if (t.width * t.height > 100)
  316. {
  317. Debug.Log("Not printing texture too large.");
  318. return;
  319. }
  320. try {
  321. Color32[] cols = t.GetPixels32();
  322. string s = "";
  323. for (int i = 0; i < t.height; i++) {
  324. for (int j = 0; j < t.width; j++) {
  325. s += cols[i * t.width + j] + ", ";
  326. }
  327. s += "\n";
  328. }
  329. Debug.Log(s);
  330. } catch (Exception e)
  331. {
  332. Debug.Log("Could not print texture. texture may not be readable." + e.ToString());
  333. }
  334. }
  335. }
  336. [ExecuteInEditMode]
  337. public class MB3_AtlasPackerRenderTexture : MonoBehaviour {
  338. MB_TextureCombinerRenderTexture fastRenderer;
  339. bool _doRenderAtlas = false;
  340. public int width;
  341. public int height;
  342. public int padding;
  343. public bool isNormalMap;
  344. public bool fixOutOfBoundsUVs;
  345. public bool considerNonTextureProperties;
  346. public MB3_TextureCombinerNonTextureProperties resultMaterialTextureBlender;
  347. public Rect[] rects;
  348. public Texture2D tex1;
  349. public List<MB_TexSet> textureSets;
  350. public int indexOfTexSetToRender;
  351. public ShaderTextureProperty texPropertyName;
  352. public MB2_LogLevel LOG_LEVEL = MB2_LogLevel.info;
  353. public Texture2D testTex;
  354. public Material testMat;
  355. public Texture2D OnRenderAtlas(MB3_TextureCombiner combiner){
  356. fastRenderer = new MB_TextureCombinerRenderTexture();
  357. _doRenderAtlas = true;
  358. Texture2D atlas = fastRenderer.DoRenderAtlas(this.gameObject,width,height,padding,rects,textureSets,indexOfTexSetToRender, texPropertyName, resultMaterialTextureBlender, isNormalMap, fixOutOfBoundsUVs, considerNonTextureProperties, combiner, LOG_LEVEL);
  359. _doRenderAtlas = false;
  360. return atlas;
  361. }
  362. void OnRenderObject(){
  363. if (_doRenderAtlas){
  364. fastRenderer.OnRenderObject();
  365. _doRenderAtlas = false;
  366. }
  367. }
  368. }