diff --git a/Game/Assembly-CSharp-vs.csproj b/Game/Assembly-CSharp-vs.csproj
index c9086fb..d26af97 100644
--- a/Game/Assembly-CSharp-vs.csproj
+++ b/Game/Assembly-CSharp-vs.csproj
@@ -66,6 +66,7 @@
+
diff --git a/Game/Assembly-CSharp.csproj b/Game/Assembly-CSharp.csproj
index 1be76cc..f175971 100644
--- a/Game/Assembly-CSharp.csproj
+++ b/Game/Assembly-CSharp.csproj
@@ -66,6 +66,7 @@
+
diff --git a/Game/Assets/Data/Biomes/Mountain.xml b/Game/Assets/Data/Biomes/Mountain.xml
index 5226aa8..ec8ae97 100644
--- a/Game/Assets/Data/Biomes/Mountain.xml
+++ b/Game/Assets/Data/Biomes/Mountain.xml
@@ -1,7 +1,7 @@
Mountain
- 500
+ 400
diff --git a/Game/Assets/Data/Materials.meta b/Game/Assets/Data/Materials.meta
new file mode 100644
index 0000000..fd60a98
--- /dev/null
+++ b/Game/Assets/Data/Materials.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 6fed1937e656c34458559bd2469f5455
+folderAsset: yes
+timeCreated: 1433236765
+licenseType: Free
+DefaultImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Game/Assets/Data/Materials/sidewalk.mat b/Game/Assets/Data/Materials/sidewalk.mat
new file mode 100644
index 0000000..05010f2
Binary files /dev/null and b/Game/Assets/Data/Materials/sidewalk.mat differ
diff --git a/Game/Assets/Data/Materials/sidewalk.mat.meta b/Game/Assets/Data/Materials/sidewalk.mat.meta
new file mode 100644
index 0000000..349cc56
--- /dev/null
+++ b/Game/Assets/Data/Materials/sidewalk.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: ddd2e09300d67f041977284f4d24a0c9
+timeCreated: 1433236871
+licenseType: Free
+NativeFormatImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Game/Assets/Data/Materials/skybox.mat b/Game/Assets/Data/Materials/skybox.mat
new file mode 100644
index 0000000..01043af
Binary files /dev/null and b/Game/Assets/Data/Materials/skybox.mat differ
diff --git a/Game/Assets/Data/Materials/skybox.mat.meta b/Game/Assets/Data/Materials/skybox.mat.meta
new file mode 100644
index 0000000..1a03b1e
--- /dev/null
+++ b/Game/Assets/Data/Materials/skybox.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: bf5942f32f0096844add5527185930f0
+timeCreated: 1433257206
+licenseType: Free
+NativeFormatImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Game/Assets/Data/Materials/street.mat b/Game/Assets/Data/Materials/street.mat
new file mode 100644
index 0000000..574b40e
Binary files /dev/null and b/Game/Assets/Data/Materials/street.mat differ
diff --git a/Game/Assets/Data/Materials/street.mat.meta b/Game/Assets/Data/Materials/street.mat.meta
new file mode 100644
index 0000000..f20aed1
--- /dev/null
+++ b/Game/Assets/Data/Materials/street.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: c62816e268432f840b564c961363e771
+timeCreated: 1433236770
+licenseType: Free
+NativeFormatImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Game/Assets/Data/Textures/sidewalk.jpg b/Game/Assets/Data/Textures/sidewalk.jpg
new file mode 100644
index 0000000..e9cb3bd
Binary files /dev/null and b/Game/Assets/Data/Textures/sidewalk.jpg differ
diff --git a/Game/Assets/Data/Textures/sidewalk.jpg.meta b/Game/Assets/Data/Textures/sidewalk.jpg.meta
new file mode 100644
index 0000000..ecdc552
--- /dev/null
+++ b/Game/Assets/Data/Textures/sidewalk.jpg.meta
@@ -0,0 +1,55 @@
+fileFormatVersion: 2
+guid: ea607de0299a0a54a8650b9ca86fa298
+timeCreated: 1433235814
+licenseType: Free
+TextureImporter:
+ fileIDToRecycleName: {}
+ serializedVersion: 2
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 1
+ linearTexture: 0
+ correctGamma: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 0
+ heightScale: .25
+ normalMapFilter: 0
+ isReadable: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 0
+ cubemapConvolution: 0
+ cubemapConvolutionSteps: 8
+ cubemapConvolutionExponent: 1.5
+ seamlessCubemap: 0
+ textureFormat: -1
+ maxTextureSize: 2048
+ textureSettings:
+ filterMode: -1
+ aniso: -1
+ mipBias: -1
+ wrapMode: -1
+ nPOTScale: 1
+ lightmap: 0
+ rGBM: 0
+ compressionQuality: 50
+ spriteMode: 0
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: .5, y: .5}
+ spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+ spritePixelsToUnits: 100
+ alphaIsTransparency: 0
+ textureType: -1
+ buildTargetSettings: []
+ spriteSheet:
+ sprites: []
+ spritePackingTag:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Game/Assets/Data/Textures/street.gif b/Game/Assets/Data/Textures/street.gif
new file mode 100644
index 0000000..6dd104a
Binary files /dev/null and b/Game/Assets/Data/Textures/street.gif differ
diff --git a/Game/Assets/Data/Textures/street.gif.meta b/Game/Assets/Data/Textures/street.gif.meta
new file mode 100644
index 0000000..86c4a09
--- /dev/null
+++ b/Game/Assets/Data/Textures/street.gif.meta
@@ -0,0 +1,55 @@
+fileFormatVersion: 2
+guid: 5db8f6cba4795374c8d741df2458d00e
+timeCreated: 1433235809
+licenseType: Free
+TextureImporter:
+ fileIDToRecycleName: {}
+ serializedVersion: 2
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 1
+ linearTexture: 0
+ correctGamma: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 0
+ heightScale: .25
+ normalMapFilter: 0
+ isReadable: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 0
+ cubemapConvolution: 0
+ cubemapConvolutionSteps: 8
+ cubemapConvolutionExponent: 1.5
+ seamlessCubemap: 0
+ textureFormat: -1
+ maxTextureSize: 2048
+ textureSettings:
+ filterMode: -1
+ aniso: -1
+ mipBias: -1
+ wrapMode: -1
+ nPOTScale: 1
+ lightmap: 0
+ rGBM: 0
+ compressionQuality: 50
+ spriteMode: 0
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: .5, y: .5}
+ spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+ spritePixelsToUnits: 100
+ alphaIsTransparency: 0
+ textureType: -1
+ buildTargetSettings: []
+ spriteSheet:
+ sprites: []
+ spritePackingTag:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Game/Assets/Main.unity b/Game/Assets/Main.unity
index a97991b..f104383 100644
Binary files a/Game/Assets/Main.unity and b/Game/Assets/Main.unity differ
diff --git a/Game/Assets/Scripts/Generator/RoadGenerator.cs b/Game/Assets/Scripts/Generator/RoadGenerator.cs
index 00ba354..6826cef 100644
--- a/Game/Assets/Scripts/Generator/RoadGenerator.cs
+++ b/Game/Assets/Scripts/Generator/RoadGenerator.cs
@@ -55,7 +55,7 @@ namespace TransportGame.Generator
const float DefaultBranchProbability = 0.2f;
const float DefaultSegmentLength = 24;
const float SteepnessLimit = 10;
- const float SlopeLimit = (float)Math.PI / 6;
+ const float SlopeLimit = (float)Math.PI / 7;
const float RoadSegmentAngleLimit = (float)Math.PI / 4;
const float RoadSnapDistance = 19;
const float MinNodeDistance = 12;
@@ -154,7 +154,7 @@ namespace TransportGame.Generator
private IEnumerable GlobalGoals(RoadSegment segment)
{
Vector2 prevPos = segment.Terminal2.Position;
- Vector2 dir = (segment.Terminal2.Position - segment.Terminal1.Position).Normalized;
+ Vector2 dir = segment.Direction;
bool highway = (segment.LanesTo1 >= 3);
bool highwayBranched = false;
@@ -258,18 +258,14 @@ namespace TransportGame.Generator
}
// Filter & sort the segments by distance
- segmentIds = segmentIds.Distinct().OrderBy(id =>
- {
- var seg = map.RoadNetwork.ArticulationSegments[id];
- var line = new LineSegment(seg.Terminal1.Position, seg.Terminal2.Position);
- return LineSegment.Distance(line, segment.Terminal2Pos);
- });
+ segmentIds = segmentIds.Distinct().OrderBy(id =>
+ LineSegment.Distance(map.RoadNetwork.ArticulationSegments[id].AsLineSegment(), segment.Terminal2Pos));
foreach (var segmentId in segmentIds)
{
var other = map.RoadNetwork.ArticulationSegments[segmentId];
var line1 = new LineSegment(segment.Terminal1.Position, segment.Terminal2Pos);
- var line2 = new LineSegment(other.Terminal1.Position, other.Terminal2.Position);
+ var line2 = other.AsLineSegment();
Vector2? inters = LineSegment.Intersect(line1, line2);
@@ -294,7 +290,7 @@ namespace TransportGame.Generator
// Check angle between intersecting segments
foreach (var intersSeg in other.Terminal2.ArticulationSegments)
{
- float cos = Vector2.Dot((line1.P1 - line1.P0).Normalized, (intersSeg.Terminal2.Position - intersSeg.Terminal1.Position).Normalized);
+ float cos = Vector2.Dot(line1.Direction, intersSeg.Direction);
if (Math.Abs(Math.Acos(cos)) < RoadSegmentAngleLimit)
return false;
}
diff --git a/Game/Assets/Scripts/Model/LineSegment.cs b/Game/Assets/Scripts/Model/LineSegment.cs
index 65d2606..5eb33da 100644
--- a/Game/Assets/Scripts/Model/LineSegment.cs
+++ b/Game/Assets/Scripts/Model/LineSegment.cs
@@ -43,6 +43,14 @@ namespace TransportGame.Model
}
}
+ public Vector2 Direction
+ {
+ get
+ {
+ return (P1 - P0).Normalized;
+ }
+ }
+
public LineSegment(Vector2 p0, Vector2 p1)
: this()
{
diff --git a/Game/Assets/Scripts/Model/Road/RoadSegment.cs b/Game/Assets/Scripts/Model/Road/RoadSegment.cs
index a066e5d..4008dd9 100644
--- a/Game/Assets/Scripts/Model/Road/RoadSegment.cs
+++ b/Game/Assets/Scripts/Model/Road/RoadSegment.cs
@@ -80,6 +80,33 @@ namespace TransportGame.Model.Road
[XmlAttribute("lanesTo1")]
public int LanesTo1 { get; set; }
+ ///
+ /// Gets the direction vector of this road segment
+ ///
+ [XmlIgnore]
+ public Vector2 Direction
+ {
+ get
+ {
+ if (Terminal1Id == -1 || Terminal2Id == -1 || ParentNetwork == null)
+ return Vector2.Zero;
+
+ return (Terminal2.Position - Terminal1.Position).Normalized;
+ }
+ }
+
+ ///
+ /// Gets the road segment as a line segment
+ ///
+ ///
+ public LineSegment AsLineSegment()
+ {
+ if (Terminal1Id == -1 || Terminal2Id == -1 || ParentNetwork == null)
+ throw new InvalidOperationException("Terminals are not initialized.");
+
+ return new LineSegment(Terminal1.Position, Terminal2.Position);
+ }
+
///
/// Initializes road segment
///
diff --git a/Game/Assets/Scripts/Model/Vector2.cs b/Game/Assets/Scripts/Model/Vector2.cs
index 7cdeac2..5a250b2 100644
--- a/Game/Assets/Scripts/Model/Vector2.cs
+++ b/Game/Assets/Scripts/Model/Vector2.cs
@@ -78,6 +78,22 @@ namespace TransportGame.Model
}
}
+ ///
+ /// Gets the normalized vector raised to second power
+ ///
+ ///
+ /// This is less computationally expensive (no need to calculate square root).
+ ///
+ /// Normalized vector
+ public Vector2 NormalizedSq
+ {
+ get
+ {
+ float len2 = LengthSq;
+ return new Vector2(X * X / len2, Y * Y / len2);
+ }
+ }
+
///
/// Rotates vector by given number of radians
///
@@ -123,6 +139,16 @@ namespace TransportGame.Model
return new Vector2(a.X - b.X, a.Y - b.Y);
}
+ ///
+ /// Negation operator
+ ///
+ /// Vector
+ /// Negated vector
+ public static Vector2 operator -(Vector2 a)
+ {
+ return new Vector2(-a.X, -a.Y);
+ }
+
///
/// Multiply by constant
///
@@ -188,6 +214,28 @@ namespace TransportGame.Model
return a.X * b.X + a.Y * b.Y;
}
+ ///
+ /// Returns the magnitude of the cross product between the two vectors (z considered 0)
+ ///
+ /// First vector
+ /// Second vector
+ /// Magnitude of cross product
+ public static float Cross(Vector2 a, Vector2 b)
+ {
+ return (a.X * b.Y) - (a.Y * b.X);
+ }
+
+ ///
+ /// Tests if two vectors are colliniar
+ ///
+ /// a
+ /// b
+ /// True if vectors are colliniar
+ public static bool AreColliniar(Vector2 a, Vector2 b)
+ {
+ return Math.Abs(Cross(a, b)) < 1e-12;
+ }
+
///
/// Gets the vector corresponding with specified angle (in radians)
///
diff --git a/Game/Assets/Scripts/Unity/InitializeScript.cs b/Game/Assets/Scripts/Unity/InitializeScript.cs
index 52e2260..0d0b207 100644
--- a/Game/Assets/Scripts/Unity/InitializeScript.cs
+++ b/Game/Assets/Scripts/Unity/InitializeScript.cs
@@ -13,12 +13,10 @@ public class InitializeScript : MonoBehaviour
// Load configuration
Logger.Info("Loading configuration...");
ConfigurationManager.LoadConfiguration();
- Logger.Info("Finished loading configuration.");
// Load biomes
Logger.Info("Loading biomes...");
BiomeManager.LoadBiomes();
- Logger.Info("Finished loading biomes.");
}
// Update is called once per frame
diff --git a/Game/Assets/Scripts/Unity/RoadMeshGenerator.cs b/Game/Assets/Scripts/Unity/RoadMeshGenerator.cs
new file mode 100644
index 0000000..def8ec3
--- /dev/null
+++ b/Game/Assets/Scripts/Unity/RoadMeshGenerator.cs
@@ -0,0 +1,434 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using TransportGame.Model;
+using TransportGame.Model.Road;
+using TransportGame.Utils;
+using UnityEngine;
+using Vector2 = TransportGame.Model.Vector2;
+
+//#define DEBUG_ROAD_MESH_GENERATOR
+
+namespace TransportGame.Unity
+{
+ class RoadMeshGenerator
+ {
+ #region Constants
+
+ private const float SidewalkWidth = .8f;
+ private const float LaneWidth = 1f;
+
+ private const float RaiseOffset = 0.8f; // Raises road above actual height of terrain
+ private const float SidewalkHeight = 0.1f; // Height of sidewalk
+ private const float SideCoverHeight = 0.1f; // On the sides of the roads, so that we can't see the road from the underside
+
+ #endregion
+
+ #region Private fields
+
+ private Map map;
+
+ private Dictionary segmentTerminal1Limit = new Dictionary();
+ private Dictionary segmentTerminal2Limit = new Dictionary();
+
+ private GameObject parent = new GameObject("roadNetwork");
+
+ #endregion
+
+ public Material RoadMaterial { get; set; }
+ public Material SidewalkMaterial { get; set; }
+
+ public IEnumerable Generate(Map map)
+ {
+ this.map = map;
+
+ List visitedNodes = new List();
+ List visitedSegments = new List();
+ Queue queue = new Queue();
+ queue.Enqueue(map.RoadNetwork.Nodes.First().Value);
+ int i = 0;
+
+ while (queue.Count > 0)
+ {
+ // Pop a node
+ RoadNode node = queue.Dequeue();
+ visitedNodes.Add(node.Id);
+
+ // Generate intersection
+ GenerateIntersection(node);
+
+ // Enqueue
+ foreach (var segment in node.ArticulationSegments)
+ {
+ // Generate segments linked to node that have been visited
+ if (visitedNodes.Contains(segment.Terminal1Id) && visitedNodes.Contains(segment.Terminal2Id))
+ {
+ if (!visitedSegments.Contains(segment.Id))
+ {
+ GenerateSegment(segment);
+ visitedSegments.Add(segment.Id);
+ }
+ }
+ else
+ {
+ if (node == segment.Terminal1)
+ queue.Enqueue(segment.Terminal2);
+ else queue.Enqueue(segment.Terminal1);
+ }
+ }
+
+ // Take a break
+ ++i;
+ if (i % 20 == 0)
+ yield return null;
+ }
+
+ Logger.Info("Finished generating road mesh.");
+ }
+
+ private int LanesTo(RoadSegment segment, RoadNode terminal)
+ {
+ if (segment.Terminal1 == terminal)
+ return segment.LanesTo1;
+
+ if (segment.Terminal2 == terminal)
+ return segment.LanesTo2;
+
+ return -1;
+ }
+
+ private int LanesFrom(RoadSegment segment, RoadNode terminal)
+ {
+ if (segment.Terminal1 == terminal)
+ return segment.LanesTo2;
+
+ if (segment.Terminal2 == terminal)
+ return segment.LanesTo1;
+
+ return -1;
+ }
+
+ private Vector2 DirectionFrom(RoadSegment segment, RoadNode terminal)
+ {
+ return (segment.Terminal1 == terminal) ? segment.Direction : -segment.Direction;
+ }
+
+ public void GenerateIntersection(RoadNode node)
+ {
+ List vertices = new List();
+ List uv = new List();
+ List triangles = new List();
+ int vindex = 0;
+
+ // Helper function
+ Action addV = (v, h) =>
+ {
+ Vector3 v3 = new Vector3(v.Y, map.GetHeight((int)node.X, (int)node.Y) + RaiseOffset + h, v.X);
+ UnityEngine.Vector2 v2 = new UnityEngine.Vector2(v.Y / 3 - v3.y, v.X / 3 - v3.y);
+
+ vertices.Add(v3);
+ uv.Add(v2);
+ };
+
+ // Add current node
+ addV(node.Position, 0);
+ vindex += 1;
+
+ // Sort adjacent segments in trigonometric order
+ var segs = node.ArticulationSegments.OrderBy(segment =>
+ {
+ Vector2 dir = DirectionFrom(segment, node);
+
+ if (dir.X >= 0 && dir.Y >= 0) // First quadrant:
+ return dir.Y; // Y coordinate grows from 0 to 1
+
+ else if (dir.X <= 0 && dir.Y >= 0) // Second quadrant:
+ return 1 + Math.Abs(dir.X); // X goes from 0 to -1
+
+ else if (dir.X <= 0 && dir.Y <= 0) // Third quadrant:
+ return 2 + Math.Abs(dir.Y); // Y goes from 0 to -1
+
+ else return 3 + dir.X; // Fourth quadrant: X grows from 0 to 1
+ }).ToArray();
+
+ Vector2[] sideCrns = new Vector2[segs.Length];
+ Vector2[] strCrns = new Vector2[segs.Length];
+
+ // Holds intersection points for perpendicular on segment that goes through sidewalk corners
+ // 0, 1, 2, 3 - 0 sidewalk, 1 street, 2 street, 3 sidewalk (in trigonometric order). (when first segment)
+ // 4, 5, 6, 7 - the same (when second segment)
+ Vector2[,] p = new Vector2[8, segs.Length];
+
+ // Process each pair of segments
+ for (int i = 0; i < segs.Length; ++i)
+ {
+ // Wrap around
+ int j = i + 1;
+ if (j >= segs.Length)
+ j = 0;
+
+ // Get direction vectors
+ var dir0 = DirectionFrom(segs[i], node);
+ var dir1 = DirectionFrom(segs[j], node);
+ float cross = Vector2.Cross(dir0, dir1);
+
+ // Colliniar vectors
+ if (Math.Abs(cross) < 1e-2)
+ {
+ var dirP = dir0.RotateDeg(90);
+
+ strCrns[i] = node.Position + dirP * LanesTo(segs[i], node) * LaneWidth;
+ sideCrns[i] = strCrns[i] + dirP * SidewalkWidth;
+ }
+
+ // Vectors not colliniar
+ else
+ {
+ Vector2 off0 = dir0 * ((LanesFrom(segs[j], node) * LaneWidth) / cross);
+ Vector2 off1 = dir1 * ((LanesTo(segs[i], node) * LaneWidth) / cross);
+ strCrns[i] = node.Position + off0 + off1;
+ sideCrns[i] = strCrns[i] + dir0 * (SidewalkWidth / cross) + dir1 * (SidewalkWidth / cross);
+ }
+
+ // Calculate perpendicular vectors from sidewalk corner on first segment
+ Vector2 perp0 = dir0.RotateDeg(-90);
+ p[3, i] = sideCrns[i];
+ p[2, i] = p[3, i] + perp0 * SidewalkWidth;
+ p[1, i] = p[2, i] + perp0 * (segs[i].LanesTo1 + segs[i].LanesTo2) * LaneWidth;
+ p[0, i] = p[1, i] + perp0 * SidewalkWidth;
+
+ // Calculate perpendicular vectors from sidewalk corner on second segment
+ Vector2 perp1 = dir1.RotateDeg(90);
+ p[4, j] = sideCrns[i];
+ p[5, j] = p[4, j] + perp1 * SidewalkWidth;
+ p[6, j] = p[5, j] + perp1 * (segs[j].LanesTo1 + segs[j].LanesTo2) * LaneWidth;
+ p[7, j] = p[6, j] + perp1 * SidewalkWidth;
+ }
+
+ if (segs.Length == 1)
+ {
+ var dir = DirectionFrom(segs[0], node);
+ Vector2 perp = dir.RotateDeg(-90);
+ Vector2[] end = new[] {
+ node.Position + perp * (LanesFrom(segs[0], node) * LaneWidth + SidewalkWidth),
+ node.Position + perp * (LanesFrom(segs[0], node) * LaneWidth),
+ node.Position - perp * (LanesTo(segs[0], node) * LaneWidth),
+ node.Position - perp * (LanesTo(segs[0], node) * LaneWidth + SidewalkWidth)
+ };
+
+ if (segs[0].Terminal1 == node)
+ {
+ segmentTerminal1Limit[segs[0].Id] = end;
+ }
+ else
+ {
+ segmentTerminal2Limit[segs[0].Id] = end;
+ }
+ }
+ else
+ {
+
+ // Now we generate for each segment
+ for (int i = 0; i < segs.Length; i++)
+ {
+ // Find furthest perpendicular
+ float dist0 = (p[0, i] - node.Position).LengthSq;
+ float dist1 = (p[4, i] - node.Position).LengthSq;
+
+ int furth = (dist0 > dist1) ? 0 : 4;
+ int cornerLeft = i;
+ int cornerRight = (i - 1 >= 0) ? i - 1 : segs.Length - 1;
+
+ // Add to limit dictionaries
+ if (segs[i].Terminal1 == node)
+ {
+ segmentTerminal1Limit[segs[i].Id] = new[] { p[furth, i], p[furth + 1, i], p[furth + 2, i], p[furth + 3, i] };
+ }
+ else
+ {
+ segmentTerminal2Limit[segs[i].Id] = new[] { p[furth, i], p[furth + 1, i], p[furth + 2, i], p[furth + 3, i] };
+ }
+
+ // Build road
+ // Right side
+ addV(p[furth, i], SidewalkHeight); // 0 - p0 raised
+ addV(sideCrns[cornerRight], SidewalkHeight); // 1 - side corner raised
+ addV(sideCrns[cornerRight], SidewalkHeight - SideCoverHeight); // 2 - side corner cover
+ addV(p[furth, i], SidewalkHeight - SideCoverHeight); // 3 - p0 cover
+ addV(p[furth + 1, i], SidewalkHeight); // 4 - p1 raised
+ addV(strCrns[cornerRight], SidewalkHeight); // 5 - street corner raised
+ addV(strCrns[cornerRight], 0); // 6 - street corner
+ addV(p[furth + 1, i], 0); // 7 - p1
+
+ // Left side
+ addV(p[furth + 2, i], 0); // 8 - p2
+ addV(strCrns[cornerLeft], 0); // 9 - street corner
+ addV(strCrns[cornerLeft], SidewalkHeight); //10 - street corner raised
+ addV(p[furth + 2, i], SidewalkHeight); //11 - p2 raised
+ addV(p[furth + 3, i], SidewalkHeight - SideCoverHeight); //12 - p3 cover
+ addV(sideCrns[cornerLeft], SidewalkHeight - SideCoverHeight); //13 - side corner cover
+ addV(sideCrns[cornerLeft], SidewalkHeight); //14 - side corner raised
+ addV(p[furth + 3, i], SidewalkHeight); //15 - p3 raised
+
+ // Right sidewalk
+ triangles.AddRange(new[] { vindex, vindex + 1, vindex + 2 }); // sidewalk side cover 0
+ triangles.AddRange(new[] { vindex, vindex + 2, vindex + 3 }); // sidewalk side cover 1
+ triangles.AddRange(new[] { vindex, vindex + 4, vindex + 1 }); // sidewalk surface 0
+ triangles.AddRange(new[] { vindex + 4, vindex + 5, vindex + 1 }); // sidewalk surface 1
+ triangles.AddRange(new[] { vindex + 4, vindex + 7, vindex + 6 }); // sidewalk road raise 0
+ triangles.AddRange(new[] { vindex + 4, vindex + 6, vindex + 5 }); // sidewalk road raise 1
+
+ // Road surface
+ triangles.AddRange(new[] { vindex + 8, vindex + 6, vindex + 7 }); // road surface 0
+ triangles.AddRange(new[] { vindex + 8, vindex + 9, vindex + 6 }); // road surface 0
+ triangles.AddRange(new[] { vindex + 6, vindex + 9, 0 }); // road surface 0
+
+ // Left sidewalk
+ triangles.AddRange(new[] { vindex + 11, vindex + 10, vindex + 9 }); // sidewalk road raise 0
+ triangles.AddRange(new[] { vindex + 11, vindex + 9, vindex + 8 }); // sidewalk road raise 1
+ triangles.AddRange(new[] { vindex + 11, vindex + 14, vindex + 10 });// sidewalk surface 0
+ triangles.AddRange(new[] { vindex + 11, vindex + 15, vindex + 14 });// sidewalk surface 1
+ triangles.AddRange(new[] { vindex + 15, vindex + 12, vindex + 13 });// sidewalk side cover 0
+ triangles.AddRange(new[] { vindex + 15, vindex + 13, vindex + 14 });// sidewalk side cover 1
+
+ // update vindex
+ vindex = vertices.Count;
+ }
+
+ // Construct mesh
+ Mesh mesh = new Mesh();
+ mesh.vertices = vertices.ToArray();
+ mesh.triangles = triangles.ToArray();
+ mesh.uv = uv.ToArray();
+ mesh.RecalculateNormals();
+
+ // Construct game object
+ GameObject inters = new GameObject("intersection" + node.Id);
+ inters.transform.parent = parent.transform;
+
+ MeshFilter meshFilter = inters.AddComponent();
+ meshFilter.mesh = mesh;
+
+ MeshRenderer meshRenderer = inters.AddComponent();
+ meshRenderer.materials = new[] { RoadMaterial };
+ }
+
+#if DEBUG_ROAD_MESH_GENERATOR
+ // Debug draw
+ Debug.DrawLine(
+ new Vector3(node.Y, map.GetHeight((int)node.X, (int)node.Y) + 0.5f, node.X),
+ new Vector3(node.Y, map.GetHeight((int)node.X, (int)node.Y) + 1.2f, node.X),
+ Color.white, 10000);
+
+ foreach (var sidewalkCorner in sideCrns)
+ Debug.DrawLine(
+ new Vector3(sidewalkCorner.Y, map.GetHeight((int)sidewalkCorner.X, (int)sidewalkCorner.Y) + 0.5f, sidewalkCorner.X),
+ new Vector3(sidewalkCorner.Y, map.GetHeight((int)sidewalkCorner.X, (int)sidewalkCorner.Y) + 1.1f, sidewalkCorner.X),
+ Color.cyan, 10000);
+
+ foreach (var strCorner in strCrns)
+ Debug.DrawLine(
+ new Vector3(strCorner.Y, map.GetHeight((int)strCorner.X, (int)strCorner.Y) + 0.5f, strCorner.X),
+ new Vector3(strCorner.Y, map.GetHeight((int)strCorner.X, (int)strCorner.Y) + 1.1f, strCorner.X),
+ Color.blue, 10000);
+
+ for (int i = 0; i < segs.Length; i++)
+ for (int j = 0; j < 8; j++)
+ Debug.DrawLine(
+ new Vector3(p[j, i].Y, map.GetHeight((int)p[j, i].X, (int)p[j, i].Y) + 0.5f, p[j, i].X),
+ new Vector3(p[j, i].Y, map.GetHeight((int)p[j, i].X, (int)p[j, i].Y) + 1.0f, p[j, i].X),
+ Color.green, 10000);
+#endif
+ }
+
+ public void GenerateSegment(RoadSegment s)
+ {
+ List vertices = new List();
+ List uv = new List();
+ List triangles = new List();
+
+ // Helper function
+ Action addV = (v, h, term) =>
+ {
+ if (term == 1)
+ h += map.GetHeight((int)s.Terminal1.X, (int)s.Terminal1.Y);
+ else
+ h += map.GetHeight((int)s.Terminal2.X, (int)s.Terminal2.Y);
+
+ Vector3 v3 = new Vector3(v.Y, RaiseOffset + h, v.X);
+ UnityEngine.Vector2 v2 = new UnityEngine.Vector2(v.Y / 3 - v3.y, v.X / 3 - v3.y);
+
+ vertices.Add(v3);
+ uv.Add(v2);
+ };
+
+ Vector2[] pT1 = segmentTerminal1Limit[s.Id];
+ Vector2[] pT2 = segmentTerminal2Limit[s.Id];
+
+ addV(pT1[0], SidewalkHeight - SideCoverHeight, 1);
+ addV(pT1[0], SidewalkHeight, 1);
+ addV(pT1[1], SidewalkHeight, 1);
+ addV(pT1[1], 0, 1);
+ addV(pT1[2], 0, 1);
+ addV(pT1[2], SidewalkHeight, 1);
+ addV(pT1[3], SidewalkHeight, 1);
+ addV(pT1[3], SidewalkHeight - SideCoverHeight, 1);
+
+ addV(pT2[0], SidewalkHeight - SideCoverHeight, 2);
+ addV(pT2[0], SidewalkHeight, 2);
+ addV(pT2[1], SidewalkHeight, 2);
+ addV(pT2[1], 0, 2);
+ addV(pT2[2], 0, 2);
+ addV(pT2[2], SidewalkHeight, 2);
+ addV(pT2[3], SidewalkHeight, 2);
+ addV(pT2[3], SidewalkHeight - SideCoverHeight, 2);
+
+ triangles.AddRange(new[] { 0, 15, 14 }); // sidewalk side cover 0
+ triangles.AddRange(new[] { 0, 14, 1 }); // sidewalk side cover 1
+ triangles.AddRange(new[] { 1, 14, 13 }); // sidewalk surface 0
+ triangles.AddRange(new[] { 1, 13, 2 }); // sidewalk surface 1
+ triangles.AddRange(new[] { 2, 12, 3 }); // sidewalk road raise 0
+ triangles.AddRange(new[] { 2, 13, 12 }); // sidewalk road raise 1
+
+ // Road surface
+ triangles.AddRange(new[] { 3, 12, 11 }); // road surface 0
+ triangles.AddRange(new[] { 3, 11, 4 }); // road surface 0
+
+ // Left sidewalk
+ triangles.AddRange(new[] { 4, 11, 10 }); // sidewalk road raise 0
+ triangles.AddRange(new[] { 4, 10, 5 }); // sidewalk road raise 1
+ triangles.AddRange(new[] { 5, 10, 9 }); // sidewalk surface 0
+ triangles.AddRange(new[] { 5, 9, 6 }); // sidewalk surface 1
+ triangles.AddRange(new[] { 6, 9, 8 }); // sidewalk side cover 0
+ triangles.AddRange(new[] { 6, 8, 7 }); // sidewalk side cover 1
+
+ // Construct mesh
+ Mesh mesh = new Mesh();
+ mesh.vertices = vertices.ToArray();
+ mesh.triangles = triangles.ToArray();
+ mesh.uv = uv.ToArray();
+ mesh.RecalculateNormals();
+
+ // Construct game object
+ GameObject inters = new GameObject("road" + s.Id);
+ inters.transform.parent = parent.transform;
+
+ MeshFilter meshFilter = inters.AddComponent();
+ meshFilter.mesh = mesh;
+
+ MeshRenderer meshRenderer = inters.AddComponent();
+ meshRenderer.materials = new[] { RoadMaterial };
+
+#if DEBUG_ROAD_MESH_GENERATOR
+ // Debug draw
+ Color color = (s.LanesTo1 >= 3) ? Color.magenta : Color.red;
+ Debug.DrawLine(
+ new Vector3(s.Terminal1.Y, map.GetHeight((int)s.Terminal1.X, (int)s.Terminal1.Y) + 1f, s.Terminal1.X),
+ new Vector3(s.Terminal2.Y, map.GetHeight((int)s.Terminal2.X, (int)s.Terminal2.Y) + 1f, s.Terminal2.X),
+ color, 10000);
+#endif
+ }
+ }
+}
diff --git a/Game/Assets/Scripts/Unity/RoadMeshGenerator.cs.meta b/Game/Assets/Scripts/Unity/RoadMeshGenerator.cs.meta
new file mode 100644
index 0000000..6da3234
--- /dev/null
+++ b/Game/Assets/Scripts/Unity/RoadMeshGenerator.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 6515820b5324dbf4baa10c3a1a480eaa
+timeCreated: 1432970903
+licenseType: Free
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Game/Assets/Scripts/Unity/TerrainGeneratorScript.cs b/Game/Assets/Scripts/Unity/TerrainGeneratorScript.cs
index 373babd..3ce771d 100644
--- a/Game/Assets/Scripts/Unity/TerrainGeneratorScript.cs
+++ b/Game/Assets/Scripts/Unity/TerrainGeneratorScript.cs
@@ -3,6 +3,7 @@ using System.Collections;
using System.Linq;
using TransportGame.Generator;
using TransportGame.Model;
+using TransportGame.Unity;
using TransportGame.Utils;
using UnityEngine;
@@ -14,6 +15,7 @@ public class TerrainGeneratorScript : MonoBehaviour
public int TerrainHeight = 1024;
public GameObject WaterObject;
public Texture2D[] Textures;
+ public Material RoadMaterial;
// Use this for initialization
void Start()
@@ -101,55 +103,49 @@ public class TerrainGeneratorScript : MonoBehaviour
}
yield return null;
- // Set up textures
- //SetupSplatmaps(terrainData);
+ // Set up textures
+ Logger.Info("Setting up textures...");
+ foreach (var i in SetupSplatmaps(terrainData))
+ yield return i;
+
+ // Generate road mesh
+ Logger.Info("Generating roads...");
+ RoadMeshGenerator meshGenerator = new RoadMeshGenerator();
+ meshGenerator.RoadMaterial = RoadMaterial;
+ foreach (object i in meshGenerator.Generate(map))
+ yield return i;
// -- DEBUG --
foreach (var center in map.PopulationCenters)
Debug.DrawLine(new Vector3(center.Y, 0, center.X), new Vector3(center.Y, map.Biome.Height, center.X), Color.yellow, 100000);
-
- // Debug - draw lines
- if (map != null && map.RoadNetwork != null)
- {
- foreach (var segment in map.RoadNetwork.ArticulationSegments)
- {
- Color color = (segment.Value.LanesTo1 >= 3) ? Color.magenta : Color.red;
- Debug.DrawLine(new Vector3(segment.Value.Terminal1.Y, map.Biome.Height / 2, segment.Value.Terminal1.X), new Vector3(segment.Value.Terminal2.Y, map.Biome.Height / 2, segment.Value.Terminal2.X), color, 10000);
- }
-
- foreach (var node in map.RoadNetwork.Nodes)
- {
- Debug.DrawLine(new Vector3(node.Value.Y, map.Biome.Height / 2, node.Value.X), new Vector3(node.Value.Y, map.Biome.Height / 2 + 5, node.Value.X), Color.blue, 10000);
- }
- }
}
- private void SetupSplatmaps(TerrainData terrainData)
+ private float[,,] GenerateSplatData(int alphaWidth, int alphaHeight, int alphaLayers)
{
- float[, ,] splatData = new float[terrainData.alphamapWidth, terrainData.alphamapHeight, terrainData.alphamapLayers];
- Expression[] expressions = new Expression[terrainData.alphamapLayers];
+ float[, ,] splatData = new float[alphaWidth, alphaHeight, alphaLayers];
+ Expression[] expressions = new Expression[alphaLayers];
- for (int y = 0; y < terrainData.alphamapHeight; y++)
- for (int x = 0; x < terrainData.alphamapWidth; x++)
+ for (int y = 0; y < alphaHeight; y++)
+ for (int x = 0; x < alphaWidth; x++)
{
- float y_01 = (float)y / (float)terrainData.alphamapHeight;
- float x_01 = (float)x / (float)terrainData.alphamapWidth;
+ float y_01 = (float)y / (float)alphaHeight;
+ float x_01 = (float)x / (float)alphaWidth;
int ix = Mathf.RoundToInt(x_01 * TerrainWidth);
int iy = Mathf.RoundToInt(y_01 * TerrainHeight);
// Get height
float height = map.GetHeight(ix, iy);
-
+
// Get steepness
float steepness = map.GetSteepness(ix, iy);
-
+
// Go through each texture layer
- float[] weights = new float[terrainData.alphamapLayers];
+ float[] weights = new float[alphaLayers];
float sum = 0;
- for (int t = 0; t < terrainData.alphamapLayers; t++)
+ for (int t = 0; t < alphaLayers; t++)
{
// Set up expression
if (expressions[t] == null)
@@ -160,7 +156,7 @@ public class TerrainGeneratorScript : MonoBehaviour
expressions[t].Variables["height"] = height;
expressions[t].Variables["steepness"] = steepness;
- expressions[t].Variables["maxHeight"] = terrainData.size.y;
+ expressions[t].Variables["maxHeight"] = map.Biome.Height;
expressions[t].Variables["waterLevel"] = map.WaterLevel - 1f;
// Obtain weight
@@ -169,12 +165,25 @@ public class TerrainGeneratorScript : MonoBehaviour
}
// Normalize and copy weights
- for (int t = 0; t < terrainData.alphamapLayers; t++)
+ for (int t = 0; t < alphaLayers; t++)
{
splatData[x, y, t] = weights[t] / sum;
}
}
+ return splatData;
+ }
+
+ private IEnumerable SetupSplatmaps(TerrainData terrainData)
+ {
+ float[, ,] splatData = null;
+ int alphaW = terrainData.alphamapWidth;
+ int alphaH = terrainData.alphamapHeight;
+ int alphaL = terrainData.alphamapLayers;
+
+ foreach (var i in Task.RunAsync(() => splatData = GenerateSplatData(alphaW, alphaH, alphaL)))
+ yield return i;
+
terrainData.SetAlphamaps(0, 0, splatData);
}
diff --git a/Game/Assets/Scripts/Utils/Logger.cs b/Game/Assets/Scripts/Utils/Logger.cs
index eb94805..becc142 100644
--- a/Game/Assets/Scripts/Utils/Logger.cs
+++ b/Game/Assets/Scripts/Utils/Logger.cs
@@ -61,6 +61,10 @@ namespace TransportGame.Utils
case Level.Critical:
UnityEngine.Debug.LogError(String.Format(format, args));
break;
+
+ case Level.Info:
+ UnityEngine.Debug.Log(String.Format(format, args));
+ break;
}
}
}
diff --git a/Game/Game-csharp.sln b/Game/Game-csharp.sln
index f961dca..b14d0ca 100644
--- a/Game/Game-csharp.sln
+++ b/Game/Game-csharp.sln
@@ -23,7 +23,7 @@ Global
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
- GlobalSection(MonoDevelopProperties) = preSolution
+ GlobalSection(MonoDevelopProperties) = preSolution
StartupItem = Assembly-CSharp.csproj
Policies = $0
$0.TextStylePolicy = $1
diff --git a/Game/Game.sln b/Game/Game.sln
index 4705d3c..df6f52f 100644
--- a/Game/Game.sln
+++ b/Game/Game.sln
@@ -29,7 +29,7 @@ Global
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
- GlobalSection(MonoDevelopProperties) = preSolution
+ GlobalSection(MonoDevelopProperties) = preSolution
StartupItem = Assembly-CSharp.csproj
Policies = $0
$0.TextStylePolicy = $1
diff --git a/Game/UnityVS.Game.CSharp.csproj b/Game/UnityVS.Game.CSharp.csproj
index 157c132..d0b6ec6 100644
--- a/Game/UnityVS.Game.CSharp.csproj
+++ b/Game/UnityVS.Game.CSharp.csproj
@@ -7,16 +7,14 @@
2.0
{02576F1D-BE9C-CFA7-763D-1EBF63B36977}
Library
-
-
+
Assembly-CSharp
512
{E097FAD1-6243-4DAD-9C02-E9B9EFC3FFC1};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
.NETFramework
v3.5
Unity Subset v3.5
-
-
+
Game:1
StandaloneWindows64:19
5.0.1f1
@@ -90,6 +88,7 @@
+
@@ -109,8 +108,5 @@
-
-
-
-
\ No newline at end of file
+