#region "Imports" using UnityEngine; using System.Collections.Generic; using System.IO; using System.Text; #endregion namespace RoadArchitect { //Generic http://www.fhwa.dot.gov/bridge/bridgerail/br053504.cfm public enum RailingTypeEnum { None, Generic1, Generic2, K_Rail, WBeam }; public enum RailingSubTypeEnum { Both, Left, Right }; public enum SignPlacementSubTypeEnum { Center, Left, Right }; public enum CenterDividerTypeEnum { None, K_Rail, KRail_Blinds, Wire, Markers }; public enum EndCapTypeEnum { None, WBeam, Barrels3Static, Barrels3Rigid, Barrels7Static, Barrels7Rigid }; public enum RoadUpdateTypeEnum { Full, Intersection, Railing, CenterDivider, Bridges }; public enum AxisTypeEnum { X, Y, Z }; public static class RoadUtility { public const string FileSepString = "\n#### RoadArchitect ####\n"; public const string FileSepStringCRLF = "\r\n#### RoadArchitect ####\r\n"; /// Returns closest terrain to _vect public static Terrain GetTerrain(Vector3 _vect) { Terrain terrain; //Sphere cast 5m first. Then raycast down 1000m, then up 1000m. Collider[] colliders = Physics.OverlapSphere(_vect, 10f); if (colliders != null) { int collidersLength = colliders.Length; for (int index = 0; index < collidersLength; index++) { terrain = colliders[index].transform.GetComponent(); if (terrain) { colliders = null; return terrain; } } colliders = null; } RaycastHit[] hits; hits = Physics.RaycastAll(_vect, Vector3.down, 1000f); int hitsLength = 0; if (hits != null) { hitsLength = hits.Length; for (int index = 0; index < hitsLength; index++) { terrain = hits[index].collider.transform.GetComponent(); if (terrain) { hits = null; return terrain; } } hits = null; } hits = Physics.RaycastAll(_vect, Vector3.up, 1000f); if (hits != null) { hitsLength = hits.Length; for (int i = 0; i < hitsLength; i++) { terrain = hits[i].collider.transform.GetComponent(); if (terrain) { hits = null; return terrain; } } hits = null; } return null; } #region "Terrain history" public static void ConstructRoadStoreTerrainHistory(ref Road _road) { Object[] allTerrains = GameObject.FindObjectsOfType(); HashSet terrainIDs = new HashSet(); foreach (RoadTerrain terrain in allTerrains) { terrainIDs.Add(terrain.UID); } if (_road.TerrainHistory == null) { _road.TerrainHistory = new List(); } if (_road.TerrainHistory.Count > 0) { //Delete unnecessary terrain histories: foreach (TerrainHistoryMaker THMaker in _road.TerrainHistory) { if (!terrainIDs.Contains(THMaker.terrainID)) { THMaker.Nullify(); _road.TerrainHistory.Remove(THMaker); } } } TerrainHistoryMaker TH; RoadTerrain roadTerrain; foreach (Terraforming.TempTerrainData TTD in _road.EditorTTDList) { roadTerrain = null; //Get terrainID: foreach (RoadTerrain terrain in allTerrains) { if (terrain.UID == TTD.uID) { roadTerrain = terrain; } } if (roadTerrain == null) { continue; } TH = null; int THCount = _road.TerrainHistory.Count; bool isContainingTID = false; for (int index = 0; index < THCount; index++) { if (_road.TerrainHistory[index].terrainID == roadTerrain.UID) { isContainingTID = true; TH = _road.TerrainHistory[index]; break; } } if (!isContainingTID) { TerrainHistoryMaker THMaker = new TerrainHistoryMaker(); THMaker.terrainID = roadTerrain.UID; _road.TerrainHistory.Add(THMaker); TH = THMaker; } if (TH == null) { continue; } //Heights: if (_road.isHeightModificationEnabled) { if (TTD.cX != null && TTD.cY != null) { TH.x1 = new int[TTD.Count]; System.Array.Copy(TTD.cX, 0, TH.x1, 0, TTD.Count); TH.y1 = new int[TTD.Count]; System.Array.Copy(TTD.cY, 0, TH.y1, 0, TTD.Count); TH.height = new float[TTD.Count]; System.Array.Copy(TTD.oldH, 0, TH.height, 0, TTD.Count); TH.Count = TTD.Count; TH.heightmapResolution = TTD.TerrainMaxIndex; } } else { TH.x1 = null; TH.y1 = null; TH.height = null; TH.Count = 0; } //Details: if (_road.isDetailModificationEnabled) { int TotalSize = 0; for (int i = 0; i < TTD.DetailLayersCount; i++) { TotalSize += TTD.detailsCount[i]; } TH.detailsX = new int[TotalSize]; TH.detailsY = new int[TotalSize]; TH.detailsOldValue = new int[TotalSize]; int RunningIndex = 0; int cLength; for (int index = 0; index < TTD.DetailLayersCount; index++) { cLength = TTD.detailsCount[index]; if (cLength < 1) { continue; } System.Array.Copy(TTD.DetailsX[index].ToArray(), 0, TH.detailsX, RunningIndex, cLength); System.Array.Copy(TTD.DetailsY[index].ToArray(), 0, TH.detailsY, RunningIndex, cLength); System.Array.Copy(TTD.OldDetailsValue[index].ToArray(), 0, TH.detailsOldValue, RunningIndex, cLength); RunningIndex += TTD.detailsCount[index]; } //TH.detailsX = TTD.detailsX; //TH.detailsY = TTD.detailsY; //TH.detailsOldValue = TTD.OldDetailsValue; TH.detailsCount = TTD.detailsCount; TH.detailLayersCount = TTD.DetailLayersCount; } else { TH.detailsX = null; TH.detailsY = null; TH.detailsOldValue = null; TH.detailsCount = null; TH.detailLayersCount = 0; } //Trees: if (_road.isTreeModificationEnabled) { if (TTD.TreesOld != null) { TH.MakeRATrees(ref TTD.TreesOld); TTD.TreesOld.Clear(); TTD.TreesOld = null; TH.treesCount = TTD.treesCount; } } else { TH.oldTrees = null; TH.treesCount = 0; } } } /// Clears the terrain history of _road public static void ResetTerrainHistory(ref Road _road) { if (_road.TerrainHistory != null) { _road.TerrainHistory.Clear(); _road.TerrainHistory = null; } } #endregion public static void SaveNodeObjects(ref Splination.SplinatedMeshMaker[] _splinatedObjects, ref EdgeObjects.EdgeObjectMaker[] _edgeObjects, ref WizardObject _wizardObj) { //Splinated objects first: RootUtils.CheckCreateSpecialLibraryDirs(); string libraryPath = RootUtils.GetDirLibrary(); string filePath = Path.Combine(Path.Combine(libraryPath, "Groups"), _wizardObj.fileName + ".rao"); if (_wizardObj.isDefault) { filePath = Path.Combine(Path.Combine(libraryPath, "Groups"), "Default"); filePath = Path.Combine(filePath, _wizardObj.fileName + ".rao"); } StringBuilder builder = new StringBuilder(32768); //Wizard object: builder.Append(_wizardObj.ConvertToString()); builder.Append(FileSepString); int sCount = _splinatedObjects.Length; Splination.SplinatedMeshMaker SMM = null; for (int index = 0; index < sCount; index++) { SMM = _splinatedObjects[index]; builder.Append(SMM.ConvertToString()); builder.Append(FileSepString); } int eCount = _edgeObjects.Length; EdgeObjects.EdgeObjectMaker EOM = null; for (int index = 0; index < eCount; index++) { EOM = _edgeObjects[index]; builder.Append(EOM.ConvertToString()); builder.Append(FileSepString); } File.WriteAllText(filePath, builder.ToString()); } /// Loads splinated objects for this _node public static void LoadNodeObjects(string _fileName, SplineN _node, bool _isDefault = false, bool _isBridge = false) { string filePath; RootUtils.CheckCreateSpecialLibraryDirs(); string libraryPath = RootUtils.GetDirLibrary(); if (_isDefault) { filePath = Path.Combine(Path.Combine(libraryPath, "Groups"), "Default"); filePath = Path.Combine(filePath, _fileName + ".rao"); } else { filePath = Path.Combine(Path.Combine(libraryPath, "Groups"), _fileName + ".rao"); } string fileData = File.ReadAllText(filePath); string[] seperators = new string[2]; seperators[0] = FileSepString; seperators[1] = FileSepStringCRLF; string[] fileSplitted = fileData.Split(seperators, System.StringSplitOptions.RemoveEmptyEntries); Splination.SplinatedMeshMaker SMM; Splination.SplinatedMeshMaker.SplinatedMeshLibraryMaker SLM; EdgeObjects.EdgeObjectMaker EOM; EdgeObjects.EdgeObjectMaker.EdgeObjectLibraryMaker ELM; int fileSplitCount = fileSplitted.Length; for (int index = 0; index < fileSplitCount; index++) { SLM = Splination.SplinatedMeshMaker.SLMFromData(fileSplitted[index]); if (SLM != null) { SMM = _node.AddSplinatedObject(); SMM.LoadFromLibraryBulk(ref SLM); SMM.isToggled = false; if (_isBridge && _node.isBridgeStart && _node.isBridgeMatched && _node.bridgeCounterpartNode != null) { SMM.StartTime = _node.time; SMM.EndTime = _node.bridgeCounterpartNode.time; SMM.StartPos = _node.spline.GetSplineValue(SMM.StartTime); SMM.EndPos = _node.spline.GetSplineValue(SMM.EndTime); } continue; } ELM = EdgeObjects.EdgeObjectMaker.ELMFromData(fileSplitted[index]); if (ELM != null) { EOM = _node.AddEdgeObject(); EOM.LoadFromLibraryBulk(ref ELM); EOM.isToggled = false; if (!EOM.isSingle && _isBridge && _node.isBridgeStart && _node.isBridgeMatched && _node.bridgeCounterpartNode != null) { EOM.startTime = _node.time; EOM.endTime = _node.bridgeCounterpartNode.time; EOM.startPos = _node.spline.GetSplineValue(EOM.startTime); EOM.endPos = _node.spline.GetSplineValue(EOM.endTime); } else if (EOM.isSingle && _isBridge && _node.bridgeCounterpartNode != null && _node.isBridgeStart) { float tDist = (EOM.singleOnlyBridgePercent * (_node.bridgeCounterpartNode.dist - _node.dist) + _node.dist); EOM.singlePosition = _node.spline.TranslateDistBasedToParam(tDist); EOM.startPos = _node.spline.GetSplineValue(EOM.singlePosition); EOM.endPos = _node.spline.GetSplineValue(EOM.singlePosition); } continue; } } _node.SetupSplinatedMeshes(); _node.SetupEdgeObjects(); } #region "Splat maps" /// Returns a splat map texture encoded as png public static byte[] MakeSplatMap(Terrain _terrain, Color _BG, Color _FG, int _width, int _height, float _splatWidth, bool _isSkippingBridge, bool _isSkippingTunnel, string _roadUID = "") { Texture2D texture = new Texture2D(_width, _height, TextureFormat.RGB24, false); //Set background color: Color[] backgroundColors = new Color[_width * _height]; int backgroundCount = backgroundColors.Length; for (int i = 0; i < backgroundCount; i++) { backgroundColors[i] = _BG; } texture.SetPixels(0, 0, _width, _height, backgroundColors); Object[] roadObjects; if (_roadUID != "") { roadObjects = new Object[1]; Object[] roads = GameObject.FindObjectsOfType(); foreach (Road road in roads) { if (string.CompareOrdinal(road.UID, _roadUID) == 0) { roadObjects[0] = road; break; } } } else { roadObjects = GameObject.FindObjectsOfType(); } Vector3 terrainPos = _terrain.transform.position; Vector3 terrainSize = _terrain.terrainData.size; foreach (Road road in roadObjects) { SplineC spline = road.spline; int tCount = spline.RoadDefKeysArray.Length; Vector3 POS1 = default(Vector3); Vector3 POS2 = default(Vector3); Vector3 tVect = default(Vector3); Vector3 tVect2 = default(Vector3); Vector3 lVect1 = default(Vector3); Vector3 lVect2 = default(Vector3); Vector3 rVect1 = default(Vector3); Vector3 rVect2 = default(Vector3); int x1, y1; int[] tX = new int[4]; int[] tY = new int[4]; int MinX = -1; int MaxX = -1; int MinY = -1; int MaxY = -1; int xDiff = -1; int yDiff = -1; float p1 = 0f; float p2 = 0f; bool isXBad = false; bool isYBad = false; for (int i = 0; i < (tCount - 1); i++) { isXBad = false; isYBad = false; p1 = spline.TranslateInverseParamToFloat(spline.RoadDefKeysArray[i]); p2 = spline.TranslateInverseParamToFloat(spline.RoadDefKeysArray[i + 1]); //Skip bridges: if (_isSkippingBridge) { if (spline.IsInBridgeTerrain(p1)) { continue; } } //Skip tunnels: if (_isSkippingTunnel) { if (spline.IsInTunnelTerrain(p1)) { continue; } } spline.GetSplineValueBoth(p1, out tVect, out POS1); spline.GetSplineValueBoth(p2, out tVect2, out POS2); lVect1 = (tVect + new Vector3(_splatWidth * -POS1.normalized.z, 0, _splatWidth * POS1.normalized.x)); rVect1 = (tVect + new Vector3(_splatWidth * POS1.normalized.z, 0, _splatWidth * -POS1.normalized.x)); lVect2 = (tVect2 + new Vector3(_splatWidth * -POS2.normalized.z, 0, _splatWidth * POS2.normalized.x)); rVect2 = (tVect2 + new Vector3(_splatWidth * POS2.normalized.z, 0, _splatWidth * -POS2.normalized.x)); TranslateWorldVectToCustom(_width, _height, lVect1, ref terrainPos, ref terrainSize, out x1, out y1); tX[0] = x1; tY[0] = y1; TranslateWorldVectToCustom(_width, _height, rVect1, ref terrainPos, ref terrainSize, out x1, out y1); tX[1] = x1; tY[1] = y1; TranslateWorldVectToCustom(_width, _height, lVect2, ref terrainPos, ref terrainSize, out x1, out y1); tX[2] = x1; tY[2] = y1; TranslateWorldVectToCustom(_width, _height, rVect2, ref terrainPos, ref terrainSize, out x1, out y1); tX[3] = x1; tY[3] = y1; MinX = Mathf.Min(tX); MaxX = Mathf.Max(tX); MinY = Mathf.Min(tY); MaxY = Mathf.Max(tY); if (MinX < 0) { MinX = 0; isXBad = true; } if (MaxX < 0) { MaxX = 0; isXBad = true; } if (MinY < 0) { MinY = 0; isYBad = true; } if (MaxY < 0) { MaxY = 0; isYBad = true; } if (MinX > (_width - 1)) { MinX = (_width - 1); isXBad = true; } if (MaxX > (_width - 1)) { MaxX = (_width - 1); isXBad = true; } if (MinY > (_height - 1)) { MinY = (_height - 1); isYBad = true; } if (MaxY > (_height - 1)) { MaxY = (_height - 1); isYBad = true; } if (isXBad && isYBad) { continue; } xDiff = MaxX - MinX; yDiff = MaxY - MinY; Color[] colors = new Color[xDiff * yDiff]; int colorCount = colors.Length; for (int j = 0; j < colorCount; j++) { colors[j] = _FG; } if (xDiff > 0 && yDiff > 0) { texture.SetPixels(MinX, MinY, xDiff, yDiff, colors); } } } texture.Apply(); byte[] imageBytes = texture.EncodeToPNG(); Object.DestroyImmediate(texture); return imageBytes; } /// Writes _vect location into _x1 and _y1 relative to the terrain on a 2D map private static void TranslateWorldVectToCustom(int _width, int _height, Vector3 _vect, ref Vector3 _pos, ref Vector3 _size, out int _x1, out int _y1) { //Get the normalized position of this game object relative to the terrain: _vect -= _pos; _vect.x = _vect.x / _size.x; _vect.z = _vect.z / _size.z; //Get the position of the terrain heightmap where this game object is: _x1 = (int) (_vect.x * _width); _y1 = (int) (_vect.z * _height); } #endregion } }