MeshCreator.cs 52 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271
  1. /****************************************************************************
  2. Copyright (c) 2013, Jonathan Cecil and UCLA Game Lab
  3. All rights reserved.
  4. Redistribution and use in source and binary forms, with or without
  5. modification, are permitted provided that the following conditions are met:
  6. 1. Redistributions of source code must retain the above copyright notice, this
  7. list of conditions and the following disclaimer.
  8. 2. Redistributions in binary form must reproduce the above copyright notice,
  9. this list of conditions and the following disclaimer in the documentation
  10. and/or other materials provided with the distribution.
  11. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  12. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  13. WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  14. DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
  15. ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  16. (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  17. LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  18. ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  19. (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  20. SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  21. *****************************************************************************/
  22. using UnityEngine;
  23. using UnityEditor;
  24. using System;
  25. using System.IO;
  26. using System.Collections;
  27. using System.Collections.Generic;
  28. public class MeshCreator : UnityEngine.Object {
  29. public static float versionNumber = 0.7f;
  30. public static void UpdateMesh(GameObject gameObject)
  31. {
  32. MeshCreatorData mcd = gameObject.GetComponent(typeof(MeshCreatorData)) as MeshCreatorData;
  33. // unity should prevent this from happening to the inspector, but just in case.....
  34. if (mcd == null) {
  35. Debug.LogError("MeshCreator Error: selected object does not have a MeshCreatorData component. Select an object with a MeshCreatorData component to update.");
  36. return;
  37. }
  38. // add a TextureImporter object here to check whether texture is readable
  39. // set it to readable if necessary
  40. if (mcd.outlineTexture == null) {
  41. Debug.LogError("MeshCreator: no texture found. Make sure to have a texture selected before updating mesh.");
  42. return;
  43. }
  44. // if this is a new save, generate a unique idNumber
  45. if ( mcd.idNumber == "" )
  46. {
  47. mcd.idNumber = MeshCreator.GenerateId();
  48. // Debug.Log(mcd.gameObject.name + "MeshCreator: set new mesh id number to " + mcd.idNumber);
  49. }
  50. // check the id number, if it is used in another scene object
  51. // generate a new id number
  52. while (MeshCreator.IdExistsInScene(mcd))
  53. {
  54. mcd.idNumber = MeshCreator.GenerateId();
  55. }
  56. // check for scene folder
  57. string[] sceneNames = EditorApplication.currentScene.Split('/');
  58. if (sceneNames.Length == 1 && sceneNames[0] == "")
  59. {
  60. Debug.LogError("MeshCreator Error: please save the scene before creating a mesh.");
  61. DestroyImmediate(mcd.gameObject);
  62. return;
  63. }
  64. string sceneName = sceneNames[sceneNames.Length-1];
  65. string folderName = sceneName.Substring(0, sceneName.Length - 6);
  66. string folderPath = "Assets/UCLAGameLab/Meshes/" + folderName; // TODO: this should be a preference
  67. if (!Directory.Exists("Assets/UCLAGameLab/Meshes"))
  68. {
  69. if (!Directory.Exists("Assets/UCLAGameLab"))
  70. {
  71. Debug.LogError("MeshCreator: UCLAGameLab folder is missing from your project, please reinstall Mesh Creator.");
  72. return;
  73. }
  74. AssetDatabase.CreateFolder("Assets/UCLAGameLab", "Meshes");
  75. Debug.Log("MeshCreator: making new Meshes folder at Assets/Meshes");
  76. }
  77. if (!Directory.Exists(folderPath))
  78. {
  79. Debug.Log("MeshCreator: making new folder in Meshes folder at " + folderPath);
  80. AssetDatabase.CreateFolder("Assets/UCLAGameLab/Meshes", folderName );
  81. }
  82. string saveName = folderName + "/" + mcd.gameObject.name + "." + mcd.idNumber ;
  83. // stash the rotation value, set back to identity, then switch back later
  84. Quaternion oldRotation = mcd.gameObject.transform.rotation;
  85. mcd.gameObject.transform.rotation = Quaternion.identity;
  86. // stash the scale value, set back to one, then switch back later
  87. Vector3 oldScale = mcd.gameObject.transform.localScale;
  88. mcd.gameObject.transform.localScale = Vector3.one;
  89. // transform the object if needed to account for the new pivot
  90. if (mcd.pivotHeightOffset != mcd.lastPivotOffset.x || mcd.pivotWidthOffset != mcd.lastPivotOffset.y || mcd.pivotWidthOffset != mcd.lastPivotOffset.z ) {
  91. mcd.gameObject.transform.localPosition -= mcd.lastPivotOffset;
  92. mcd.lastPivotOffset = new Vector3(mcd.pivotWidthOffset, mcd.pivotHeightOffset, mcd.pivotDepthOffset);
  93. mcd.gameObject.transform.localPosition += mcd.lastPivotOffset;
  94. }
  95. //
  96. // start mesh renderer setup section
  97. //
  98. // mesh for rendering the object
  99. // will either be flat or full mesh
  100. Mesh msh = new Mesh();
  101. // collider for mesh, if used
  102. Mesh collidermesh = new Mesh();
  103. if (mcd.uvWrapMesh) {
  104. // Set up game object with mesh;
  105. AssignMesh(gameObject, ref msh);
  106. collidermesh = msh;
  107. }
  108. else {
  109. AssignPlaneMesh(gameObject, ref msh);
  110. // if needed, create the 3d mesh collider
  111. if (mcd.generateCollider && !mcd.usePrimitiveCollider && !mcd.useAABBCollider)
  112. AssignMesh(gameObject, ref collidermesh);
  113. }
  114. MeshRenderer mr = (MeshRenderer) mcd.gameObject.GetComponent("MeshRenderer");
  115. if (mr == null) {
  116. //Debug.Log("MeshCreator Warning: no mesh renderer found on update object, adding one.");
  117. mcd.gameObject.AddComponent(typeof(MeshRenderer));
  118. }
  119. // update the front material via renderer
  120. Material meshmat;
  121. string materialNameLocation = "Assets/UCLAGameLab/Materials/"+mcd.outlineTexture.name+".material.mat";
  122. string transparentMaterialNameLocation = "Assets/UCLAGameLab/Materials/"+mcd.outlineTexture.name+".trans.material.mat";
  123. string baseMaterialNameLocation = "Assets/UCLAGameLab/Materials/baseMaterial.mat";
  124. string transparentBaseMaterialNameLocation = "Assets/UCLAGameLab/Materials/baseTransparentMaterial.mat";
  125. if (mcd.useAutoGeneratedMaterial) {
  126. // if using uvWrapMesh, use regular material
  127. if (mcd.uvWrapMesh) {
  128. meshmat = (Material) AssetDatabase.LoadAssetAtPath(materialNameLocation, typeof(Material));
  129. if (meshmat == null) {
  130. meshmat = CopyTexture(baseMaterialNameLocation, materialNameLocation, mcd.outlineTexture);
  131. }
  132. mcd.gameObject.GetComponent<Renderer>().sharedMaterial = meshmat;
  133. }
  134. else { // use a transparent material
  135. meshmat = (Material) AssetDatabase.LoadAssetAtPath(transparentMaterialNameLocation, typeof(Material));
  136. if (meshmat == null) {
  137. meshmat = CopyTexture(transparentBaseMaterialNameLocation, transparentMaterialNameLocation, mcd.outlineTexture);
  138. }
  139. mcd.gameObject.GetComponent<Renderer>().sharedMaterial = meshmat;
  140. }
  141. }
  142. else {
  143. mcd.gameObject.GetComponent<Renderer>().sharedMaterial = mcd.frontMaterial;
  144. }
  145. MeshFilter mf = (MeshFilter) mcd.gameObject.GetComponent("MeshFilter");
  146. if (mf == null) {
  147. //Debug.LogWarning("MeshCreator Warning: no mesh filter found on update object, adding one.");
  148. mf= mcd.gameObject.AddComponent(typeof(MeshFilter)) as MeshFilter;
  149. }
  150. mf.sharedMesh = msh;
  151. // save the main mesh
  152. string meshName = "Assets/UCLAGameLab/Meshes/" + saveName + ".asset";
  153. AssetDatabase.CreateAsset(msh, meshName);
  154. // make the side edges
  155. if (!mcd.uvWrapMesh && mcd.createEdges)
  156. {
  157. Mesh edgemesh = new Mesh();
  158. MeshCreator.AssignEdgeMesh(gameObject, ref edgemesh);
  159. // remove the old backside mesh game object
  160. string edgeName = mcd.gameObject.name + ".edge";
  161. ArrayList destroyObject = new ArrayList();
  162. foreach (Transform child in mcd.gameObject.transform) {
  163. if (child.name == edgeName) {
  164. MeshFilter emf = (MeshFilter) child.gameObject.GetComponent("MeshFilter");
  165. if (emf != null) {
  166. Mesh ems = (Mesh) emf.sharedMesh;
  167. if (ems != null) {
  168. //DestroyImmediate(ems, true);
  169. }
  170. }
  171. destroyObject.Add(child);
  172. }
  173. }
  174. while (destroyObject.Count > 0) {
  175. Transform child = (Transform) destroyObject[0];
  176. destroyObject.Remove(child);
  177. DestroyImmediate(child.gameObject);
  178. }
  179. // create a new game object to attach the backside plane
  180. GameObject edgeObject = new GameObject();
  181. edgeObject.transform.parent = mcd.gameObject.transform;
  182. edgeObject.transform.localPosition = Vector3.zero;
  183. edgeObject.transform.rotation = Quaternion.identity;
  184. edgeObject.name = edgeName;
  185. MeshFilter edgemf = (MeshFilter) edgeObject.AddComponent(typeof(MeshFilter)) as MeshFilter;
  186. edgemf.sharedMesh = edgemesh;
  187. // save the mesh in the Assets folder
  188. string edgeMeshName = "Assets/UCLAGameLab/Meshes/" + saveName + ".Edge" + ".asset";
  189. AssetDatabase.CreateAsset(edgemesh, edgeMeshName);
  190. MeshRenderer edgemr = edgeObject.AddComponent(typeof(MeshRenderer)) as MeshRenderer;
  191. // for side meshes use the opaque material
  192. Material edgematerial = (Material)AssetDatabase.LoadAssetAtPath(materialNameLocation, typeof(Material));
  193. if (edgematerial == null)
  194. {
  195. edgematerial = CopyTexture(baseMaterialNameLocation, materialNameLocation, mcd.outlineTexture);
  196. }
  197. edgemr.GetComponent<Renderer>().sharedMaterial = edgematerial;
  198. }
  199. else // destroy the old edge objects because they're not needed
  200. {
  201. string edgeName = mcd.gameObject.name + ".edge";
  202. ArrayList destroyObject = new ArrayList();
  203. foreach (Transform child in mcd.gameObject.transform) {
  204. if (child.name == edgeName) {
  205. destroyObject.Add(child);
  206. MeshFilter emf = (MeshFilter) child.gameObject.GetComponent("MeshFilter");
  207. if (emf != null) {
  208. Mesh ems = (Mesh) emf.sharedMesh;
  209. if (ems != null) {
  210. //DestroyImmediate(ems, true);
  211. }
  212. }
  213. }
  214. }
  215. while (destroyObject.Count > 0) {
  216. Transform child = (Transform) destroyObject[0];
  217. destroyObject.Remove(child);
  218. DestroyImmediate(child.gameObject);
  219. }
  220. }
  221. // make the backside plane
  222. if (!mcd.uvWrapMesh && mcd.createBacksidePlane) {
  223. Mesh backmesh = new Mesh();
  224. AssignPlaneMeshBackside(gameObject, ref backmesh);
  225. // remove the old backside mesh game object
  226. string backsideName = mcd.gameObject.name + ".backside";
  227. ArrayList destroyObject = new ArrayList();
  228. foreach (Transform child in mcd.gameObject.transform) {
  229. if (child.name == backsideName) {
  230. destroyObject.Add(child);
  231. MeshFilter emf = (MeshFilter) child.gameObject.GetComponent("MeshFilter");
  232. if (emf != null) {
  233. Mesh ems = (Mesh) emf.sharedMesh;
  234. if (ems != null) {
  235. //DestroyImmediate(ems, true);
  236. }
  237. }
  238. }
  239. }
  240. while (destroyObject.Count > 0) {
  241. Transform child = (Transform) destroyObject[0];
  242. destroyObject.Remove(child);
  243. DestroyImmediate(child.gameObject);
  244. }
  245. // create a new game object to attach the backside plane
  246. GameObject backsideObject = new GameObject();
  247. backsideObject.transform.parent = mcd.gameObject.transform;
  248. backsideObject.transform.localPosition = Vector3.zero;
  249. backsideObject.transform.rotation = Quaternion.identity;
  250. backsideObject.name = backsideName;
  251. MeshFilter backmf = (MeshFilter) backsideObject.AddComponent(typeof(MeshFilter)) as MeshFilter;
  252. backmf.sharedMesh = backmesh;
  253. // save the mesh in the Assets folder
  254. string backMeshName = "Assets/UCLAGameLab/Meshes/" + saveName + ".Back" + ".asset";
  255. AssetDatabase.CreateAsset(backmesh, backMeshName);
  256. MeshRenderer backmr = backsideObject.AddComponent(typeof(MeshRenderer)) as MeshRenderer;
  257. // for backside plane, use the transparent material
  258. Material backmaterial = (Material)AssetDatabase.LoadAssetAtPath(transparentMaterialNameLocation, typeof(Material));
  259. if (backmaterial == null)
  260. {
  261. backmaterial = CopyTexture(transparentBaseMaterialNameLocation, transparentMaterialNameLocation, mcd.outlineTexture);
  262. }
  263. backmr.GetComponent<Renderer>().sharedMaterial = backmaterial;
  264. }
  265. else // remove the old backside mesh game object because it's not needed
  266. {
  267. string backsideName = mcd.gameObject.name + ".backside";
  268. ArrayList destroyObject = new ArrayList();
  269. foreach (Transform child in mcd.gameObject.transform) {
  270. if (child.name == backsideName) {
  271. destroyObject.Add(child);
  272. // get rid of the old mesh from the assets
  273. MeshFilter emf = (MeshFilter) child.gameObject.GetComponent("MeshFilter");
  274. if (emf != null) {
  275. Mesh ems = (Mesh) emf.sharedMesh;
  276. if (ems != null) {
  277. //DestroyImmediate(ems, true);
  278. }
  279. }
  280. }
  281. }
  282. while (destroyObject.Count > 0) {
  283. Transform child = (Transform) destroyObject[0];
  284. destroyObject.Remove(child);
  285. DestroyImmediate(child.gameObject);
  286. }
  287. }
  288. // end mesh renderer setup section
  289. //
  290. // start collider setup section
  291. //
  292. // generate a mesh collider
  293. if (mcd.generateCollider && !mcd.usePrimitiveCollider && !mcd.useAABBCollider) {
  294. // remove the old compound collider before assigning new
  295. string compoundColliderName = mcd.gameObject.name + "CompoundColliders";
  296. foreach(Transform child in mcd.gameObject.transform) {
  297. if (child.name == compoundColliderName) {
  298. DestroyImmediate(child.gameObject);
  299. }
  300. }
  301. // if the current mesh on the mesh renderer is flat
  302. // and the object has a rigidbody, unity will give an
  303. // error trying to update the mass.
  304. // the fix is to stash the current mesh, switch to the
  305. // full 3d version, and switch back
  306. if ( !mcd.uvWrapMesh )
  307. {
  308. mf.mesh = collidermesh;
  309. }
  310. Collider col = mcd.gameObject.GetComponent<Collider>();
  311. if (col == null)
  312. {
  313. mcd.gameObject.AddComponent(typeof(MeshCollider));
  314. }
  315. else
  316. {
  317. DestroyImmediate(col);
  318. mcd.gameObject.AddComponent(typeof(MeshCollider));
  319. }
  320. MeshCollider mcol = mcd.gameObject.GetComponent("MeshCollider") as MeshCollider;
  321. if (mcol == null)
  322. {
  323. Debug.LogWarning("MeshCreator: found a non-Mesh collider on object to update. If you really want a new collider generated, remove the old one and update the object with MeshCreator again.");
  324. }
  325. else
  326. {
  327. mcol.sharedMesh = collidermesh;
  328. // save the collider mesh if necessary
  329. if (!mcd.uvWrapMesh) // if uvWrapMesh, then mesh already saved
  330. {
  331. string colliderMeshName = "Assets/UCLAGameLab/Meshes/" + saveName + ".collider.asset";
  332. AssetDatabase.CreateAsset(collidermesh, colliderMeshName);
  333. }
  334. }
  335. // switch mesh filter back if the flat one was
  336. // swapped out previously
  337. if (!mcd.uvWrapMesh)
  338. {
  339. mf.mesh = msh;
  340. }
  341. if (mcd.usePhysicMaterial)
  342. {
  343. mcol.material = mcd.physicMaterial;
  344. }
  345. // set triggers for the mesh collider?
  346. if (mcd.setTriggers)
  347. {
  348. mcol.isTrigger = true;
  349. }
  350. else
  351. {
  352. mcol.isTrigger = false;
  353. }
  354. } // end generate mesh collider
  355. // generate box colliders
  356. else if (mcd.generateCollider && mcd.usePrimitiveCollider && !mcd.useAABBCollider) {
  357. // remove the old collider if necessary
  358. Collider col = mcd.gameObject.GetComponent<Collider>();
  359. if (col != null) {
  360. if (col.GetType() == typeof(MeshCollider))
  361. {
  362. //Debug.LogWarning("Mesh Creator: found a collider on game object " + gameObject.name +", please remove it.");
  363. MeshCollider mshcol = mcd.gameObject.GetComponent("MeshCollider") as MeshCollider;
  364. if (mshcol != null)
  365. {
  366. //Debug.LogWarning("Mesh Creator: found a mesh collider on game object " + gameObject.name + ", destroying it's mesh.");
  367. mshcol.sharedMesh = null;
  368. }
  369. }
  370. DestroyImmediate(col);
  371. }
  372. // all compound colliders are stored in a gameObject
  373. string compoundColliderName = mcd.gameObject.name + "CompoundColliders";
  374. GameObject go = new GameObject();
  375. // find old compound colliders and remove
  376. foreach (Transform child in mcd.gameObject.transform) {
  377. if (child.name == compoundColliderName) {
  378. DestroyImmediate(go);
  379. go = child.gameObject;
  380. ArrayList removeChildren = new ArrayList();
  381. foreach (Transform childchild in child) {
  382. removeChildren.Add(childchild);
  383. }
  384. foreach (Transform childchild in removeChildren) {
  385. DestroyImmediate(childchild.gameObject);
  386. }
  387. }
  388. }
  389. go.name = compoundColliderName;
  390. go.transform.parent = mcd.gameObject.transform;
  391. go.transform.localPosition = Vector3.zero;
  392. go.transform.rotation = Quaternion.identity;
  393. ArrayList boxColliderCoordinates = GetBoxColliderCoordinates(gameObject);
  394. int count = 0;
  395. int imageHeight = mcd.outlineTexture.height;
  396. int imageWidth = mcd.outlineTexture.width;
  397. foreach (Vector4 bcc in boxColliderCoordinates) {
  398. Vector4 bc = bcc;
  399. // if using a uvWrapMesh, subtract half a pixel from each side
  400. if (mcd.uvWrapMesh && Math.Abs(bc.x - bc.z) > 1.0f && Math.Abs(bc.y - bc.w) > 1.0f) {
  401. bc.x += 0.5f;
  402. bc.y += 0.5f;
  403. bc.z -= 0.5f;
  404. bc.w -= 0.5f;
  405. }
  406. else if (mcd.uvWrapMesh) { // if here, height or width is only one
  407. continue;
  408. }
  409. {
  410. count++;
  411. GameObject colgo = new GameObject();
  412. colgo.name = compoundColliderName+"."+count;
  413. colgo.transform.parent = go.transform;
  414. colgo.transform.localPosition = Vector3.zero;
  415. BoxCollider bxcol = colgo.AddComponent(typeof(BoxCollider)) as BoxCollider;
  416. float vertX = 1.0f - (bc.x/imageWidth) ; // get X point and normalize
  417. float vertY = bc.y/imageHeight ; // get Y point and normalize
  418. float vert2X = 1.0f - (bc.z/imageWidth);
  419. float vert2Y = bc.w/imageHeight;
  420. vertX = (vertX * mcd.meshWidth) - (mcd.meshWidth / 2.0f); // scale X and position centered
  421. vertY = (vertY * mcd.meshHeight) - (mcd.meshHeight / 2.0f);
  422. vert2X = (vert2X * mcd.meshWidth) - (mcd.meshWidth / 2.0f); // scale X and position centered
  423. vert2Y = (vert2Y * mcd.meshHeight) - (mcd.meshHeight / 2.0f);
  424. bxcol.center = new Vector3(vertX - ((vertX-vert2X)/2.0f)-mcd.pivotWidthOffset, vertY - ((vertY-vert2Y)/2.0f)-mcd.pivotHeightOffset, - mcd.pivotDepthOffset);
  425. bxcol.size = new Vector3(Math.Abs(vertX-vert2X), Math.Abs(vertY-vert2Y), mcd.meshDepth);
  426. // use physics material
  427. if (mcd.usePhysicMaterial) {
  428. bxcol.material = mcd.physicMaterial;
  429. }
  430. // set trigger for this box collider?
  431. if (mcd.setTriggers)
  432. {
  433. bxcol.isTrigger = true;
  434. }
  435. }
  436. }
  437. } // end generate box colliders
  438. // generate AABB collider
  439. else if (mcd.generateCollider && !mcd.usePrimitiveCollider && mcd.useAABBCollider)
  440. {
  441. // remove the old collider if necessary
  442. Collider col = mcd.gameObject.GetComponent<Collider>();
  443. if (col != null)
  444. {
  445. DestroyImmediate(col);
  446. }
  447. mcd.gameObject.AddComponent(typeof(BoxCollider));
  448. // remove the old compound collider before assigning new
  449. string compoundColliderName = mcd.gameObject.name + "CompoundColliders";
  450. foreach (Transform child in mcd.gameObject.transform)
  451. {
  452. if (child.name == compoundColliderName)
  453. {
  454. DestroyImmediate(child.gameObject);
  455. }
  456. }
  457. BoxCollider bxcol = mcd.gameObject.GetComponent("BoxCollider") as BoxCollider;
  458. Vector4 extents = GetTransparencyExtents(mcd.gameObject);
  459. int imageHeight = mcd.outlineTexture.height;
  460. int imageWidth = mcd.outlineTexture.width;
  461. float vertX = 1.0f - (extents.x / imageWidth); // get X point and normalize
  462. float vertY = extents.y / imageHeight; // get Y point and normalize
  463. float vert2X = 1.0f - (extents.z / imageWidth);
  464. float vert2Y = extents.w / imageHeight;
  465. vertX = (vertX * mcd.meshWidth) - (mcd.meshWidth / 2.0f); // scale X and position centered
  466. vertY = (vertY * mcd.meshHeight) - (mcd.meshHeight / 2.0f);
  467. vert2X = (vert2X * mcd.meshWidth) - (mcd.meshWidth / 2.0f); // scale X and position centered
  468. vert2Y = (vert2Y * mcd.meshHeight) - (mcd.meshHeight / 2.0f);
  469. bxcol.center = new Vector3(vertX - ((vertX - vert2X) / 2.0f) - mcd.pivotWidthOffset, vertY - ((vertY - vert2Y) / 2.0f) - mcd.pivotHeightOffset, -mcd.pivotDepthOffset);
  470. bxcol.size = new Vector3(Math.Abs(vertX - vert2X), Math.Abs(vertY - vert2Y), mcd.meshDepth);
  471. // use physics material
  472. if (mcd.usePhysicMaterial)
  473. {
  474. bxcol.material = mcd.physicMaterial;
  475. }
  476. // set trigger for this box collider?
  477. if (mcd.setTriggers)
  478. {
  479. bxcol.isTrigger = true;
  480. }
  481. } // end generate AABB collider
  482. else
  483. {
  484. // remove the old collider if necessary
  485. Collider col = mcd.gameObject.GetComponent<Collider>();
  486. if (col != null)
  487. {
  488. DestroyImmediate(col);
  489. }
  490. // remove the old compound collider before assigning new
  491. string compoundColliderName = mcd.gameObject.name + "CompoundColliders";
  492. foreach (Transform child in mcd.gameObject.transform)
  493. {
  494. if (child.name == compoundColliderName)
  495. {
  496. DestroyImmediate(child.gameObject);
  497. }
  498. }
  499. }
  500. // end collider section
  501. mcd.gameObject.transform.rotation = oldRotation;
  502. mcd.gameObject.transform.localScale = oldScale;
  503. }
  504. // Vec4 returned is box coordinates
  505. // upperleft.x,upperleft.y, lowerright.x,lowerright.y
  506. // pixels in Unity are left to right, from bottom to top
  507. static Vector4 GetTransparencyExtents(GameObject gameObject)
  508. {
  509. MeshCreatorData mcd = gameObject.GetComponent(typeof(MeshCreatorData)) as MeshCreatorData;
  510. Vector4 extents = new Vector4();
  511. string path = AssetDatabase.GetAssetPath(mcd.outlineTexture);
  512. TextureImporter textureImporter = AssetImporter.GetAtPath(path) as TextureImporter;
  513. textureImporter.isReadable = true;
  514. AssetDatabase.ImportAsset(path);
  515. Color[] pixels = mcd.outlineTexture.GetPixels(); // get the pixels to build the mesh from
  516. float pixelThreshold = mcd.pixelTransparencyThreshold / 255.0f;
  517. int imageHeight = mcd.outlineTexture.height;
  518. int imageWidth = mcd.outlineTexture.width;
  519. // set the extents to max mins
  520. extents.z = imageWidth - 1;
  521. extents.w = imageHeight - 1;
  522. extents.x = 0;
  523. extents.y = 0;
  524. for (int I = 0; I < imageWidth; I++)
  525. {
  526. for (int j = 0; j < imageHeight; j++)
  527. {
  528. if (pixels[I + (imageWidth * j)].a >= pixelThreshold)
  529. {
  530. if (I < extents.z) extents.z = I;
  531. if (I > extents.x) extents.x = I;
  532. if (j < extents.w) extents.w = j;
  533. if (j > extents.y) extents.y = j;
  534. }
  535. }
  536. }
  537. return extents;
  538. }
  539. static ArrayList GetBoxColliderCoordinates(GameObject gameObject) {
  540. MeshCreatorData mcd = gameObject.GetComponent(typeof(MeshCreatorData)) as MeshCreatorData;
  541. ArrayList boxCoordinates = new ArrayList();
  542. string path = AssetDatabase.GetAssetPath(mcd.outlineTexture);
  543. TextureImporter textureImporter = AssetImporter.GetAtPath(path) as TextureImporter;
  544. textureImporter.isReadable = true;
  545. AssetDatabase.ImportAsset(path);
  546. Color[] pixels = mcd.outlineTexture.GetPixels(); // get the pixels to build the mesh from
  547. // possibly do some size checking
  548. // TODO: check for a square power of two
  549. int imageHeight = mcd.outlineTexture.height;
  550. int imageWidth = mcd.outlineTexture.width;
  551. if ( ((float)imageWidth)/((float)imageHeight) != mcd.meshWidth/mcd.meshHeight) {
  552. Debug.LogWarning("Mesh Creator: selected meshWidth and meshHeight is not the same proportion as source image width and height. Results may be distorted.");
  553. }
  554. // copy the pixels so they can be modified
  555. Color[] pix = new Color[pixels.Length];
  556. for (int i = 0; i < pixels.Length; i++) {
  557. Color pixel = pixels[i];
  558. pix[i] = new Color(pixel.r, pixel.g, pixel.b, pixel.a);
  559. }
  560. Vector4 boxCoord = GetLargestBox(ref pix, imageWidth, imageHeight, mcd.pixelTransparencyThreshold/255.0f);
  561. boxCoordinates.Add(boxCoord);
  562. while(boxCoordinates.Count < mcd.maxNumberBoxes)
  563. {
  564. boxCoord = GetLargestBox(ref pix, imageWidth, imageHeight, mcd.pixelTransparencyThreshold/255.0f);
  565. boxCoordinates.Add(boxCoord);
  566. }
  567. return boxCoordinates;
  568. }
  569. // based on algorithm from http://e-maxx.ru/algo/maximum_zero_submatrix
  570. static Vector4 GetLargestBox(ref Color[] pixs, int imageWidth, int imageHeight, float threshold) {
  571. Vector4 largestBox = new Vector4(-1.0f,-1.0f,-1.0f,-1.0f);
  572. int n = imageHeight;
  573. int m = imageWidth;
  574. List< List<int> > a = new List< List<int> > ( n ) ;
  575. for (int i = 0; i < n; i++) {
  576. a.Add(new List<int>(m));
  577. for (int j = 0; j < m; j++) {
  578. a[i].Add(0);
  579. }
  580. }
  581. for ( int I = 0 ; I < n ; I++ ) {
  582. for ( int j = 0 ; j < m ; j++ ) {
  583. if (pixs[j + (imageWidth * I )].a < threshold) a[ I ][ j ] = 1; // check if alpha is less than threshold
  584. }
  585. }
  586. int ans = 0 ;
  587. List < int > d = new List < int > ( m );
  588. List < int > d1 = new List <int> ( m );
  589. List <int > d2 = new List<int>( m ) ;
  590. for (int i = 0; i < m; ++i) {
  591. d.Add(-1);
  592. d1.Add(-1);
  593. d2.Add(-1);
  594. }
  595. Stack < int > st = new Stack<int>();
  596. for (int i=0; i<n; ++i) {
  597. for (int j=0; j<m; ++j) if (a[i][j] == 1) d[j] = i;
  598. while (st.Count > 0) st.Pop(); // empty the stack
  599. for (int j=0; j<m; ++j) {
  600. while (st.Count > 0 && d[st.Peek()] <= d[j]) st.Pop();
  601. d1[j] = st.Count == 0 ? -1 : st.Peek();
  602. st.Push(j);
  603. }
  604. while (st.Count > 0) st.Pop();
  605. for (int j=m-1; j>=0; --j) {
  606. while (st.Count>0 && d[st.Peek()] <= d[j]) st.Pop();
  607. d2[j] = st.Count == 0 ? m : st.Peek();
  608. st.Push (j);
  609. }
  610. for (int j=0; j<m; ++j) {
  611. int oldLarge = ans;
  612. ans = Math.Max (ans, (i - d[j]) * (d2[j] - d1[j] - 1));
  613. if (oldLarge != ans) {
  614. largestBox[2] = d2[j];
  615. largestBox[3] = i+1;
  616. largestBox[0] = d1[j] +1;
  617. largestBox[1] = d[j]+1;
  618. }
  619. }
  620. }
  621. // remove inside pixels from the box area
  622. if (largestBox.x != -1.0f) {
  623. for (int i = (int)largestBox.x ; i < (int)largestBox.z; i++) {
  624. for (int j = (int)largestBox.y ; j < (int)largestBox.w; j++) {
  625. pixs[i + (j *imageWidth)].a = 0.0f;
  626. }
  627. }
  628. // delete all pixels if this is width 1 or height 1
  629. if ( ((int)Math.Abs(largestBox.x-largestBox.z) == 1) || ((int)Math.Abs(largestBox.y-largestBox.w) == 1) ){
  630. for (int i = (int)largestBox.x; i <= (int)largestBox.z; i++) {
  631. for (int j = (int)largestBox.y; j <= (int)largestBox.w; j++) {
  632. pixs[i + (j *imageWidth)].a = 0.0f;
  633. }
  634. }
  635. }
  636. }
  637. else {
  638. Debug.Log("Mesh Creator: yikes, got a negative box inside pixel map array. Try resaving the image. Please create a new issue at https://github.com/uclagamelab/MeshCreator/issues.");
  639. }
  640. return largestBox;
  641. }
  642. /*
  643. * AssignMesh() does calculation of a uv mapped mesh from the raster image.
  644. */
  645. public static void AssignMesh(GameObject gameObject, ref Mesh msh) {
  646. MeshCreatorData mcd = gameObject.GetComponent(typeof(MeshCreatorData)) as MeshCreatorData;
  647. string path = AssetDatabase.GetAssetPath(mcd.outlineTexture);
  648. TextureImporter textureImporter = AssetImporter.GetAtPath(path) as TextureImporter;
  649. textureImporter.isReadable = true;
  650. AssetDatabase.ImportAsset(path);
  651. Color[] pixels = mcd.outlineTexture.GetPixels(); // get the pixels to build the mesh from
  652. // possibly do some size checking
  653. int imageHeight = mcd.outlineTexture.height;
  654. int imageWidth = mcd.outlineTexture.width;
  655. if ( ((float)imageWidth)/((float)imageHeight) != mcd.meshWidth/mcd.meshHeight) {
  656. //Debug.LogWarning("Mesh Creator Inspector Warning: selected meshWidth and meshHeight is not the same proportion as source image width and height. Results may be distorted.");
  657. //Debug.LogWarning(" You may want to resize your image to be square, it can be easier that way.");
  658. }
  659. // make a surface object to create and store data from image
  660. MC_SimpleSurfaceEdge mcs = new MC_SimpleSurfaceEdge(pixels, imageWidth, imageHeight, mcd.pixelTransparencyThreshold/255.0f);
  661. if ( mcd.mergeClosePoints ) mcs.MergeClosePoints(mcd.mergeDistance);
  662. // Create the mesh
  663. if (!mcs.ContainsIslands()) {
  664. // need a list of ordered 2d points
  665. Vector2 [] vertices2D = mcs.GetOutsideEdgeVertices();
  666. // Use the triangulator to get indices for creating triangles
  667. Triangulator tr = new Triangulator(vertices2D);
  668. int[] indices = tr.Triangulate(); // these will be reversed for the back side
  669. Vector2[] uvs = new Vector2[vertices2D.Length * 4];
  670. // Create the Vector3 vertices
  671. Vector3[] vertices = new Vector3[vertices2D.Length * 4];
  672. float halfDepth = -mcd.meshDepth/2.0f;
  673. float halfVerticalPixel = 0.5f/imageHeight;
  674. float halfHorizontalPixel = 0.5f/imageWidth;
  675. for (int i=0; i<vertices2D.Length; i++) {
  676. float vertX = 1.0f - (vertices2D[i].x/imageWidth) - halfHorizontalPixel; // get X point and normalize
  677. float vertY = vertices2D[i].y/imageHeight + halfVerticalPixel; // get Y point and normalize
  678. vertX = (vertX * mcd.meshWidth) - (mcd.meshWidth / 2.0f); // scale X and position centered
  679. vertY = (vertY * mcd.meshHeight) - (mcd.meshHeight / 2.0f);
  680. vertices[i] = new Vector3(vertX - mcd.pivotWidthOffset, vertY - mcd.pivotHeightOffset, -halfDepth - mcd.pivotDepthOffset);
  681. vertices[i + vertices2D.Length] = new Vector3(vertX - mcd.pivotWidthOffset, vertY - mcd.pivotHeightOffset, halfDepth-mcd.pivotDepthOffset);
  682. vertices[i+(vertices2D.Length*2)] = new Vector3(vertX - mcd.pivotWidthOffset, vertY - mcd.pivotHeightOffset, -halfDepth -mcd.pivotDepthOffset); // vertex for side
  683. vertices[i +(vertices2D.Length*3)] = new Vector3(vertX-mcd.pivotWidthOffset, vertY-mcd.pivotHeightOffset, halfDepth -mcd.pivotDepthOffset);
  684. uvs[i] = mcs.GetUVForIndex(i);
  685. uvs[i+vertices2D.Length] = uvs[i];
  686. uvs[i+(vertices2D.Length*2)] = uvs[i];
  687. uvs[i+(vertices2D.Length*3)] = uvs[i];
  688. }
  689. // make the back side triangle indices
  690. // double the indices for front and back, 6 times the number of edges on front
  691. int[] allIndices = new int[(indices.Length*2) + ( (vertices2D.Length ) * 6)];
  692. // copy over the front and back index data
  693. for (int i = 0; i < indices.Length; i++) {
  694. allIndices[i] = indices[i]; // front side uses normal indices returned from the algorithm
  695. allIndices[(indices.Length*2) - i -1] = indices[i] + vertices2D.Length; // backside reverses the order
  696. }
  697. // create the side triangle indices
  698. // for each edge, create a new set of two triangles
  699. // edges are just two points from the original set
  700. for (int i = 0; i < vertices2D.Length - 1; i++) {
  701. allIndices[(indices.Length*2) + (6 * i)] = (vertices2D.Length *2) + i + 1;
  702. allIndices[(indices.Length*2) + (6 * i) + 1] = (vertices2D.Length *2) +i ;
  703. allIndices[(indices.Length*2) + (6 * i) + 2] = (vertices2D.Length *2) + i + 1 + vertices2D.Length;
  704. allIndices[(indices.Length*2) + (6 * i) + 3] = (vertices2D.Length *2) + i + 1 + vertices2D.Length;
  705. allIndices[(indices.Length*2) + (6 * i) + 4] = (vertices2D.Length *2) + i ;
  706. allIndices[(indices.Length*2) + (6 * i) + 5] = (vertices2D.Length *2) + i + vertices2D.Length;
  707. }
  708. // wrap around for the last face
  709. allIndices[allIndices.Length-6] = (vertices2D.Length *2) + 0;
  710. allIndices[allIndices.Length-5] = (vertices2D.Length *2) +vertices2D.Length-1;
  711. allIndices[allIndices.Length-4] = (vertices2D.Length *2) +vertices2D.Length;
  712. allIndices[allIndices.Length-3] = (vertices2D.Length *2) +vertices2D.Length;
  713. allIndices[allIndices.Length-2] = (vertices2D.Length *2) +vertices2D.Length-1;
  714. allIndices[allIndices.Length-1] = (vertices2D.Length *2) + (vertices2D.Length*2) - 1;
  715. msh.vertices = vertices;
  716. msh.triangles = allIndices;
  717. msh.uv = uvs;
  718. msh.RecalculateNormals();
  719. msh.RecalculateBounds();
  720. msh.name = mcd.outlineTexture.name + ".asset";
  721. // this will get the pivot drawing in the correct place
  722. Bounds oldBounds = msh.bounds;
  723. msh.bounds = new Bounds(Vector3.zero, new Vector3(oldBounds.size.x, oldBounds.size.y, oldBounds.size.z));
  724. }
  725. else { // there be islands here, so treat mesh creation slightly differently
  726. ArrayList allVertexLoops = mcs.GetAllEdgeVertices();
  727. ArrayList completeVertices = new ArrayList();
  728. ArrayList completeIndices = new ArrayList();
  729. ArrayList completeUVs = new ArrayList();
  730. int verticesOffset = 0;
  731. int indicesOffset = 0;
  732. int uvOffset = 0;
  733. int loopCount = 0;
  734. foreach (Vector2[] vertices2D in allVertexLoops) {
  735. // TODO: this needs to check if the current list is inside another shape
  736. // Use the triangulator to get indices for creating triangles
  737. Triangulator tr = new Triangulator(vertices2D);
  738. int[] indices = tr.Triangulate(); // these will be reversed for the back side
  739. Vector2[] uvs = new Vector2[vertices2D.Length * 4];
  740. // Create the Vector3 vertices
  741. Vector3[] vertices = new Vector3[vertices2D.Length * 4];
  742. float halfDepth = -mcd.meshDepth/2.0f;
  743. float halfVerticalPixel = 0.5f/imageHeight;
  744. float halfHorizontalPixel = 0.5f/imageWidth;
  745. for (int i=0; i<vertices2D.Length; i++) {
  746. float vertX = 1.0f - (vertices2D[i].x/imageWidth) - halfHorizontalPixel; // get X point and normalize
  747. float vertY = vertices2D[i].y/imageHeight + halfVerticalPixel; // get Y point and normalize
  748. vertX = (vertX * mcd.meshWidth) - (mcd.meshWidth / 2.0f); // scale X and position centered
  749. vertY = (vertY * mcd.meshHeight) - (mcd.meshHeight / 2.0f);
  750. vertices[i] = new Vector3(vertX-mcd.pivotWidthOffset, vertY-mcd.pivotHeightOffset, -halfDepth -mcd.pivotDepthOffset);
  751. vertices[i + vertices2D.Length] = new Vector3(vertX-mcd.pivotWidthOffset, vertY-mcd.pivotHeightOffset, halfDepth-mcd.pivotDepthOffset);
  752. vertices[i+(vertices2D.Length*2)] = new Vector3(vertX-mcd.pivotWidthOffset, vertY-mcd.pivotHeightOffset, -halfDepth -mcd.pivotDepthOffset); // vertex for side
  753. vertices[i +(vertices2D.Length*3)] = new Vector3(vertX-mcd.pivotWidthOffset, vertY-mcd.pivotHeightOffset, halfDepth -mcd.pivotDepthOffset);
  754. uvs[i] = mcs.GetUVForIndex(loopCount, i);
  755. uvs[i+vertices2D.Length] = uvs[i];
  756. uvs[i+(vertices2D.Length*2)] = uvs[i];
  757. uvs[i+(vertices2D.Length*3)] = uvs[i];
  758. }
  759. // make the back side triangle indices
  760. // double the indices for front and back, 6 times the number of edges on front
  761. int[] allIndices = new int[(indices.Length*2) + ( (vertices2D.Length ) * 6)];
  762. // copy over the front and back index data
  763. for (int i = 0; i < indices.Length; i++) {
  764. allIndices[i] = indices[i] +verticesOffset; // front side uses normal indices returned from the algorithm
  765. allIndices[(indices.Length*2) - i -1] = indices[i] + vertices2D.Length + verticesOffset; // backside reverses the order
  766. }
  767. // create the side triangle indices
  768. // for each edge, create a new set of two triangles
  769. // edges are just two points from the original set
  770. for (int i = 0; i < vertices2D.Length - 1; i++) {
  771. allIndices[(indices.Length*2) + (6 * i)] = (vertices2D.Length *2) + i + 1 + verticesOffset;
  772. allIndices[(indices.Length*2) + (6 * i) + 1] = (vertices2D.Length *2) +i + verticesOffset;
  773. allIndices[(indices.Length*2) + (6 * i) + 2] = (vertices2D.Length *2) + i + 1 + vertices2D.Length+ verticesOffset;
  774. allIndices[(indices.Length*2) + (6 * i) + 3] = (vertices2D.Length *2) + i + 1 + vertices2D.Length+ verticesOffset;
  775. allIndices[(indices.Length*2) + (6 * i) + 4] = (vertices2D.Length *2) + i + verticesOffset;
  776. allIndices[(indices.Length*2) + (6 * i) + 5] = (vertices2D.Length *2) + i + vertices2D.Length+ verticesOffset;
  777. }
  778. // wrap around for the last face
  779. allIndices[allIndices.Length-6] = (vertices2D.Length *2) + 0+ verticesOffset;
  780. allIndices[allIndices.Length-5] = (vertices2D.Length *2) +vertices2D.Length-1+ verticesOffset;
  781. allIndices[allIndices.Length-4] = (vertices2D.Length *2) +vertices2D.Length+ verticesOffset;
  782. allIndices[allIndices.Length-3] = (vertices2D.Length *2) +vertices2D.Length+ verticesOffset;
  783. allIndices[allIndices.Length-2] = (vertices2D.Length *2) +vertices2D.Length-1+ verticesOffset;
  784. allIndices[allIndices.Length-1] = (vertices2D.Length *2) + (vertices2D.Length*2) - 1+ verticesOffset;
  785. foreach(Vector3 v in vertices) {
  786. completeVertices.Add(v);
  787. }
  788. foreach(Vector2 v in uvs) {
  789. completeUVs.Add(v);
  790. }
  791. foreach(int i in allIndices) {
  792. completeIndices.Add(i);
  793. }
  794. verticesOffset += vertices.Length;
  795. uvOffset += uvs.Length;
  796. indicesOffset += allIndices.Length;
  797. loopCount++;
  798. }
  799. msh.vertices = (Vector3[]) completeVertices.ToArray(typeof(Vector3));
  800. msh.triangles = (int[]) completeIndices.ToArray(typeof(int));
  801. msh.uv = (Vector2[]) completeUVs.ToArray(typeof(Vector2));
  802. msh.RecalculateNormals();
  803. msh.RecalculateBounds();
  804. msh.name = mcd.outlineTexture.name + ".asset";
  805. // this will get the pivot drawing in the correct place
  806. Bounds oldBounds = msh.bounds;
  807. msh.bounds = new Bounds(Vector3.zero, new Vector3(oldBounds.size.x, oldBounds.size.y, oldBounds.size.z));
  808. }
  809. }
  810. /*
  811. * AssignPlaneMesh() does calculation for a simple plane with uv coordinates
  812. * at the corners of the images. Really simple.
  813. */
  814. public static void AssignPlaneMesh(GameObject gameObject, ref Mesh msh) {
  815. MeshCreatorData mcd = gameObject.GetComponent(typeof(MeshCreatorData)) as MeshCreatorData;
  816. // get the outline texture
  817. string path = AssetDatabase.GetAssetPath(mcd.outlineTexture);
  818. TextureImporter textureImporter = AssetImporter.GetAtPath(path) as TextureImporter;
  819. textureImporter.isReadable = true;
  820. AssetDatabase.ImportAsset(path);
  821. // do some size checking
  822. int imageHeight = mcd.outlineTexture.height;
  823. int imageWidth = mcd.outlineTexture.width;
  824. if ( ((float)imageWidth)/((float)imageHeight) != mcd.meshWidth/mcd.meshHeight) {
  825. Debug.LogWarning("Mesh Creator: selected meshWidth and meshHeight is not the same proportion as source image width and height. Results may be distorted.");
  826. Debug.LogWarning(" You may want to resize your image to be square, it can be easier that way.");
  827. }
  828. // need a list of ordered 2d points
  829. Vector2 [] vertices2D = {new Vector2(0.0f,0.0f), new Vector2(0.0f, imageHeight), new Vector2(imageWidth, imageHeight), new Vector2(imageWidth,0.0f)};
  830. //
  831. int[] indices = {0,1,2,0,2,3}; // these will be reversed for the back side
  832. Vector2[] frontUVs = {new Vector2(0.0f,0.0f), new Vector2(0.0f,1.0f), new Vector2(1.0f,1.0f), new Vector2(1.0f,0.0f) };
  833. Vector2[] uvs = new Vector2[vertices2D.Length];
  834. // Create the Vector3 vertices
  835. Vector3[] vertices = new Vector3[vertices2D.Length];
  836. float halfDepth = -mcd.meshDepth/2.0f;
  837. for (int i=0; i<vertices2D.Length; i++) {
  838. float vertX = 1.0f - (vertices2D[i].x/imageWidth) ; // get X point and normalize
  839. float vertY = vertices2D[i].y/imageHeight; // get Y point and normalize
  840. vertX = (vertX * mcd.meshWidth) - (mcd.meshWidth / 2.0f); // scale X and position centered
  841. vertY = (vertY * mcd.meshHeight) - (mcd.meshHeight / 2.0f);
  842. vertices[i] = new Vector3(vertX -mcd.pivotWidthOffset, vertY - mcd.pivotHeightOffset, -halfDepth - mcd.pivotDepthOffset );
  843. uvs[i] = frontUVs[i];
  844. }
  845. msh.vertices = vertices;
  846. msh.triangles = indices;
  847. msh.uv = uvs;
  848. msh.RecalculateNormals();
  849. msh.RecalculateBounds();
  850. msh.name = mcd.outlineTexture.name + ".mesh";
  851. // this will get the pivot drawing in the correct place
  852. Bounds oldBounds = msh.bounds;
  853. msh.bounds = new Bounds(Vector3.zero, new Vector3(oldBounds.size.x, oldBounds.size.y, oldBounds.size.z));
  854. }
  855. /*
  856. * AssignPlaneMesh() does calculation for a simple plane with uv coordinates
  857. * at the corners of the images. Really simple.
  858. */
  859. public static void AssignPlaneMeshBackside(GameObject gameObject, ref Mesh msh) {
  860. MeshCreatorData mcd = gameObject.GetComponent(typeof(MeshCreatorData)) as MeshCreatorData;
  861. // get the outline texture
  862. string path = AssetDatabase.GetAssetPath(mcd.outlineTexture);
  863. TextureImporter textureImporter = AssetImporter.GetAtPath(path) as TextureImporter;
  864. textureImporter.isReadable = true;
  865. AssetDatabase.ImportAsset(path);
  866. // do some size checking
  867. int imageHeight = mcd.outlineTexture.height;
  868. int imageWidth = mcd.outlineTexture.width;
  869. if ( ((float)imageWidth)/((float)imageHeight) != mcd.meshWidth/mcd.meshHeight) {
  870. Debug.LogWarning("Mesh Creator Inspector Warning: selected meshWidth and meshHeight is not the same proportion as source image width and height. Results may be distorted.");
  871. Debug.LogWarning(" You may want to resize your image to be square, it can be easier that way.");
  872. }
  873. // need a list of ordered 2d points
  874. Vector2 [] vertices2D = {new Vector2(0.0f,0.0f), new Vector2(0.0f, imageHeight), new Vector2(imageWidth, imageHeight), new Vector2(imageWidth,0.0f)};
  875. //
  876. int[] indices = {2,1,0,3,2,0}; // these will be reversed for the back side
  877. Vector2[] frontUVs = {new Vector2(0.0f,0.0f), new Vector2(0.0f,1.0f), new Vector2(1.0f,1.0f), new Vector2(1.0f,0.0f) };
  878. Vector2[] uvs = new Vector2[vertices2D.Length];
  879. // Create the Vector3 vertices
  880. Vector3[] vertices = new Vector3[vertices2D.Length];
  881. float halfDepth = mcd.meshDepth/2.0f;
  882. for (int i=0; i<vertices2D.Length; i++) {
  883. float vertX = 1.0f - (vertices2D[i].x/imageWidth) ; // get X point and normalize
  884. float vertY = vertices2D[i].y/imageHeight; // get Y point and normalize
  885. vertX = (vertX * mcd.meshWidth) - (mcd.meshWidth / 2.0f); // scale X and position centered
  886. vertY = (vertY * mcd.meshHeight) - (mcd.meshHeight / 2.0f);
  887. vertices[i] = new Vector3(vertX - mcd.pivotWidthOffset, vertY - mcd.pivotHeightOffset, -halfDepth - mcd.pivotDepthOffset);
  888. uvs[i] = frontUVs[i];
  889. }
  890. msh.vertices = vertices;
  891. msh.triangles = indices;
  892. msh.uv = uvs;
  893. msh.RecalculateNormals();
  894. msh.RecalculateBounds();
  895. msh.name = mcd.outlineTexture.name + ".asset";
  896. // this will get the pivot drawing in the correct place
  897. Bounds oldBounds = msh.bounds;
  898. msh.bounds = new Bounds(Vector3.zero, new Vector3(oldBounds.size.x, oldBounds.size.y, oldBounds.size.z));
  899. }
  900. /*
  901. * AssignEdgeMesh() does calculation of a uv mapped edge mesh from the raster image.
  902. * no front or back planes are included
  903. */
  904. public static void AssignEdgeMesh(GameObject gameObject, ref Mesh msh) {
  905. MeshCreatorData mcd = gameObject.GetComponent(typeof(MeshCreatorData)) as MeshCreatorData;
  906. string path = AssetDatabase.GetAssetPath(mcd.outlineTexture);
  907. TextureImporter textureImporter = AssetImporter.GetAtPath(path) as TextureImporter;
  908. textureImporter.isReadable = true;
  909. AssetDatabase.ImportAsset(path);
  910. Color[] pixels = mcd.outlineTexture.GetPixels(); // get the pixels to build the mesh from
  911. // possibly do some size checking
  912. int imageHeight = mcd.outlineTexture.height;
  913. int imageWidth = mcd.outlineTexture.width;
  914. if ( ((float)imageWidth)/((float)imageHeight) != mcd.meshWidth/mcd.meshHeight) {
  915. Debug.LogWarning("Mesh Creator: selected meshWidth and meshHeight is not the same proportion as source image width and height. Results may be distorted.");
  916. Debug.LogWarning(" You may want to resize your image to be square, it can be easier that way.");
  917. }
  918. // make a surface object to create and store data from image
  919. MC_SimpleSurfaceEdge mcs = new MC_SimpleSurfaceEdge(pixels, imageWidth, imageHeight, mcd.pixelTransparencyThreshold/255.0f);
  920. if (!mcs.ContainsIslands()) {
  921. // need a list of ordered 2d points
  922. Vector2 [] vertices2D = mcs.GetOutsideEdgeVertices();
  923. // Use the triangulator to get indices for creating triangles
  924. //Triangulator tr = new Triangulator(vertices2D);
  925. //int[] indices = tr.Triangulate(); // these will be reversed for the back side
  926. Vector2[] uvs = new Vector2[vertices2D.Length * 2];
  927. // Create the Vector3 vertices
  928. Vector3[] vertices = new Vector3[vertices2D.Length * 2];
  929. float halfDepth = -mcd.meshDepth/2.0f;
  930. float halfVerticalPixel = 0.5f/imageHeight;
  931. float halfHorizontalPixel = 0.5f/imageWidth;
  932. for (int i=0; i<vertices2D.Length; i++) {
  933. float vertX = 1.0f - (vertices2D[i].x/imageWidth) - halfHorizontalPixel; // get X point and normalize
  934. float vertY = vertices2D[i].y/imageHeight + halfVerticalPixel; // get Y point and normalize
  935. vertX = (vertX * mcd.meshWidth) - (mcd.meshWidth / 2.0f); // scale X and position centered
  936. vertY = (vertY * mcd.meshHeight) - (mcd.meshHeight / 2.0f);
  937. vertices[i] = new Vector3(vertX - mcd.pivotWidthOffset, vertY - mcd.pivotHeightOffset, -halfDepth - mcd.pivotDepthOffset); // vertex for side
  938. vertices[i +vertices2D.Length] = new Vector3(vertX - mcd.pivotWidthOffset, vertY - mcd.pivotHeightOffset, halfDepth - mcd.pivotDepthOffset);
  939. uvs[i] = mcs.GetUVForIndex(i);
  940. uvs[i+vertices2D.Length] = uvs[i];
  941. }
  942. // make the back side triangle indices
  943. // double the indices for front and back, 6 times the number of edges on front
  944. int[] allIndices = new int[vertices2D.Length * 6];
  945. // create the side triangle indices
  946. // for each edge, create a new set of two triangles
  947. // edges are just two points from the original set
  948. for (int i = 0; i < vertices2D.Length - 1; i++) {
  949. allIndices[ (6 * i)] = i + 1;
  950. allIndices[ (6 * i) + 1] = i ;
  951. allIndices[ (6 * i) + 2] = i + 1 + vertices2D.Length;
  952. allIndices[ (6 * i) + 3] = i + 1 + vertices2D.Length;
  953. allIndices[ (6 * i) + 4] = i ;
  954. allIndices[ (6 * i) + 5] = i + vertices2D.Length;
  955. }
  956. // wrap around for the last face
  957. allIndices[allIndices.Length-6] = 0;
  958. allIndices[allIndices.Length-5] = vertices2D.Length-1;
  959. allIndices[allIndices.Length-4] =vertices2D.Length;
  960. allIndices[allIndices.Length-3] = vertices2D.Length;
  961. allIndices[allIndices.Length-2] = vertices2D.Length-1;
  962. allIndices[allIndices.Length-1] = (vertices2D.Length*2) - 1;
  963. msh.vertices = vertices;
  964. msh.triangles = allIndices;
  965. msh.uv = uvs;
  966. msh.RecalculateNormals();
  967. msh.RecalculateBounds();
  968. msh.name = mcd.outlineTexture.name + ".asset";
  969. // this will get the pivot drawing in the correct place
  970. Bounds oldBounds = msh.bounds;
  971. msh.bounds = new Bounds(Vector3.zero, new Vector3(oldBounds.size.x, oldBounds.size.y, oldBounds.size.z));
  972. }
  973. else { // there be islands here, so treat mesh creation slightly differently
  974. ArrayList allVertexLoops = mcs.GetAllEdgeVertices();
  975. ArrayList completeVertices = new ArrayList();
  976. ArrayList completeIndices = new ArrayList();
  977. ArrayList completeUVs = new ArrayList();
  978. int verticesOffset = 0;
  979. int indicesOffset = 0;
  980. int uvOffset = 0;
  981. int loopCount = 0;
  982. foreach (Vector2[] vertices2D in allVertexLoops) {
  983. Vector2[] uvs = new Vector2[vertices2D.Length * 4];
  984. // Create the Vector3 vertices
  985. Vector3[] vertices = new Vector3[vertices2D.Length * 4];
  986. float halfDepth = -mcd.meshDepth/2.0f;
  987. float halfVerticalPixel = 0.5f/imageHeight;
  988. float halfHorizontalPixel = 0.5f/imageWidth;
  989. for (int i=0; i<vertices2D.Length; i++) {
  990. float vertX = 1.0f - (vertices2D[i].x/imageWidth) - halfHorizontalPixel; // get X point and normalize
  991. float vertY = vertices2D[i].y/imageHeight + halfVerticalPixel; // get Y point and normalize
  992. vertX = (vertX * mcd.meshWidth) - (mcd.meshWidth / 2.0f); // scale X and position centered
  993. vertY = (vertY * mcd.meshHeight) - (mcd.meshHeight / 2.0f);
  994. vertices[i] = new Vector3(vertX - mcd.pivotWidthOffset, vertY - mcd.pivotHeightOffset, -halfDepth - mcd.pivotDepthOffset);
  995. vertices[i + vertices2D.Length] = new Vector3(vertX - mcd.pivotWidthOffset, vertY - mcd.pivotHeightOffset, halfDepth - mcd.pivotDepthOffset);
  996. uvs[i] = mcs.GetUVForIndex(loopCount, i);
  997. uvs[i+vertices2D.Length] = uvs[i];
  998. }
  999. // make the back side triangle indices
  1000. // double the indices for front and back, 6 times the number of edges on front
  1001. int[] allIndices = new int[vertices2D.Length * 6];
  1002. // create the side triangle indices
  1003. // for each edge, create a new set of two triangles
  1004. // edges are just two points from the original set
  1005. for (int i = 0; i < vertices2D.Length - 1; i++) {
  1006. allIndices[(6 * i)] = i + 1 + verticesOffset;
  1007. allIndices[(6 * i) + 1] = i + verticesOffset;
  1008. allIndices[(6 * i) + 2] = i + 1 + vertices2D.Length+ verticesOffset;
  1009. allIndices[ (6 * i) + 3] = i + 1 + vertices2D.Length+ verticesOffset;
  1010. allIndices[(6 * i) + 4] = i + verticesOffset;
  1011. allIndices[(6 * i) + 5] = i + vertices2D.Length+ verticesOffset;
  1012. }
  1013. // wrap around for the last face
  1014. allIndices[allIndices.Length-6] = 0+ verticesOffset;
  1015. allIndices[allIndices.Length-5] =vertices2D.Length-1+ verticesOffset;
  1016. allIndices[allIndices.Length-4] = vertices2D.Length+ verticesOffset;
  1017. allIndices[allIndices.Length-3] = vertices2D.Length+ verticesOffset;
  1018. allIndices[allIndices.Length-2] = vertices2D.Length-1+ verticesOffset;
  1019. allIndices[allIndices.Length-1] = (vertices2D.Length*2) - 1+ verticesOffset;
  1020. foreach(Vector3 v in vertices) {
  1021. completeVertices.Add(v);
  1022. }
  1023. foreach(Vector2 v in uvs) {
  1024. completeUVs.Add(v);
  1025. }
  1026. foreach(int i in allIndices) {
  1027. completeIndices.Add(i);
  1028. }
  1029. verticesOffset += vertices.Length;
  1030. uvOffset += uvs.Length;
  1031. indicesOffset += allIndices.Length;
  1032. loopCount++;
  1033. }
  1034. msh.vertices = (Vector3[]) completeVertices.ToArray(typeof(Vector3));
  1035. msh.triangles = (int[]) completeIndices.ToArray(typeof(int));
  1036. msh.uv = (Vector2[]) completeUVs.ToArray(typeof(Vector2));
  1037. msh.RecalculateNormals();
  1038. msh.RecalculateBounds();
  1039. msh.name = mcd.outlineTexture.name + ".asset";
  1040. // this will get the pivot drawing in the correct place
  1041. Bounds oldBounds = msh.bounds;
  1042. msh.bounds = new Bounds(Vector3.zero, new Vector3(oldBounds.size.x, oldBounds.size.y, oldBounds.size.z));
  1043. }
  1044. }
  1045. private static String GetTimestamp() {
  1046. return DateTime.Now.ToString("yyyyMMddHHmmssffff");
  1047. }
  1048. // copies a texture and saves into the project
  1049. public static Material CopyTexture(string baseNameLocation,
  1050. string newNameLocation,
  1051. Texture texture)
  1052. {
  1053. Material mat;
  1054. AssetDatabase.CopyAsset(baseNameLocation, newNameLocation);
  1055. AssetDatabase.ImportAsset(newNameLocation);
  1056. mat = (Material)AssetDatabase.LoadAssetAtPath(newNameLocation, typeof(Material));
  1057. // mat.name = mcd.outlineTexture.name + ".Material"; // this probably isn't needed
  1058. mat.mainTexture = texture;
  1059. AssetDatabase.SaveAssets();
  1060. return mat;
  1061. }
  1062. // generates a unique string for mesh naming
  1063. // from http://madskristensen.net/post/Generate-unique-strings-and-numbers-in-C.aspx
  1064. public static string GenerateId()
  1065. {
  1066. long i = 1;
  1067. foreach (byte b in Guid.NewGuid().ToByteArray())
  1068. {
  1069. i *= ((int)b + 1);
  1070. }
  1071. return string.Format("{0:x}", i - DateTime.Now.Ticks);
  1072. }
  1073. public static bool IdExistsInScene(MeshCreatorData mcd)
  1074. {
  1075. // check all objects in this scene for a matching unique number
  1076. object[] objs = GameObject.FindObjectsOfType( typeof(GameObject));
  1077. foreach (GameObject go in objs)
  1078. {
  1079. MeshCreatorData meshcd = go.GetComponent(typeof(MeshCreatorData)) as MeshCreatorData;
  1080. if (meshcd && go != mcd.gameObject)
  1081. {
  1082. if (meshcd.idNumber == mcd.idNumber) return true;
  1083. }
  1084. }
  1085. return false;
  1086. }
  1087. }