SceneBuilder.Meshes.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  1. using System;
  2. using System.IO;
  3. using System.Text;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using BabylonExport.Entities;
  7. using UnityEditor;
  8. using UnityEngine;
  9. namespace Unity3D2Babylon
  10. {
  11. partial class SceneBuilder
  12. {
  13. private BabylonMesh ConvertUnityEmptyObjectToBabylon(GameObject gameObject, ref UnityMetaData metaData, ref List<BabylonExport.Entities.BabylonParticleSystem> particleSystems, ref List<UnityFlareSystem> lensFlares, ref string componentTags, BabylonMesh collisionMesh = null, Collider collider = null)
  14. {
  15. BabylonMesh babylonMesh = new BabylonMesh { name = gameObject.name, id = GetID(gameObject) };
  16. metaData.type = "Game";
  17. if (!String.IsNullOrEmpty(componentTags))
  18. {
  19. babylonMesh.tags = componentTags;
  20. }
  21. var transform = gameObject.transform;
  22. babylonMesh.parentId = GetParentID(transform);
  23. babylonMesh.position = transform.localPosition.ToFloat();
  24. babylonMesh.rotation = new float[3];
  25. babylonMesh.rotation[0] = transform.localRotation.eulerAngles.x * (float)Math.PI / 180;
  26. babylonMesh.rotation[1] = transform.localRotation.eulerAngles.y * (float)Math.PI / 180;
  27. babylonMesh.rotation[2] = transform.localRotation.eulerAngles.z * (float)Math.PI / 180;
  28. babylonMesh.scaling = transform.localScale.ToFloat();
  29. babylonMesh.isVisible = false;
  30. babylonMesh.visibility = 0;
  31. babylonMesh.checkCollisions = false;
  32. // Collision mesh (No detail mesh fallback)
  33. string collisionMeshId = null;
  34. if (collider != null && collisionMesh != null)
  35. {
  36. collisionMeshId = collisionMesh.id;
  37. collisionMesh.parentId = babylonMesh.id;
  38. collisionMesh.visibility = collider.isTrigger ? 0.25f : 0.5f;
  39. collisionMesh.checkCollisions = (exportationOptions.ExportCollisions && collider.isTrigger == false);
  40. }
  41. metaData.properties["collisionMeshId"] = collisionMeshId;
  42. babylonMesh.metadata = metaData;
  43. babylonScene.MeshesList.Add(babylonMesh);
  44. // Animations
  45. ExportAnimations(transform, babylonMesh);
  46. if (IsRotationQuaternionAnimated(babylonMesh))
  47. {
  48. babylonMesh.rotationQuaternion = transform.localRotation.ToFloat();
  49. }
  50. // Lens Flares
  51. ParseLensFlares(gameObject, babylonMesh.id, ref lensFlares);
  52. // Particles Systems
  53. ParseParticleSystems(gameObject, babylonMesh.id, ref particleSystems);
  54. return babylonMesh;
  55. }
  56. private BabylonMesh ConvertUnityTerrainToBabylon(Terrain terrain, GameObject gameObject, float progress, ref UnityMetaData metaData, ref List<BabylonExport.Entities.BabylonParticleSystem> particleSystems, ref List<UnityFlareSystem> lensFlares, ref string componentTags)
  57. {
  58. ExporterWindow.ReportProgress(progress, "Exporting terrain: " + gameObject.name);
  59. var transform = gameObject.transform;
  60. float[] position = transform.localPosition.ToFloat();
  61. float[] rotation = new float[3];
  62. rotation[0] = transform.localRotation.eulerAngles.x * (float)Math.PI / 180;
  63. rotation[1] = transform.localRotation.eulerAngles.y * (float)Math.PI / 180;
  64. rotation[2] = transform.localRotation.eulerAngles.z * (float)Math.PI / 180;
  65. float[] scaling = transform.localScale.ToFloat();
  66. BabylonMesh babylonMesh = new BabylonMesh { name = gameObject.name, id = GetID(gameObject) };
  67. metaData.type = "Terrain";
  68. if (!String.IsNullOrEmpty(componentTags))
  69. {
  70. babylonMesh.tags = componentTags;
  71. }
  72. babylonMesh.tags += " [TERRAIN]";
  73. if (!String.IsNullOrEmpty(babylonMesh.tags))
  74. {
  75. babylonMesh.tags = babylonMesh.tags.Trim();
  76. }
  77. babylonMesh.parentId = GetParentID(transform);
  78. babylonMesh.position = Vector3.zero.ToFloat();
  79. babylonMesh.rotation = rotation;
  80. babylonMesh.scaling = scaling;
  81. babylonMesh.isVisible = true;
  82. babylonMesh.visibility = 1;
  83. babylonMesh.checkCollisions = false;
  84. metaData.properties["collisionMeshId"] = null;
  85. var generator = gameObject.GetComponent<BabylonTerrainGenerator>();
  86. if (generator != null && terrain != null)
  87. {
  88. // TODO: Terrain tree information
  89. object treeInstances = null;
  90. object treePrototypes = null;
  91. // Terrain metadata infomation
  92. Vector3 terrainSize = terrain.terrainData.size;
  93. metaData.properties.Add("width", terrainSize.x);
  94. metaData.properties.Add("length", terrainSize.z);
  95. metaData.properties.Add("height", terrainSize.y);
  96. metaData.properties.Add("position", position);
  97. metaData.properties.Add("rotation", rotation);
  98. metaData.properties.Add("scaling", scaling);
  99. metaData.properties.Add("thickness", terrain.terrainData.thickness);
  100. metaData.properties.Add("detailWidth", terrain.terrainData.detailWidth);
  101. metaData.properties.Add("detailHeight", terrain.terrainData.detailHeight);
  102. metaData.properties.Add("heightmapWidth", terrain.terrainData.heightmapWidth);
  103. metaData.properties.Add("heightmapHeight", terrain.terrainData.heightmapHeight);
  104. metaData.properties.Add("wavingGrassAmount", terrain.terrainData.wavingGrassAmount);
  105. metaData.properties.Add("wavingGrassSpeed", terrain.terrainData.wavingGrassSpeed);
  106. metaData.properties.Add("wavingGrassStrength", terrain.terrainData.wavingGrassStrength);
  107. metaData.properties.Add("wavingGrassTint", terrain.terrainData.wavingGrassTint.ToFloat());
  108. metaData.properties.Add("treeInstanceCount", terrain.terrainData.treeInstanceCount);
  109. metaData.properties.Add("treeInstances", treeInstances);
  110. metaData.properties.Add("treePrototypes", treePrototypes);
  111. metaData.properties.Add("physicsState", generator.physicsActive);
  112. metaData.properties.Add("physicsMass", generator.physicsMass);
  113. metaData.properties.Add("physicsFriction", generator.physicsFriction);
  114. metaData.properties.Add("physicsRestitution", generator.physicsRestitution);
  115. metaData.properties.Add("physicsImpostor", (int)generator.physicsImpostor);
  116. metaData.properties.Add("groundTessellation", generator.groundTessellation);
  117. // Generate detailed mesh
  118. ExporterWindow.ReportProgress(progress, "Generating terrain mesh: " + gameObject.name);
  119. BabylonTerrainData terrainMeshData = Unity3D2Babylon.Tools.CreateTerrainData(terrain.terrainData, (int)generator.terrainResolution, transform.localPosition, true);
  120. Tools.GenerateBabylonMeshTerrainData(terrainMeshData, babylonMesh, false, babylonScene, transform);
  121. if (generator.surfaceMaterial != null)
  122. {
  123. babylonMesh.materialId = DumpMaterial(generator.surfaceMaterial, terrain.lightmapIndex, terrain.lightmapScaleOffset, generator.coordinatesIndex).id;
  124. }
  125. // Generate collision heightmap
  126. var terrainCollider = gameObject.GetComponent<TerrainCollider>();
  127. if (terrainCollider != null && terrainCollider.enabled)
  128. {
  129. ExporterWindow.ReportProgress(progress, "Generating terrain heightmap: " + gameObject.name);
  130. float minheight = float.MaxValue;
  131. float maxheight = float.MinValue;
  132. int hwidth = terrain.terrainData.heightmapWidth;
  133. int hheight = terrain.terrainData.heightmapHeight;
  134. float[,] rawHeights = terrain.terrainData.GetHeights(0, 0, hwidth, hheight);
  135. Texture2D heightMap = new Texture2D(hwidth, hheight, TextureFormat.ARGB32, false);
  136. for (int y = 0; y < hheight; y++)
  137. {
  138. for (int x = 0; x < hwidth; x++)
  139. {
  140. float inverted = rawHeights[y, x];
  141. minheight = Mathf.Min(minheight, inverted);
  142. maxheight = Mathf.Max(maxheight, inverted);
  143. }
  144. }
  145. List<Color32> pixels = new List<Color32>();
  146. for (int y = 0; y < hheight; y++)
  147. {
  148. for (int x = 0; x < hwidth; x++)
  149. {
  150. float inverted = rawHeights[y, x];
  151. if (generator.heightmapStrength > 0)
  152. {
  153. float threadhold = minheight + generator.floorThreashold;
  154. if (inverted > threadhold)
  155. {
  156. inverted += (generator.heightmapStrength / 10.0f);
  157. }
  158. }
  159. byte[] packed = BitConverter.GetBytes(inverted);
  160. if (packed != null && packed.Length >= 4)
  161. {
  162. pixels.Add(new Color32(packed[0], packed[1], packed[2], packed[3]));
  163. }
  164. }
  165. }
  166. heightMap.SetPixels32(pixels.ToArray());
  167. heightMap.Apply();
  168. byte[] heightmapBytes = heightMap.EncodeToPNG();
  169. metaData.properties.Add("heightmapBase64", ("data:image/png;base64," + Convert.ToBase64String(heightmapBytes)));
  170. }
  171. }
  172. else
  173. {
  174. UnityEngine.Debug.LogWarning("No valid terrain or generator found for: " + gameObject.name);
  175. }
  176. babylonMesh.metadata = metaData;
  177. babylonScene.MeshesList.Add(babylonMesh);
  178. SceneBuilder.Metadata.properties["hasTerrainMeshes"] = true;
  179. // Animations
  180. ExportAnimations(transform, babylonMesh);
  181. if (IsRotationQuaternionAnimated(babylonMesh))
  182. {
  183. babylonMesh.rotationQuaternion = transform.localRotation.ToFloat();
  184. }
  185. // Lens Flares
  186. ParseLensFlares(gameObject, babylonMesh.id, ref lensFlares);
  187. // Particles Systems
  188. ParseParticleSystems(gameObject, babylonMesh.id, ref particleSystems);
  189. return babylonMesh;
  190. }
  191. private BabylonMesh ConvertUnityMeshToBabylon(Mesh mesh, Transform transform, GameObject gameObject, float progress, ref UnityMetaData metaData, ref List<BabylonExport.Entities.BabylonParticleSystem> particleSystems, ref List<UnityFlareSystem> lensFlares, ref string componentTags, BabylonMesh collisionMesh = null, Collider collider = null)
  192. {
  193. BabylonMesh babylonMesh = new BabylonMesh();
  194. metaData.type = "Mesh";
  195. if (!String.IsNullOrEmpty(componentTags))
  196. {
  197. babylonMesh.tags = componentTags;
  198. }
  199. ExporterWindow.ReportProgress(progress, "Exporting mesh: " + gameObject.name);
  200. babylonMesh.name = gameObject.name;
  201. babylonMesh.id = GetID(transform.gameObject);
  202. var renderer = gameObject.GetComponent<Renderer>();
  203. if (renderer != null)
  204. {
  205. babylonMesh.receiveShadows = renderer.receiveShadows;
  206. }
  207. babylonMesh.parentId = GetParentID(transform);
  208. babylonMesh.position = transform.localPosition.ToFloat();
  209. babylonMesh.rotation = new float[3];
  210. babylonMesh.rotation[0] = transform.localRotation.eulerAngles.x * (float)Math.PI / 180;
  211. babylonMesh.rotation[1] = transform.localRotation.eulerAngles.y * (float)Math.PI / 180;
  212. babylonMesh.rotation[2] = transform.localRotation.eulerAngles.z * (float)Math.PI / 180;
  213. babylonMesh.scaling = transform.localScale.ToFloat();
  214. babylonMesh.checkCollisions = false;
  215. // Collision mesh (With detail mesh fallback)
  216. string collisionMeshId = null;
  217. if (collider != null)
  218. {
  219. if (collisionMesh != null)
  220. {
  221. collisionMeshId = collisionMesh.id;
  222. collisionMesh.parentId = babylonMesh.id;
  223. collisionMesh.visibility = collider.isTrigger ? 0.25f : 0.5f;
  224. collisionMesh.checkCollisions = (exportationOptions.ExportCollisions && collider.isTrigger == false);
  225. }
  226. else
  227. {
  228. babylonMesh.checkCollisions = exportationOptions.ExportCollisions;
  229. }
  230. }
  231. metaData.properties["collisionMeshId"] = collisionMeshId;
  232. if (mesh != null)
  233. {
  234. Tools.GenerateBabylonMeshData(mesh, babylonMesh, babylonScene, transform);
  235. int index = 0;
  236. if (mesh.boneWeights.Length == mesh.vertexCount)
  237. {
  238. babylonMesh.matricesIndices = new int[mesh.vertexCount];
  239. babylonMesh.matricesWeights = new float[mesh.vertexCount * 4];
  240. index = 0;
  241. foreach (BoneWeight bw in mesh.boneWeights)
  242. {
  243. babylonMesh.matricesIndices[index] = (bw.boneIndex3 << 24) | (bw.boneIndex2 << 16) | (bw.boneIndex1 << 8) | bw.boneIndex0;
  244. babylonMesh.matricesWeights[index * 4 + 0] = bw.weight0;
  245. babylonMesh.matricesWeights[index * 4 + 1] = bw.weight1;
  246. babylonMesh.matricesWeights[index * 4 + 2] = bw.weight2;
  247. babylonMesh.matricesWeights[index * 4 + 3] = bw.weight3;
  248. var totalWeight = bw.weight0 + bw.weight1 + bw.weight2 + bw.weight3;
  249. if (Mathf.Abs(totalWeight - 1.0f) > 0.01f)
  250. {
  251. throw new Exception("Total bone weights is not normalized for: " + mesh);
  252. }
  253. index++;
  254. }
  255. }
  256. index = 0;
  257. if (renderer != null && renderer.sharedMaterial != null)
  258. {
  259. // Validate Multi Materials
  260. if (mesh.subMeshCount > 1)
  261. {
  262. BabylonMultiMaterial bMultiMat;
  263. string multiMatName = "";
  264. for (int i = 0; i < renderer.sharedMaterials.Length; i++)
  265. {
  266. multiMatName += renderer.sharedMaterials[i].name;
  267. }
  268. if (!multiMatDictionary.ContainsKey(multiMatName))
  269. {
  270. bMultiMat = new BabylonMultiMaterial
  271. {
  272. materials = new string[mesh.subMeshCount],
  273. id = Guid.NewGuid().ToString(),
  274. name = multiMatName
  275. };
  276. for (int i = 0; i < renderer.sharedMaterials.Length; i++)
  277. {
  278. var sharedMaterial = renderer.sharedMaterials[i];
  279. BabylonMaterial babylonMaterial;
  280. babylonMaterial = DumpMaterial(sharedMaterial, renderer.lightmapIndex, renderer.lightmapScaleOffset);
  281. bMultiMat.materials[i] = babylonMaterial.id;
  282. }
  283. if (mesh.subMeshCount > 1)
  284. {
  285. multiMatDictionary.Add(bMultiMat.name, bMultiMat);
  286. }
  287. }
  288. else
  289. {
  290. bMultiMat = multiMatDictionary[multiMatName];
  291. }
  292. babylonMesh.materialId = bMultiMat.id;
  293. babylonMesh.subMeshes = new BabylonSubMesh[mesh.subMeshCount];
  294. var offset = 0;
  295. for (int materialIndex = 0; materialIndex < mesh.subMeshCount; materialIndex++)
  296. {
  297. var unityTriangles = mesh.GetTriangles(materialIndex);
  298. babylonMesh.subMeshes[materialIndex] = new BabylonSubMesh
  299. {
  300. verticesStart = 0,
  301. verticesCount = mesh.vertexCount,
  302. materialIndex = materialIndex,
  303. indexStart = offset,
  304. indexCount = unityTriangles.Length
  305. };
  306. offset += unityTriangles.Length;
  307. }
  308. }
  309. else
  310. {
  311. babylonMesh.materialId = DumpMaterial(renderer.sharedMaterial, renderer.lightmapIndex, renderer.lightmapScaleOffset).id;
  312. }
  313. }
  314. babylonMesh.metadata = metaData;
  315. babylonScene.MeshesList.Add(babylonMesh);
  316. // Animations
  317. ExportAnimations(transform, babylonMesh);
  318. if (IsRotationQuaternionAnimated(babylonMesh))
  319. {
  320. babylonMesh.rotationQuaternion = transform.localRotation.ToFloat();
  321. }
  322. // Lens Flares
  323. ParseLensFlares(gameObject, babylonMesh.id, ref lensFlares);
  324. // Particles Systems
  325. ParseParticleSystems(gameObject, babylonMesh.id, ref particleSystems);
  326. // Babylon Physics
  327. if (exportationOptions.ExportPhysics)
  328. {
  329. var physics = gameObject.GetComponent<BabylonPhysicsState>();
  330. if (physics != null)
  331. {
  332. babylonMesh.physicsMass = physics.mass;
  333. babylonMesh.physicsFriction = physics.friction;
  334. babylonMesh.physicsRestitution = physics.restitution;
  335. babylonMesh.physicsImpostor = (int)physics.imposter;
  336. }
  337. }
  338. }
  339. return babylonMesh;
  340. }
  341. private BabylonSkeleton ConvertUnitySkeletonToBabylon(Transform[] bones, Matrix4x4[] bindPoses, Transform transform, GameObject gameObject, float progress)
  342. {
  343. ExporterWindow.ReportProgress(progress, "Exporting skeleton: " + gameObject.name);
  344. BabylonSkeleton babylonSkeleton = new BabylonSkeleton();
  345. babylonSkeleton.name = gameObject.name;
  346. babylonSkeleton.id = Math.Abs(GetID(transform.gameObject).GetHashCode());
  347. babylonSkeleton.needInitialSkinMatrix = false;
  348. // Prefilled to keep order and track parents.
  349. int index = 0;
  350. var transformToBoneMap = new Dictionary<Transform, BabylonBone>();
  351. foreach (Transform unityBone in bones)
  352. {
  353. var babylonBone = new BabylonBone();
  354. babylonBone.name = unityBone.name;
  355. babylonBone.index = index;
  356. transformToBoneMap.Add(unityBone, babylonBone);
  357. index++;
  358. }
  359. // Attaches Matrix and parent.
  360. index = 0;
  361. foreach (Transform unityBone in bones)
  362. {
  363. var babylonBone = transformToBoneMap[unityBone];
  364. Matrix4x4 localTransform;
  365. // Unity BindPose is already inverse so take the inverse again :-)
  366. if (transformToBoneMap.ContainsKey(unityBone.parent))
  367. {
  368. var babylonParentBone = transformToBoneMap[unityBone.parent];
  369. babylonBone.parentBoneIndex = babylonParentBone.index;
  370. localTransform = bindPoses[babylonBone.parentBoneIndex] * bindPoses[index].inverse;
  371. }
  372. else
  373. {
  374. babylonBone.parentBoneIndex = -1;
  375. localTransform = bindPoses[index].inverse;
  376. }
  377. transformToBoneMap[unityBone].matrix = new[] {
  378. localTransform[0, 0], localTransform[1, 0], localTransform[2, 0], localTransform[3, 0],
  379. localTransform[0, 1], localTransform[1, 1], localTransform[2, 1], localTransform[3, 1],
  380. localTransform[0, 2], localTransform[1, 2], localTransform[2, 2], localTransform[3, 2],
  381. localTransform[0, 3], localTransform[1, 3], localTransform[2, 3], localTransform[3, 3]
  382. };
  383. index++;
  384. }
  385. // Reorder and attach the skeleton.
  386. babylonSkeleton.bones = transformToBoneMap.Values.OrderBy(b => b.index).ToArray();
  387. babylonScene.SkeletonsList.Add(babylonSkeleton);
  388. return babylonSkeleton;
  389. }
  390. }
  391. }