private void TriangulateAdjacentToRiver(HexDirection direction, HexCell cell, Vector3 centre, EdgeVertices e) { if (cell.HasRoads) { TriangulateRoadAdjacentToRiver(direction, cell, centre, e); } if (cell.HasRiverThroughEdge(direction.Next())) { if (cell.HasRiverThroughEdge(direction.Previous())) // on a curve, so pull back the centre point { centre += HexMetrics.GetSolidEdgeMiddle(direction) * HexMetrics.InnerToOuter * 0.5f; } else if (cell.HasRiverThroughEdge(direction.Previous2())) // straight connection - pull to one side { centre += HexMetrics.GetFirstSolidCorner(direction) * 0.25f; } } else if (cell.HasRiverThroughEdge(direction.Previous()) && cell.HasRiverThroughEdge(direction.Next2())) { centre += HexMetrics.GetSecondSolidCorner(direction) * 0.25f; // other type of straight connection } var m = new EdgeVertices( Vector3.Lerp(centre, e.v1, 0.5f), Vector3.Lerp(centre, e.v5, 0.5f)); TriangulateEdgeStrip(m, weights1, cell.Index, e, weights1, cell.Index); TriangulateEdgeFan(centre, m, cell.Index); if (!cell.IsUnderwater && !cell.HasRoadThroughEdge(direction)) { Features.AddFeature(cell, (cell.Position + e.v1 + e.v5) * (1f / 3)); } }
private void TriangulateWithRiver(HexDirection direction, HexCell cell, Vector3 centre, EdgeVertices e) { Vector3 centreL, centreR; if (cell.HasRiverThroughEdge(direction.Opposite())) { centreL = centre + HexMetrics.GetFirstSolidCorner(direction.Previous()) * 0.25f; centreR = centre + HexMetrics.GetSecondSolidCorner(direction.Next()) * 0.25f; } else if (cell.HasRiverThroughEdge(direction.Next())) { centreL = centre; centreR = Vector3.Lerp(centre, e.v5, 2f / 3); } else if (cell.HasRiverThroughEdge(direction.Previous())) { centreL = Vector3.Lerp(centre, e.v1, 2f / 3); centreR = centre; } else if (cell.HasRiverThroughEdge(direction.Next2())) { centreL = centre; centreR = centre + HexMetrics.GetSolidEdgeMiddle(direction.Next()) * 0.5f * HexMetrics.InnerToOuter; } else { centreL = centre + HexMetrics.GetSolidEdgeMiddle(direction.Previous()) * 0.5f * HexMetrics.InnerToOuter; centreR = centre; } centre = Vector3.Lerp(centreL, centreR, 0.5f); // aligns edges var m = new EdgeVertices( Vector3.Lerp(centreL, e.v1, 0.5f), Vector3.Lerp(centreR, e.v5, 0.5f), 1f / 6); m.v3.y = centre.y = e.v3.y; TriangulateEdgeStrip(m, weights1, cell.Index, e, weights1, cell.Index); Terrain.AddTriangle(centreL, m.v1, m.v2); Terrain.AddQuad(centreL, centre, m.v2, m.v3); Terrain.AddQuad(centre, centreR, m.v3, m.v4); Terrain.AddTriangle(centreR, m.v4, m.v5); var indices = new Vector3(cell.Index, cell.Index, cell.Index); Terrain.AddTriangleCellData(indices, weights1); Terrain.AddQuadCellData(indices, weights1); Terrain.AddQuadCellData(indices, weights1); Terrain.AddTriangleCellData(indices, weights1); var reversed = cell.IncomingRiver == direction; TriangulateRiverQuad(centreL, centreR, m.v2, m.v4, cell.RiverSurfaceY, 0.4f, reversed, indices); TriangulateRiverQuad(m.v2, m.v4, e.v2, e.v4, cell.RiverSurfaceY, 0.6f, reversed, indices); }
// 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 TriangulateWaterShore(HexDirection direction, HexCell cell, HexCell neighbour, Vector3 centre) { var e1 = new EdgeVertices( centre + HexMetrics.GetFirstWaterCorner(direction), centre + HexMetrics.GetSecondWaterCorner(direction) ); Water.AddTriangle(centre, e1.v1, e1.v2); Water.AddTriangle(centre, e1.v2, e1.v3); Water.AddTriangle(centre, e1.v3, e1.v4); Water.AddTriangle(centre, e1.v4, e1.v5); var indices = new Vector3(cell.Index, neighbour.Index, cell.Index); Water.AddTriangleCellData(indices, weights1); Water.AddTriangleCellData(indices, weights1); Water.AddTriangleCellData(indices, weights1); Water.AddTriangleCellData(indices, weights1); var centre2 = neighbour.Position; if (neighbour.ColumnIndex < cell.ColumnIndex - 1) { centre2.x += HexMetrics.WrapSize * HexMetrics.InnerDiameter; } else if (neighbour.ColumnIndex > cell.ColumnIndex + 1) { centre2.x -= HexMetrics.WrapSize * HexMetrics.InnerDiameter; } centre2.y = centre.y; var e2 = new EdgeVertices( centre2 + HexMetrics.GetSecondSolidCorner(direction.Opposite()), centre2 + HexMetrics.GetFirstSolidCorner(direction.Opposite()) ); // rather than calculating from current centre, work backwards from neighbour centre to find edge if (cell.HasRiverThroughEdge(direction)) { TriangulateEstuary(e1, e2, cell.IncomingRiver == direction, indices); } else { WaterShore.AddQuad(e1.v1, e1.v2, e2.v1, e2.v2); WaterShore.AddQuad(e1.v2, e1.v3, e2.v2, e2.v3); WaterShore.AddQuad(e1.v3, e1.v4, e2.v3, e2.v4); WaterShore.AddQuad(e1.v4, e1.v5, e2.v4, e2.v5); WaterShore.AddQuadUV(0f, 0f, 0f, 1f); WaterShore.AddQuadUV(0f, 0f, 0f, 1f); WaterShore.AddQuadUV(0f, 0f, 0f, 1f); WaterShore.AddQuadUV(0f, 0f, 0f, 1f); WaterShore.AddQuadCellData(indices, weights1, weights2); WaterShore.AddQuadCellData(indices, weights1, weights2); WaterShore.AddQuadCellData(indices, weights1, weights2); WaterShore.AddQuadCellData(indices, weights1, weights2); } var nextNeighbour = cell.GetNeighbour(direction.Next()); if (nextNeighbour == null) { return; } var centre3 = nextNeighbour.Position; if (nextNeighbour.ColumnIndex < cell.ColumnIndex - 1) { centre3.x += HexMetrics.WrapSize * HexMetrics.InnerDiameter; } else if (nextNeighbour.ColumnIndex > cell.ColumnIndex + 1) { centre3.x -= HexMetrics.WrapSize * HexMetrics.InnerDiameter; } var v3 = centre3 + (nextNeighbour.IsUnderwater ? HexMetrics.GetFirstWaterCorner(direction.Previous()) : HexMetrics.GetFirstSolidCorner(direction.Previous())); v3.y = centre.y; WaterShore.AddTriangle(e1.v5, e2.v5, v3); WaterShore.AddTriangleUV( new Vector2(0f, 0f), new Vector2(0f, 1f), new Vector2(0f, nextNeighbour.IsUnderwater ? 0f : 1f)); indices.z = nextNeighbour.Index; WaterShore.AddTriangleCellData(indices, weights1, weights2, weights3); }
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); } }