private void TriangulateWithoutRiver(HexDirection direction, HexCell cell, Vector3 centre, EdgeVertices e) { TriangulateEdgeFan(cell.Position, e, cell.Index); if (cell.HasRoads) { var interpolators = GetRoadInterpolators(direction, cell); TriangulateRoad(centre, Vector3.Lerp(centre, e.v1, interpolators.x), Vector3.Lerp(centre, e.v5, interpolators.y), e, cell.HasRoadThroughEdge(direction), cell.Index); } }
// triangulates one of the six cores of a hex cell // and, if the conditions are met, the bridge and corner on that side private void TriangulateCellDirection(HexDirection direction, HexCell cell) { var e = new EdgeVertices( cell.Position + HexMetrics.GetFirstSolidCorner(direction), cell.Position + HexMetrics.GetSecondSolidCorner(direction) ); if (!cell.IsUnderwater && cell.HasRiver) { if (cell.HasRiverThroughEdge(direction)) { e.v3.y = cell.StreamBedY; if (cell.HasRiverBeginOrEnd) { TriangulateWithRiverBeginOrEnd(direction, cell, cell.Position, e); } else { TriangulateWithRiver(direction, cell, cell.Position, e); } } else { TriangulateAdjacentToRiver(direction, cell, cell.Position, e); } } else { TriangulateWithoutRiver(direction, cell, cell.Position, e); if (!cell.IsUnderwater && !cell.HasRoadThroughEdge(direction)) { Features.AddFeature(cell, (cell.Position + e.v1 + e.v5) * (1f / 3)); } } if (direction <= HexDirection.SE) { TriangulateConnection(direction, cell, e); } if (cell.IsUnderwater) { TriangulateWater(direction, cell, cell.Position); } }
private void TriangulateRoadAdjacentToRiver(HexDirection direction, HexCell cell, Vector3 centre, EdgeVertices e) { var edgeRoad = cell.HasRoadThroughEdge(direction); var previousRiver = cell.HasRiverThroughEdge(direction.Previous()); var nextRiver = cell.HasRiverThroughEdge(direction.Next()); var interpolators = GetRoadInterpolators(direction, cell); var roadCentre = centre; if (cell.HasRiverBeginOrEnd) // pointy end of rivers { roadCentre += HexMetrics.GetSolidEdgeMiddle(cell.RiverBeginOrEndDirection.Opposite()) * 1f / 3; } else if (cell.IncomingRiver == cell.OutgoingRiver.Opposite()) // straight rivers { Vector3 corner; if (previousRiver) { if (!edgeRoad && !cell.HasRoadThroughEdge(direction.Next())) { return; // isolated on one side of the river } corner = HexMetrics.GetSecondSolidCorner(direction); } else { if (!edgeRoad && !cell.HasRoadThroughEdge(direction.Previous())) { return; // isolated on one side of the river } corner = HexMetrics.GetFirstSolidCorner(direction); } roadCentre += corner / 2; if (cell.IncomingRiver == direction.Next() && (cell.HasRoadThroughEdge(direction.Next2()) || cell.HasRoadThroughEdge(direction.Opposite()))) { Features.AddBridge(roadCentre, centre - corner / 2); } centre += corner / 4; } else if (cell.IncomingRiver == cell.OutgoingRiver.Previous()) // zigzag river orientation 1 { roadCentre -= HexMetrics.GetSecondSolidCorner(cell.IncomingRiver) * 0.2f; } else if (cell.IncomingRiver == cell.OutgoingRiver.Next()) // zigzag river orientation 2 { roadCentre -= HexMetrics.GetFirstSolidCorner(cell.IncomingRiver) * 0.2f; } else if (previousRiver && nextRiver) // inside of curved river { if (!edgeRoad) { return; // isolated road - i.e road didn't come from this edge, and doesn't connect out either. } var offset = HexMetrics.GetSolidEdgeMiddle(direction) * HexMetrics.InnerToOuter; roadCentre += offset * 0.7f; centre += offset / 2; } else // outside of curved river { HexDirection middle; if (previousRiver) { middle = direction.Next(); } else if (nextRiver) { middle = direction.Previous(); } else { middle = direction; } if (!cell.HasRoadThroughEdge(middle) && !cell.HasRoadThroughEdge(middle.Previous()) && !cell.HasRoadThroughEdge(middle.Next())) { return; // prune orphaned rivers on the inside of a curve } var offset = HexMetrics.GetSolidEdgeMiddle(middle); roadCentre += offset / 4; if (direction == middle && cell.HasRoadThroughEdge(direction.Opposite())) { Features.AddBridge(roadCentre, centre - offset * (HexMetrics.InnerToOuter * 0.7f)); } } var ml = Vector3.Lerp(roadCentre, e.v1, interpolators.x); var mr = Vector3.Lerp(roadCentre, e.v5, interpolators.y); TriangulateRoad(roadCentre, ml, mr, e, edgeRoad, cell.Index); if (previousRiver) { TriangulateRoadEdge(roadCentre, centre, ml, cell.Index); } if (nextRiver) { TriangulateRoadEdge(roadCentre, mr, centre, cell.Index); } }
private void TriangulateConnection(HexDirection direction, HexCell cell, EdgeVertices e1) { var neighbour = cell.GetNeighbour(direction); if (neighbour == null) { return; // dont add for edge hexes } var bridge = HexMetrics.GetBridge(direction); bridge.y = neighbour.Position.y - cell.Position.y; var e2 = new EdgeVertices( e1.v1 + bridge, e1.v5 + bridge ); var hasRiver = cell.HasRiverThroughEdge(direction); var hasRoad = cell.HasRoadThroughEdge(direction); if (hasRiver) { var startV3 = e2.v3.y; e2.v3.y = neighbour.StreamBedY; var indices = new Vector3(cell.Index, neighbour.Index, cell.Index); // by definition, both cells have rivers through them // however, if both are underwater, then we want (i want) no river bed // if only one is, the river should merge with the stream // EXCEPT if there is an elevation difference, in which case we need the stream bed to make a waterfall // if im a river and the neighbour is a river if (!cell.IsUnderwater && !neighbour.IsUnderwater) { TriangulateRiverQuad( e1.v2, e1.v4, e2.v2, e2.v4, cell.RiverSurfaceY, neighbour.RiverSurfaceY, 0.8f, cell.HasIncomingRiver && cell.IncomingRiver == direction, indices); } // if im a river and the neighbour is beneath me else if (!cell.IsUnderwater && cell.Elevation > neighbour.WaterLevel) { TriangulateWaterfallInWater( e1.v2, e1.v4, e2.v2, e2.v4, cell.RiverSurfaceY, neighbour.RiverSurfaceY, neighbour.WaterLevel, indices); } // im underwater but the neighbour is heigher than me else if (cell.IsUnderwater && !neighbour.IsUnderwater && neighbour.Elevation > cell.WaterLevel) { TriangulateWaterfallInWater( e2.v4, e2.v2, e1.v4, e1.v2, neighbour.RiverSurfaceY, cell.RiverSurfaceY, cell.WaterLevel, indices); } else if ((cell.IsUnderwater == neighbour.IsUnderwater == true) || // both underwater !cell.IsUnderwater && neighbour.IsUnderwater) // river into water on same level { e2.v3.y = startV3; // if a river, this will smooth e1 (river side) into e2 (lake/sea bed, which is normal surface) } // if not a river, then e1 is already sea/lake bed, so e2 will now be approximate same level } if (HexMetrics.GetEdgeType(cell.Elevation, neighbour.Elevation) == HexEdgeType.Slope) { TriangulateEdgeTerrace(e1, cell, e2, neighbour, hasRoad); } else { TriangulateEdgeStrip(e1, weights1, cell.Index, e2, weights2, neighbour.Index, hasRoad); } Features.AddWall(e1, cell, e2, neighbour, hasRiver, hasRoad); if (direction > HexDirection.E) { return; } var nextDirection = direction.Next(); var nextNeighbour = cell.GetNeighbour(nextDirection); if (nextNeighbour == null) { return; } var v5 = e1.v5 + HexMetrics.GetBridge(nextDirection); v5.y = nextNeighbour.Position.y; var minElevation = Mathf.Min(cell.Elevation, neighbour.Elevation, nextNeighbour.Elevation); if (minElevation == cell.Elevation) { TriangulateCorner(e1.v5, cell, e2.v5, neighbour, v5, nextNeighbour); } else if (minElevation == neighbour.Elevation) { TriangulateCorner(e2.v5, neighbour, v5, nextNeighbour, e1.v5, cell); } else { TriangulateCorner(v5, nextNeighbour, e1.v5, cell, e2.v5, neighbour); } }