RealWorldTerrainBuilding.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393
  1. /* INFINITY CODE */
  2. /* https://infinity-code.com */
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using UnityEngine;
  7. namespace InfinityCode.RealWorldTerrain
  8. {
  9. /// <summary>
  10. /// This class contains basic information about the building.
  11. /// </summary>
  12. [AddComponentMenu("")]
  13. [RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))]
  14. public class RealWorldTerrainBuilding : MonoBehaviour
  15. {
  16. /// <summary>
  17. /// The height of the walls.
  18. /// </summary>
  19. public float baseHeight;
  20. /// <summary>
  21. /// Array of base vertices.
  22. /// </summary>
  23. public Vector3[] baseVertices;
  24. /// <summary>
  25. /// Reference to RealWorldTerrainContainer instance.
  26. /// </summary>
  27. public RealWorldTerrainContainer container;
  28. /// <summary>
  29. /// ID of the building
  30. /// </summary>
  31. public string id;
  32. /// <summary>
  33. /// Indicates that roof normals is inverted.
  34. /// </summary>
  35. public bool invertRoof;
  36. /// <summary>
  37. /// Indicates that walls normals is inverted.
  38. /// </summary>
  39. public bool invertWall;
  40. /// <summary>
  41. /// Height of roof.
  42. /// </summary>
  43. public float roofHeight;
  44. /// <summary>
  45. /// Type of roof.
  46. /// </summary>
  47. public RealWorldTerrainRoofType roofType;
  48. /// <summary>
  49. /// Whether to generate the wall?
  50. /// </summary>
  51. public bool generateWall;
  52. /// <summary>
  53. /// Material of the roof.
  54. /// </summary>
  55. public Material roofMaterial;
  56. public float startHeight = 0;
  57. /// <summary>
  58. /// Size of a tile texture in meters.
  59. /// </summary>
  60. public Vector2 tileSize = new Vector2(30, 30);
  61. public Vector2 uvOffset = Vector2.zero;
  62. /// <summary>
  63. /// Material of the wall.
  64. /// </summary>
  65. public Material wallMaterial;
  66. private MeshFilter _meshFilter;
  67. /// <summary>
  68. /// Reference to MeshFilter of the building.
  69. /// </summary>
  70. public MeshFilter meshFilter
  71. {
  72. get
  73. {
  74. if (_meshFilter == null) _meshFilter = GetComponent<MeshFilter>();
  75. return _meshFilter;
  76. }
  77. }
  78. /// <summary>
  79. /// Reference to MeshFilter of roof.
  80. /// </summary>
  81. [Obsolete("Use meshFilter instead.")]
  82. public MeshFilter roof
  83. {
  84. get { return meshFilter; }
  85. }
  86. /// <summary>
  87. /// Reference to MeshFilter of wall.
  88. /// </summary>
  89. [Obsolete("Use meshFilter instead.")]
  90. public MeshFilter wall
  91. {
  92. get { return meshFilter; }
  93. }
  94. private void CreateRoofDome(List<Vector3> vertices, List<int> triangles)
  95. {
  96. Vector3 roofTopPoint = Vector3.zero;
  97. roofTopPoint = vertices.Aggregate(roofTopPoint, (current, point) => current + point) / vertices.Count;
  98. roofTopPoint.y = (baseHeight + roofHeight) * container.scale.y;
  99. int vIndex = vertices.Count;
  100. for (int i = 0; i < vertices.Count; i++)
  101. {
  102. int p1 = i;
  103. int p2 = i + 1;
  104. if (p2 >= vertices.Count) p2 -= vertices.Count;
  105. triangles.AddRange(new[] { p1, p2, vIndex });
  106. }
  107. vertices.Add(roofTopPoint);
  108. }
  109. private void CreateRoofMesh(List<Vector3> vertices, out List<Vector2> uv, out List<int> triangles)
  110. {
  111. List<Vector2> roofPoints = CreateRoofVertices(vertices);
  112. triangles = CreateRoofTriangles(vertices, roofPoints);
  113. if (invertRoof) triangles.Reverse();
  114. float minX = vertices.Min(p => p.x);
  115. float minZ = vertices.Min(p => p.z);
  116. float maxX = vertices.Max(p => p.x);
  117. float maxZ = vertices.Max(p => p.z);
  118. float offX = maxX - minX;
  119. float offZ = maxZ - minZ;
  120. uv = vertices.Select(v => new Vector2((v.x - minX) / offX, (v.z - minZ) / offZ)).ToList();
  121. }
  122. private List<int> CreateRoofTriangles(List<Vector3> vertices, List<Vector2> roofPoints)
  123. {
  124. List<int> triangles = new List<int>();
  125. if (roofType == RealWorldTerrainRoofType.flat)
  126. {
  127. int[] trs = RealWorldTerrainTriangulator.Triangulate(roofPoints);
  128. if (trs != null) triangles.AddRange(trs);
  129. }
  130. else if (roofType == RealWorldTerrainRoofType.dome)
  131. {
  132. CreateRoofDome(vertices, triangles);
  133. }
  134. return triangles;
  135. }
  136. private List<Vector2> CreateRoofVertices(List<Vector3> vertices)
  137. {
  138. Vector3[] targetVertices = new Vector3[baseVertices.Length];
  139. Array.Copy(baseVertices, targetVertices, baseVertices.Length);
  140. if (container.prefs.buildingBottomMode == RealWorldTerrainBuildingBottomMode.followTerrain)
  141. {
  142. Vector3 tp = transform.position;
  143. RealWorldTerrainItem terrainItem = container.GetItemByWorldPosition(baseVertices[0] + tp);
  144. if (terrainItem != null)
  145. {
  146. TerrainData t = terrainItem.terrainData;
  147. Vector3 offset = tp - terrainItem.transform.position;
  148. for (int i = 0; i < targetVertices.Length; i++)
  149. {
  150. Vector3 v = targetVertices[i];
  151. Vector3 localPos = offset + v;
  152. float y = t.GetInterpolatedHeight(localPos.x / t.size.x, localPos.z / t.size.z);
  153. v.y = terrainItem.transform.position.y + y - tp.y;
  154. targetVertices[i] = v;
  155. }
  156. }
  157. }
  158. List<Vector2> roofPoints = new List<Vector2>();
  159. float topPoint = targetVertices.Max(v => v.y) + baseHeight * container.scale.y;
  160. foreach (Vector3 p in targetVertices)
  161. {
  162. Vector3 tv = new Vector3(p.x, topPoint, p.z);
  163. Vector2 rp = new Vector2(p.x, p.z);
  164. vertices.Add(tv);
  165. roofPoints.Add(rp);
  166. }
  167. return roofPoints;
  168. }
  169. private void CreateWallMesh(List<Vector3> vertices, List<Vector2> uv, out List<int> triangles)
  170. {
  171. List<Vector3> wv = new List<Vector3>();
  172. List<Vector2> wuv = new List<Vector2>();
  173. bool reversed = CreateWallVertices(wv, wuv);
  174. if (invertWall) reversed = !reversed;
  175. triangles = CreateWallTriangles(wv, vertices.Count, reversed);
  176. vertices.AddRange(wv);
  177. uv.AddRange(wuv);
  178. }
  179. private List<int> CreateWallTriangles(List<Vector3> vertices, int offset, bool reversed)
  180. {
  181. List<int> triangles = new List<int>();
  182. for (int i = 0; i < vertices.Count / 4; i++)
  183. {
  184. int p1 = i * 4;
  185. int p2 = i * 4 + 2;
  186. int p3 = i * 4 + 3;
  187. int p4 = i * 4 + 1;
  188. if (p2 >= vertices.Count) p2 -= vertices.Count;
  189. if (p3 >= vertices.Count) p3 -= vertices.Count;
  190. p1 += offset;
  191. p2 += offset;
  192. p3 += offset;
  193. p4 += offset;
  194. if (reversed)
  195. {
  196. triangles.AddRange(new[] { p1, p4, p3, p1, p3, p2 });
  197. }
  198. else
  199. {
  200. triangles.AddRange(new[] { p2, p3, p1, p3, p4, p1 });
  201. }
  202. }
  203. return triangles;
  204. }
  205. private bool CreateWallVertices(List<Vector3> vertices, List<Vector2> uv)
  206. {
  207. Vector3[] targetVertices = new Vector3[baseVertices.Length];
  208. Array.Copy(baseVertices, targetVertices, baseVertices.Length);
  209. if (container.prefs.buildingBottomMode == RealWorldTerrainBuildingBottomMode.followTerrain)
  210. {
  211. Vector3 tp = transform.position;
  212. RealWorldTerrainItem terrainItem = container.GetItemByWorldPosition(baseVertices[0] + tp);
  213. if (terrainItem != null)
  214. {
  215. TerrainData t = terrainItem.terrainData;
  216. Vector3 offset = tp - terrainItem.transform.position;
  217. for (int i = 0; i < targetVertices.Length; i++)
  218. {
  219. Vector3 v = targetVertices[i];
  220. Vector3 localPos = offset + v;
  221. float y = t.GetInterpolatedHeight(localPos.x / t.size.x, localPos.z / t.size.z);
  222. v.y = terrainItem.transform.position.y + y - tp.y;
  223. targetVertices[i] = v;
  224. }
  225. }
  226. }
  227. float topPoint = targetVertices.Max(v => v.y) + baseHeight * container.scale.y;
  228. float startY = startHeight * container.scale.y;
  229. float offsetY = startY < 0 ? startY : 0;
  230. for (int i = 0; i < targetVertices.Length; i++)
  231. {
  232. Vector3 p1 = targetVertices[i];
  233. Vector3 p2 = i < targetVertices.Length - 1 ? targetVertices[i + 1] : targetVertices[0];
  234. if (p1.y < startY) p1.y = startY;
  235. if (p2.y < startY) p2.y = startY;
  236. p1.y += offsetY;
  237. p2.y += offsetY;
  238. vertices.Add(p1);
  239. vertices.Add(new Vector3(p1.x, topPoint, p1.z));
  240. vertices.Add(p2);
  241. vertices.Add(new Vector3(p2.x, topPoint, p2.z));
  242. }
  243. float totalDistance = 0;
  244. float bottomPoint = float.MaxValue;
  245. for (int i = 0; i < vertices.Count / 4; i++)
  246. {
  247. int i1 = Mathf.RoundToInt(Mathf.Repeat(i * 4, vertices.Count));
  248. int i2 = Mathf.RoundToInt(Mathf.Repeat((i + 1) * 4, vertices.Count));
  249. Vector3 v1 = vertices[i1];
  250. Vector3 v2 = vertices[i2];
  251. v1.y = v2.y = 0;
  252. totalDistance += (v1 - v2).magnitude;
  253. if (bottomPoint > targetVertices[i].y) bottomPoint = targetVertices[i].y;
  254. }
  255. Vector3 lv1 = vertices[vertices.Count - 4];
  256. Vector3 lv2 = vertices[0];
  257. lv1.y = lv2.y = 0;
  258. totalDistance += (lv1 - lv2).magnitude;
  259. float currentDistance = 0;
  260. float nextU = 0;
  261. float uMul = totalDistance / tileSize.x;
  262. float vMax = topPoint / tileSize.y;
  263. float vMinMul = container.scale.y * tileSize.y;
  264. for (int i = 0; i < vertices.Count / 4; i++)
  265. {
  266. int i1 = Mathf.RoundToInt(Mathf.Repeat(i * 4, vertices.Count));
  267. int i2 = Mathf.RoundToInt(Mathf.Repeat((i + 1) * 4, vertices.Count));
  268. float curU = nextU;
  269. uv.Add(new Vector2(curU * uMul + uvOffset.x, (vertices[i * 4].y - bottomPoint) / vMinMul + uvOffset.y));
  270. uv.Add(new Vector2(curU * uMul + uvOffset.x, vMax + uvOffset.y));
  271. Vector3 v1 = vertices[i1];
  272. Vector3 v2 = vertices[i2];
  273. v1.y = v2.y = 0;
  274. currentDistance += (v1 - v2).magnitude;
  275. nextU = currentDistance / totalDistance;
  276. uv.Add(new Vector2(nextU * uMul + uvOffset.x, (vertices[i * 4 + 2].y - bottomPoint) / vMinMul + uvOffset.y));
  277. uv.Add(new Vector2(nextU * uMul + uvOffset.x, vMax + uvOffset.y));
  278. }
  279. int southIndex = -1;
  280. float southZ = float.MaxValue;
  281. for (int i = 0; i < targetVertices.Length; i++)
  282. {
  283. if (targetVertices[i].z < southZ)
  284. {
  285. southZ = targetVertices[i].z;
  286. southIndex = i;
  287. }
  288. }
  289. int prevIndex = southIndex - 1;
  290. if (prevIndex < 0) prevIndex = targetVertices.Length - 1;
  291. int nextIndex = southIndex + 1;
  292. if (nextIndex >= targetVertices.Length) nextIndex = 0;
  293. float angle1 = RealWorldTerrainUtils.Angle2D(targetVertices[southIndex], targetVertices[nextIndex]);
  294. float angle2 = RealWorldTerrainUtils.Angle2D(targetVertices[southIndex], targetVertices[prevIndex]);
  295. return angle1 < angle2;
  296. }
  297. public void Generate()
  298. {
  299. Mesh mesh;
  300. if (meshFilter.sharedMesh != null) mesh = meshFilter.sharedMesh;
  301. else
  302. {
  303. mesh = new Mesh();
  304. mesh.name = "Building " + id;
  305. mesh.subMeshCount = 2;
  306. meshFilter.sharedMesh = mesh;
  307. }
  308. List<Vector3> vertices = new List<Vector3>();
  309. List<Vector2> uv;
  310. List<int> roofTriangles;
  311. List<int> wallTriangles = null;
  312. CreateRoofMesh(vertices, out uv, out roofTriangles);
  313. if (generateWall) CreateWallMesh(vertices, uv, out wallTriangles);
  314. mesh.SetVertices(vertices);
  315. mesh.SetUVs(0, uv);
  316. mesh.SetTriangles(roofTriangles, 0);
  317. if (generateWall) mesh.SetTriangles(wallTriangles, 1);
  318. mesh.RecalculateNormals();
  319. mesh.RecalculateBounds();
  320. GetComponent<MeshRenderer>().materials = new[]
  321. {
  322. roofMaterial,
  323. wallMaterial,
  324. };
  325. }
  326. }
  327. }