void TriangulateWithRiverBeginOrEnd(HexDirection direction, HexCell cell) { var center = cell.Center; var closerEdge = cell.Edges[(int)direction]; if (cell.HasRiverThroughEdge(direction.Next())) { if (cell.HasRiverThroughEdge(direction.Previous())) { center += HexMetrics.GetSolidEdgeMiddle(direction) * (HexMetrics.InnerToOuter * 0.5f); } else if (cell.HasRiverThroughEdge(direction.Previous2())) { center += HexMetrics.GetLeftSolidCorner(direction) * 0.25f; } } else if (cell.HasRiverThroughEdge(direction.Previous()) && cell.HasRiverThroughEdge(direction.Next2())) { center += HexMetrics.GetRightSolidCorner(direction) * 0.25f; } var m = new EdgeVertices(Vector3.Lerp(center, closerEdge.V1, 0.5f), Vector3.Lerp(center, closerEdge.V5, 0.5f)); m.V3.y = closerEdge.V3.y; // reassign middle verticle height as it is ommited in the calculation above TriangulateEdgeStrip(m, cell.Color, closerEdge, cell.Color); TriangulateEdgeFan(center, m, cell.Color); // river segments are added only if the current segment is not under water if (!cell.IsUnderwater) { bool reversed = cell.HasIncomingRiver; // outer circle of the hex TriangulateRiverQuadUnperturbed(m.V2, m.V4, closerEdge.V2, closerEdge.V4, cell.RiverSurfaceY, 0.6f, reversed); // end (or start) triangle center.y = m.V2.y = m.V4.y = cell.RiverSurfaceY; Rivers.AddTriangleUnperturbed(center, m.V2, m.V4); if (reversed) { Rivers.AddTriangleUV(new Vector2(0.5f, 0.4f), new Vector2(1f, 0.2f), new Vector2(0f, 0.2f)); } else { Rivers.AddTriangleUV(new Vector2(0.5f, 0.4f), new Vector2(0f, 0.6f), new Vector2(1f, 0.6f)); } } }
void Precalculation(HexCell cell) { cell.Center = cell.WaterCenter = cell.transform.localPosition; cell.WaterCenter.y = HexMetrics.WaterSurfaceY; for (int i = 0; i <= 5; i++) { var direction = (HexDirection)i; cell.Edges[i] = new EdgeVertices( cell.Center + HexMetrics.GetLeftSolidCorner(direction), cell.Center + HexMetrics.GetRightSolidCorner(direction)); if (cell.HasRiver && cell.HasRiverThroughEdge(direction)) { cell.Edges[i].V3.y = cell.StreamBedY; } cell.WaterEdges[i] = new EdgeVertices( cell.WaterCenter + HexMetrics.GetLeftWaterCorner(direction), cell.WaterCenter + HexMetrics.GetRightWaterCorner(direction)); } }
/// <summary> /// Fill the cell triangle with a strip and a fan. We cannot suffice with a single fan, /// because we have to make sure that we match the middle edge of the parts that do contain a river. /// </summary> void TriangulateAdjacentToRiver(HexDirection direction, HexCell cell, EdgeVertices e) { if (cell.HasRoads) { TriangulateRoadAdjacentToRiver(direction, cell, e); } var center = cell.Center; if (cell.HasRiverThroughEdge(direction.Next())) { // we are inside a curve if (cell.HasRiverThroughEdge(direction.Previous())) { center += HexMetrics.GetSolidEdgeMiddle(direction) * (HexMetrics.InnerToOuter * 0.5f); } // we are inside a straight line else if (cell.HasRiverThroughEdge(direction.Previous2())) { center += HexMetrics.GetLeftSolidCorner(direction) * 0.25f; } } // The final case is when we have a river in the previous direction, and it is a straight one. // That requires moving the center towards the next solid corner. else if (cell.HasRiverThroughEdge(direction.Previous()) && cell.HasRiverThroughEdge(direction.Next2())) { center += HexMetrics.GetRightSolidCorner(direction) * 0.25f; } var m = new EdgeVertices(Vector3.Lerp(center, e.V1, 0.5f), Vector3.Lerp(center, e.V5, 0.5f)); m.V3.y = e.V3.y; TriangulateEdgeStrip(m, cell.Color, e, cell.Color); TriangulateEdgeFan(center, m, cell.Color); }
void TriangulateRoadAdjacentToRiver(HexDirection direction, HexCell cell, EdgeVertices e) { var center = cell.Center; // produce partial roads in cells with rivers. The directions with rivers through them will cut gaps in the roads. bool hasRoadThroughEdge = cell.HasRoadThroughEdge(direction); bool previousHasRiver = cell.HasRiverThroughEdge(direction.Previous()); bool nextHasRiver = cell.HasRiverThroughEdge(direction.Next()); Vector3 roadCenter = center; if (cell.HasRiverBeginOrEnd) { roadCenter += HexMetrics.GetSolidEdgeMiddle(cell.RiverBeginOrEndDirection.Opposite()) * (1f / 3f); } else if (cell.IncomingRiver == cell.OutgoingRiver.Opposite()) { Vector3 corner; if (previousHasRiver) { if (!hasRoadThroughEdge && !cell.HasRoadThroughEdge(direction.Next())) { return; } corner = HexMetrics.GetRightSolidCorner(direction); } else { if (!hasRoadThroughEdge && !cell.HasRoadThroughEdge(direction.Previous())) { return; } corner = HexMetrics.GetLeftSolidCorner(direction); } roadCenter += corner * 0.5f; // add bridges if (cell.IncomingRiver == direction.Next() && (cell.HasRoadThroughEdge(direction.Next2()) || cell.HasRoadThroughEdge(direction.Opposite()))) { Features.AddBridge(roadCenter, center - corner * 0.5f); } center += corner * 0.25f; } // in case of zigzags else if (cell.IncomingRiver == cell.OutgoingRiver.Previous()) { roadCenter -= HexMetrics.GetRightCorner(cell.IncomingRiver) * 0.2f; } else if (cell.IncomingRiver == cell.OutgoingRiver.Next()) { roadCenter -= HexMetrics.GetLeftCorner(cell.IncomingRiver) * 0.2f; } // in case of curved rivers else if (previousHasRiver && nextHasRiver) { if (!hasRoadThroughEdge) { return; } Vector3 offset = HexMetrics.GetSolidEdgeMiddle(direction) * HexMetrics.InnerToOuter; roadCenter += offset * 0.7f; center += offset * 0.5f; } // outside of the curved river else { HexDirection middle; if (previousHasRiver) { middle = direction.Next(); } else if (nextHasRiver) { middle = direction.Previous(); } else { middle = direction; } // get rid off roads on the other side of the river if (!cell.HasRoadThroughEdge(middle) && !cell.HasRoadThroughEdge(middle.Previous()) && !cell.HasRoadThroughEdge(middle.Next())) { return; } Vector3 offset = HexMetrics.GetSolidEdgeMiddle(middle); roadCenter += offset * 0.25f; // prevent duplications if (direction == middle && cell.HasRoadThroughEdge(direction.Opposite())) { Features.AddBridge(roadCenter, center - offset * (HexMetrics.InnerToOuter * 0.7f)); } } Vector2 interpolators = GetRoadInterpolators(direction, cell); Vector3 mL = Vector3.Lerp(roadCenter, e.V1, interpolators.x); Vector3 mR = Vector3.Lerp(roadCenter, e.V5, interpolators.y); TriangulateRoad(roadCenter, mL, mR, e, hasRoadThroughEdge); // close the gaps if (previousHasRiver) { TriangulateRoadEdge(roadCenter, center, mL); } if (nextHasRiver) { TriangulateRoadEdge(roadCenter, mR, center); } }
void TriangulateWithRiver(HexDirection direction, HexCell cell) { var center = cell.Center; var closerEdge = cell.Edges[(int)direction]; if (cell.HasRiverThroughEdge(direction.Next())) { if (cell.HasRiverThroughEdge(direction.Previous())) { center += HexMetrics.GetSolidEdgeMiddle(direction) * (HexMetrics.InnerToOuter * 0.5f); } else if (cell.HasRiverThroughEdge(direction.Previous2())) { center += HexMetrics.GetLeftSolidCorner(direction) * 0.25f; } } else if (cell.HasRiverThroughEdge(direction.Previous()) && cell.HasRiverThroughEdge(direction.Next2())) { center += HexMetrics.GetRightSolidCorner(direction) * 0.25f; } Vector3 centerL, centerR; if (cell.HasRiverThroughEdge(direction.Opposite())) { centerL = center + HexMetrics.GetLeftSolidCorner(direction.Previous()) * 0.25f; centerR = center + HexMetrics.GetRightSolidCorner(direction.Next()) * 0.25f; } else if (cell.HasRiverThroughEdge(direction.Next())) { centerL = center; centerR = Vector3.Lerp(center, closerEdge.V5, 0.65f); } else if (cell.HasRiverThroughEdge(direction.Previous())) { centerL = Vector3.Lerp(center, closerEdge.V1, 0.65f); centerR = center; } else if (cell.HasRiverThroughEdge(direction.Next2())) { centerL = center; centerR = center + HexMetrics.GetSolidEdgeMiddle(direction.Next()) * (0.5f * HexMetrics.InnerToOuter); } else { centerL = center + HexMetrics.GetSolidEdgeMiddle(direction.Previous()) * (0.5f * HexMetrics.InnerToOuter); centerR = center; } // after deciding where the left and right points are, we can determine the final center by averaging them center = Vector3.Lerp(centerL, centerR, 0.5f); var m = new EdgeVertices(Vector3.Lerp(centerL, closerEdge.V1, 0.5f), Vector3.Lerp(centerR, closerEdge.V5, 0.5f), 1f / 12f); m.V3.y = closerEdge.V3.y; // external hex circle TriangulateEdgeStrip(m, cell.Color, closerEdge, cell.Color); // connection between hexes Terrain.AddTriangle(centerL, m.V1, m.V2); Terrain.AddTriangleColor(cell.Color); Terrain.AddQuad(centerL, new Vector3(center.x, cell.StreamBedY, center.z), m.V2, m.V3); Terrain.AddQuadColor(cell.Color); Terrain.AddQuad(new Vector3(center.x, cell.StreamBedY, center.z), centerR, m.V3, m.V4); Terrain.AddQuadColor(cell.Color); Terrain.AddTriangle(centerR, m.V4, m.V5); Terrain.AddTriangleColor(cell.Color); // create river quads if (!cell.IsUnderwater) { bool reversed = cell.IncomingRiver == direction; // inner fan of the hex TriangulateRiverQuadUnperturbed(centerL, centerR, m.V2, m.V4, cell.RiverSurfaceY, 0.4f, reversed); // external circle of the hex var neighbor = cell.GetNeighbor(direction); if (neighbor.IsUnderwater) { TriangulateRiverQuadUnperturbed(m.V2, m.V4, closerEdge.V2, closerEdge.V4, cell.RiverSurfaceY, HexMetrics.WaterSurfaceY, 0.6f, reversed); } // normal connection between two rivers else { TriangulateRiverQuadUnperturbed(m.V2, m.V4, closerEdge.V2, closerEdge.V4, cell.RiverSurfaceY, 0.6f, reversed); } } }