// Triangulate a corner with half terraces, half flat - Cliff on the left void TriangulateCornerCliffTerraces(Vector3 begin, HexCell beginCell, Vector3 left, HexCell leftCell, Vector3 right, HexCell rightCell) { float b = 1f / (leftCell.Elevation - beginCell.Elevation); if (b < 0) { b = -b; } Vector3 boundary = Vector3.Lerp(HexMetrics.Perturb(begin), HexMetrics.Perturb(left), b); // boundary is on a pertubed slope Color boundaryColor = Color.Lerp(beginCell.Color, leftCell.Color, b); TriangulateBoundaryTriangle(right, rightCell, begin, beginCell, boundary, boundaryColor); if (leftCell.GetEdgeType(rightCell) == HexEdgeType.Slope) { TriangulateBoundaryTriangle(left, leftCell, right, rightCell, boundary, boundaryColor); } else { terrain.AddTriangleUnpertubed(HexMetrics.Perturb(left), HexMetrics.Perturb(right), boundary); terrain.AddTriangleColor(leftCell.Color, rightCell.Color, boundaryColor); } }
private void TriangulateCornerTerraces( Vector3 begin, HexCell beginCell, Vector3 left, HexCell leftCell, Vector3 right, HexCell rightCell ) { Vector3 v3 = HexMetrics.TerraceLerp(begin, left, 1); Vector3 v4 = HexMetrics.TerraceLerp(begin, right, 1); Color w3 = HexMetrics.TerraceLerp(weights1, weights2, 1); Color w4 = HexMetrics.TerraceLerp(weights1, weights3, 1); Vector3 indices; indices.x = beginCell.Index; indices.y = leftCell.Index; indices.z = rightCell.Index; terrain.AddTriangle(begin, v3, v4); terrain.AddTriangleCellData(indices, weights1, w3, w4); for (int i = 2; i < HexMetrics.terraceSteps; i++) { Vector3 v1 = v3; Vector3 v2 = v4; Color w1 = w3; Color w2 = w4; v3 = HexMetrics.TerraceLerp(begin, left, i); v4 = HexMetrics.TerraceLerp(begin, right, i); w3 = HexMetrics.TerraceLerp(weights1, weights2, i); w4 = HexMetrics.TerraceLerp(weights1, weights3, i); terrain.AddQuad(v1, v2, v3, v4); terrain.AddQuadCellData(indices, w1, w2, w3, w4); } terrain.AddQuad(v3, v4, left, right); terrain.AddQuadCellData(indices, w3, w4, weights2, weights3); }
private void TriangulateEdgeTerraces( EdgeVertices begin, HexCell beginCell, EdgeVertices end, HexCell endCell, bool hasRoad ) { EdgeVertices e2 = EdgeVertices.TerraceLerp(begin, end, 1); Color w2 = HexMetrics.TerraceLerp(weights1, weights2, 1); float i1 = beginCell.Index; float i2 = endCell.Index; TriangulateEdgeStrip(begin, weights1, i1, e2, w2, i2, hasRoad); for (int i = 2; i < HexMetrics.terraceSteps; i++) { EdgeVertices e1 = e2; Color w1 = w2; e2 = EdgeVertices.TerraceLerp(begin, end, i); w2 = HexMetrics.TerraceLerp(weights1, weights2, i); TriangulateEdgeStrip(e1, w1, i1, e2, w2, i2, hasRoad); } TriangulateEdgeStrip(e2, w2, i1, end, weights2, i2, hasRoad); }
// Make a terrace in a corner, each terrace end in the boundary void TriangulateBoundaryTriangle(Vector3 begin, HexCell beginCell, Vector3 left, HexCell leftCell, Vector3 boundary, Color boundaryColor) { Vector3 v2 = HexMetrics.Perturb(HexMetrics.TerraceLerp(begin, left, 1)); Color c2 = HexMetrics.TerraceLerp(beginCell.Color, leftCell.Color, 1); // The first step is a triangle terrain.AddTriangleUnpertubed(HexMetrics.Perturb(begin), v2, boundary); terrain.AddTriangleColor(beginCell.Color, c2, boundaryColor); // All the steps are triangles for (int i = 2; i < HexMetrics.terraceSteps; i++) { Vector3 v1 = v2; Color c1 = c2; v2 = HexMetrics.Perturb(HexMetrics.TerraceLerp(begin, left, i)); c2 = HexMetrics.TerraceLerp(beginCell.Color, leftCell.Color, i); terrain.AddTriangleUnpertubed(v1, v2, boundary); terrain.AddTriangleColor(c1, c2, boundaryColor); } // The last step is a triangle terrain.AddTriangleUnpertubed(v2, HexMetrics.Perturb(left), boundary); terrain.AddTriangleColor(c2, leftCell.Color, boundaryColor); }
void TriangulateEdgeTerraces(Vector3 beginLeft, Vector3 beginRight, HexCell beginCell, Vector3 endLeft, Vector3 endRight, HexCell endCell) { Vector3 v3 = HexMetrics.TerraceLerp(beginLeft, endLeft, 1); Vector3 v4 = HexMetrics.TerraceLerp(beginRight, endRight, 1); Color c2 = HexMetrics.TerraceLerp(beginCell.Color, endCell.Color, 1); AddQuad(beginLeft, beginRight, v3, v4); AddQuadColor(beginCell.Color, c2); for (int i = 2; i < HexMetrics.TerraceSteps; i++) { Vector3 v1 = v3; Vector3 v2 = v4; Color c1 = c2; v3 = HexMetrics.TerraceLerp(beginLeft, endLeft, i); v4 = HexMetrics.TerraceLerp(beginRight, endRight, i); c2 = HexMetrics.TerraceLerp(beginCell.Color, endCell.Color, i); AddQuad(v1, v2, v3, v4); AddQuadColor(c1, c2); } AddQuad(v3, v4, endLeft, endRight); AddQuadColor(c2, endCell.Color); }
private void TriangulateConnection( HexDirection direction, HexCell cell, EdgeVertices e1 ) { HexCell neighbor = cell.GetNeighbor(direction); if (neighbor == null) { return; } Vector3 bridge = HexMetrics.GetBridge(direction); bridge.y = neighbor.Position.y - cell.Position.y; EdgeVertices e2 = new EdgeVertices( e1.v1 + bridge, e1.v5 + bridge ); bool hasRiver = cell.HasRiverThroughEdge(direction); bool hasRoad = cell.HasRoadThroughEdge(direction); if (hasRiver) { e2.v3.y = neighbor.StreamBedY; Vector3 indices; indices.x = indices.z = cell.Index; indices.y = neighbor.Index; if (!cell.IsUnderwater) { if (!neighbor.IsUnderwater) { TriangulateRiverQuad( e1.v2, e1.v4, e2.v2, e2.v4, cell.RiverSurfaceY, neighbor.RiverSurfaceY, 0.8f, cell.HasIncomingRiver && cell.IncomingRiver == direction, indices ); } else if (cell.Elevation > neighbor.WaterLevel) { TriangulateWaterfallInWater( e1.v2, e1.v4, e2.v2, e2.v4, cell.RiverSurfaceY, neighbor.RiverSurfaceY, neighbor.WaterSurfaceY, indices ); } } else if (!neighbor.IsUnderwater && neighbor.Elevation > cell.WaterLevel) { TriangulateWaterfallInWater( e2.v4, e2.v2, e1.v4, e1.v2, neighbor.RiverSurfaceY, cell.RiverSurfaceY, cell.WaterSurfaceY, indices ); } } if (cell.GetEdgeType(direction) == HexEdgeType.Slope) { TriangulateEdgeTerraces(e1, cell, e2, neighbor, hasRoad); } else { TriangulateEdgeStrip( e1, weights1, cell.Index, e2, weights2, neighbor.Index, hasRoad ); } features.AddWall(e1, cell, e2, neighbor, hasRiver, hasRoad); HexCell nextNeighbor = cell.GetNeighbor(direction.Next()); if (direction <= HexDirection.E && nextNeighbor != null) { Vector3 v5 = e1.v5 + HexMetrics.GetBridge(direction.Next()); v5.y = nextNeighbor.Position.y; if (cell.Elevation <= neighbor.Elevation) { if (cell.Elevation <= nextNeighbor.Elevation) { TriangulateCorner( e1.v5, cell, e2.v5, neighbor, v5, nextNeighbor ); } else { TriangulateCorner( v5, nextNeighbor, e1.v5, cell, e2.v5, neighbor ); } } else if (neighbor.Elevation <= nextNeighbor.Elevation) { TriangulateCorner( e2.v5, neighbor, v5, nextNeighbor, e1.v5, cell ); } else { TriangulateCorner( v5, nextNeighbor, e1.v5, cell, e2.v5, neighbor ); } } }
private void TriangulateWithRiver( HexDirection direction, HexCell cell, Vector3 center, EdgeVertices e ) { Vector3 centerL, centerR; if (cell.HasRiverThroughEdge(direction.Opposite())) { centerL = center + HexMetrics.GetFirstSolidCorner(direction.Previous()) * 0.25f; centerR = center + HexMetrics.GetSecondSolidCorner(direction.Next()) * 0.25f; } else if (cell.HasRiverThroughEdge(direction.Next())) { centerL = center; centerR = Vector3.Lerp(center, e.v5, 2f / 3f); } else if (cell.HasRiverThroughEdge(direction.Previous())) { centerL = Vector3.Lerp(center, e.v1, 2f / 3f); 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; } center = Vector3.Lerp(centerL, centerR, 0.5f); EdgeVertices m = new EdgeVertices( Vector3.Lerp(centerL, e.v1, 0.5f), Vector3.Lerp(centerR, e.v5, 0.5f), 1f / 6f ); m.v3.y = center.y = e.v3.y; TriangulateEdgeStrip(m, weights1, cell.Index, e, weights1, cell.Index); terrain.AddTriangle(centerL, m.v1, m.v2); terrain.AddQuad(centerL, center, m.v2, m.v3); terrain.AddQuad(center, centerR, m.v3, m.v4); terrain.AddTriangle(centerR, m.v4, m.v5); Vector3 indices; indices.x = indices.y = indices.z = cell.Index; terrain.AddTriangleCellData(indices, weights1); terrain.AddQuadCellData(indices, weights1); terrain.AddQuadCellData(indices, weights1); terrain.AddTriangleCellData(indices, weights1); if (!cell.IsUnderwater) { bool reversed = cell.IncomingRiver == direction; TriangulateRiverQuad( centerL, centerR, 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 ); } }
private void TriangulateRoadAdjacentToRiver( HexDirection direction, HexCell cell, Vector3 center, EdgeVertices e ) { bool hasRoadThroughEdge = cell.HasRoadThroughEdge(direction); bool previousHasRiver = cell.HasRiverThroughEdge(direction.Previous()); bool nextHasRiver = cell.HasRiverThroughEdge(direction.Next()); Vector2 interpolators = GetRoadInterpolators(direction, cell); 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.GetSecondSolidCorner(direction); } else { if (!hasRoadThroughEdge && !cell.HasRoadThroughEdge(direction.Previous())) { return; } corner = HexMetrics.GetFirstSolidCorner(direction); } roadCenter += corner * 0.5f; if (cell.IncomingRiver == direction.Next() && ( cell.HasRoadThroughEdge(direction.Next2()) || cell.HasRoadThroughEdge(direction.Opposite()) )) { features.AddBridge(roadCenter, center - corner * 0.5f); } center += corner * 0.25f; } else if (cell.IncomingRiver == cell.OutgoingRiver.Previous()) { roadCenter -= HexMetrics.GetSecondCorner(cell.IncomingRiver) * 0.2f; } else if (cell.IncomingRiver == cell.OutgoingRiver.Next()) { roadCenter -= HexMetrics.GetFirstCorner(cell.IncomingRiver) * 0.2f; } else if (previousHasRiver && nextHasRiver) { if (!hasRoadThroughEdge) { return; } Vector3 offset = HexMetrics.GetSolidEdgeMiddle(direction) * HexMetrics.innerToOuter; roadCenter += offset * 0.7f; center += offset * 0.5f; } else { HexDirection middle; if (previousHasRiver) { middle = direction.Next(); } else if (nextHasRiver) { middle = direction.Previous(); } else { middle = direction; } if ( !cell.HasRoadThroughEdge(middle) && !cell.HasRoadThroughEdge(middle.Previous()) && !cell.HasRoadThroughEdge(middle.Next()) ) { return; } Vector3 offset = HexMetrics.GetSolidEdgeMiddle(middle); roadCenter += offset * 0.25f; if (direction == middle && cell.HasRoadThroughEdge(direction.Opposite())) { features.AddBridge( roadCenter, center - offset * (HexMetrics.innerToOuter * 0.7f) ); } } Vector3 mL = Vector3.Lerp(roadCenter, e.v1, interpolators.x); Vector3 mR = Vector3.Lerp(roadCenter, e.v5, interpolators.y); TriangulateRoad(roadCenter, mL, mR, e, hasRoadThroughEdge, cell.Index); if (previousHasRiver) { TriangulateRoadEdge(roadCenter, center, mL, cell.Index); } if (nextHasRiver) { TriangulateRoadEdge(roadCenter, mR, center, cell.Index); } }
private void TriangulateWaterShore( HexDirection direction, HexCell cell, HexCell neighbor, Vector3 center ) { EdgeVertices e1 = new EdgeVertices( center + HexMetrics.GetFirstWaterCorner(direction), center + HexMetrics.GetSecondWaterCorner(direction) ); water.AddTriangle(center, e1.v1, e1.v2); water.AddTriangle(center, e1.v2, e1.v3); water.AddTriangle(center, e1.v3, e1.v4); water.AddTriangle(center, e1.v4, e1.v5); Vector3 indices; indices.x = indices.z = cell.Index; indices.y = neighbor.Index; water.AddTriangleCellData(indices, weights1); water.AddTriangleCellData(indices, weights1); water.AddTriangleCellData(indices, weights1); water.AddTriangleCellData(indices, weights1); Vector3 center2 = neighbor.Position; if (neighbor.ColumnIndex < cell.ColumnIndex - 1) { center2.x += HexMetrics.wrapSize * HexMetrics.innerDiameter; } else if (neighbor.ColumnIndex > cell.ColumnIndex + 1) { center2.x -= HexMetrics.wrapSize * HexMetrics.innerDiameter; } center2.y = center.y; EdgeVertices e2 = new EdgeVertices( center2 + HexMetrics.GetSecondSolidCorner(direction.Opposite()), center2 + HexMetrics.GetFirstSolidCorner(direction.Opposite()) ); if (cell.HasRiverThroughEdge(direction)) { TriangulateEstuary(e1, e2, cell.HasIncomingRiver && 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); } HexCell nextNeighbor = cell.GetNeighbor(direction.Next()); if (nextNeighbor != null) { Vector3 center3 = nextNeighbor.Position; if (nextNeighbor.ColumnIndex < cell.ColumnIndex - 1) { center3.x += HexMetrics.wrapSize * HexMetrics.innerDiameter; } else if (nextNeighbor.ColumnIndex > cell.ColumnIndex + 1) { center3.x -= HexMetrics.wrapSize * HexMetrics.innerDiameter; } Vector3 v3 = center3 + (nextNeighbor.IsUnderwater ? HexMetrics.GetFirstWaterCorner(direction.Previous()) : HexMetrics.GetFirstSolidCorner(direction.Previous())); v3.y = center.y; waterShore.AddTriangle(e1.v5, e2.v5, v3); waterShore.AddTriangleUV( new Vector2(0f, 0f), new Vector2(0f, 1f), new Vector2(0f, nextNeighbor.IsUnderwater ? 0f : 1f) ); indices.z = nextNeighbor.Index; waterShore.AddTriangleCellData( indices, weights1, weights2, weights3 ); } }
public Mesh ElevatedTileHexagon(HexMetrics metrics, HexMap hexMap, int column, int row, Mesh recycleMesh = null) { ClearAndSetup("Hexagon Tile With Elevation", recycleMesh); Vector3 center = HexUtils.PositionFromCoordinates(column, row, metrics.tileSize); int elevation = hexMap.GetElevationAt(column, row); float elevationHeight = metrics.elevationStepHeight * elevation; Color baseColor = hexMap.tileset.GetColorForElevation(elevation, metrics.maxElevation); float centerHeight = metrics.XZPositionToHeight(center); float[] cornerHeights = new float[6]; foreach (var corner in HexCornerUtils.AllCorners()) { Vector3 vertex = HexUtils.CornerPosition((int)corner) * metrics.tileSize; cornerHeights[corner.GetInt()] = metrics.XZPositionToHeight(center + vertex); } #region Base Hexagon float baseHexagonPercentage = metrics.tileInnerRadiusPercent; // Add vertex data for each corner first in order to reduce redundancy (18 vertices -> 7) // Not making triangles yet foreach (HexCorner corner in HexCornerUtils.AllCorners()) { // Vertice w/ height //Vector3 cornerPoint = SpokePoint(metrics, corner, baseHexagonPercentage, centerHeight, cornerHeights); // Base the height of the 'spokes' on the straight line from the center out to the corner of the whole tile Vector3 cornerVertex = HexUtils.CornerPosition((int)corner) * metrics.tileSize * baseHexagonPercentage; float projectedSpokeHeight = (cornerHeights[corner.GetInt()] - centerHeight) * baseHexagonPercentage; float vertexHeight = centerHeight + projectedSpokeHeight; Vector3 cornerPoint = cornerVertex + Vector3.up * (vertexHeight * metrics.mapHeight + elevationHeight); float textureGradient = vertexHeight; AddHexPointData(metrics, cornerPoint, center, baseColor, textureGradient); } // Add the center point last just so that we can use the HexCorner enums as indexes and the center is @ 6 int Center = 6; // just for readability // UV uvs.Add(metrics.XZPositionToUV(center)); // Vertice Vector3 centerVertex = new Vector3(0, centerHeight * metrics.mapHeight + elevationHeight, 0); vertices.Add(centerVertex); // Color colors.Add(baseColor); // Gradient uvs2.Add(new Vector2(centerHeight, 0)); // Make triangles foreach (HexCorner corner in HexCornerUtils.AllCorners()) { triangles.Add((int)corner.Next()); // Next first because of normals triangles.Add((int)corner); triangles.Add(Center); } #endregion // The starting index of the points for the outer ring int ringStartIndex = vertices.Count; // should be 7 here // ----------Add another ring to the hexagon #region First Ring float firstRingPercentage = metrics.tileOuterRadiusPercent; // Add vertex data for each outside corner // Not making triangles yet // Add three points for each corner: the actual outside corner and then the two bridge points foreach (HexCorner corner in HexCornerUtils.AllCorners()) { HexDirection direction = corner.GetDirection(); float theta = HexUtils.CornerAngle((int)corner); //// We need to account for the three walls connecting to this corner: //// the wall on this hex going left, //// the wall on this hex going right, //// and the wall adjacent to this hex running out and away //bool leftWall = hexMap.IsWallAt(column, row, corner.GetDirection()); //bool rightWall = hexMap.IsWallAt(column, row, corner.GetDirection().Last()); //// move to the hex left of the given corner, turn two directions CW and check that wall //bool awayWall = hexMap.IsWallAt(HexUtils.MoveFrom(column, row, corner.GetDirection()), corner.GetDirection().Last2()); // -------Actual Corner---------- - { Vector3 cornerVertex = HexUtils.CornerPosition((int)corner) * metrics.tileSize * firstRingPercentage; float nextNeighborElevationHeight = metrics.elevationStepHeight * hexMap.GetElevationAt(HexUtils.MoveFrom(column, row, direction)); float lastNeighborElevationHeight = metrics.elevationStepHeight * hexMap.GetElevationAt(HexUtils.MoveFrom(column, row, direction.Last())); float heightModFromElevation = (elevationHeight + nextNeighborElevationHeight + lastNeighborElevationHeight) * 0.33f; float projectedSpokeHeight = (cornerHeights[corner.GetInt()] - centerHeight) * firstRingPercentage; float vertexHeight = centerHeight + projectedSpokeHeight; Vector3 cornerPoint = cornerVertex + Vector3.up * (vertexHeight * metrics.mapHeight + heightModFromElevation); float textureGradient = vertexHeight; bool isWall = hexMap.IsWallAt(column, row, direction.Last()); Color slopeColor = baseColor; if (isWall) { slopeColor = hexMap.tileset.steepSlopeColor; } AddHexPointData(metrics, cornerPoint, center, slopeColor, textureGradient); } //// ------- CCW Bridge Point ----------- { var fromCorner = corner; var towardCorner = corner.Next(); var bridgePercentage = metrics.tileInnerRadiusPercent; //Vector3 bridgePoint = BridgePoint(metrics, corner, corner.Next(), firstRingPercentage, // metrics.tileInnerRadiusPercent, centerVertex, cornerHeights); float nextNeighborElevationHeight = metrics.elevationStepHeight * hexMap.GetElevationAt(HexUtils.MoveFrom(column, row, direction)); float heightModFromElevation = (elevationHeight + nextNeighborElevationHeight) * 0.5f; Vector3 cornerVertex = HexUtils.CornerPosition((int)fromCorner) * metrics.tileSize; cornerVertex = cornerVertex + Vector3.up * (cornerHeights[fromCorner.GetInt()] * metrics.mapHeight + heightModFromElevation); Vector3 nextCornerPoint = HexUtils.CornerPosition(towardCorner.GetInt()) * metrics.tileSize; nextCornerPoint = nextCornerPoint + Vector3.up * (cornerHeights[towardCorner.GetInt()] * metrics.mapHeight + heightModFromElevation); Vector3 vector = nextCornerPoint - cornerVertex; float bridgeMod = 0.5f * (1f - bridgePercentage); Vector3 bridgePoint = cornerVertex + vector * bridgeMod; vector = centerVertex - bridgePoint; bridgePoint = bridgePoint + vector * (1f - firstRingPercentage); bool isWall = hexMap.IsWallAt(column, row, direction); Color slopeColor = baseColor; if (isWall) { slopeColor = hexMap.tileset.steepSlopeColor; } AddHexPointData(metrics, bridgePoint, center, slopeColor, cornerHeights[corner.GetInt()]); } //// ------- CW Bridge Point ----------- { //Vector3 bridgePoint = BridgePoint(metrics, corner, corner.Last(), firstRingPercentage, // metrics.tileInnerRadiusPercent, centerVertex, cornerHeights); var fromCorner = corner; var towardCorner = corner.Last(); var bridgePercentage = metrics.tileInnerRadiusPercent; //Vector3 bridgePoint = BridgePoint(metrics, corner, corner.Next(), firstRingPercentage, // metrics.tileInnerRadiusPercent, centerVertex, cornerHeights); float neighborElevationHeight = metrics.elevationStepHeight * hexMap.GetElevationAt(HexUtils.MoveFrom(column, row, direction.Last())); float heightModFromElevation = (elevationHeight + neighborElevationHeight) * 0.5f; Vector3 cornerVertex = HexUtils.CornerPosition((int)fromCorner) * metrics.tileSize; cornerVertex = cornerVertex + Vector3.up * (cornerHeights[fromCorner.GetInt()] * metrics.mapHeight + heightModFromElevation); Vector3 nextCornerPoint = HexUtils.CornerPosition(towardCorner.GetInt()) * metrics.tileSize; nextCornerPoint = nextCornerPoint + Vector3.up * (cornerHeights[towardCorner.GetInt()] * metrics.mapHeight + heightModFromElevation); Vector3 vector = nextCornerPoint - cornerVertex; float bridgeMod = 0.5f * (1f - bridgePercentage); Vector3 bridgePoint = cornerVertex + vector * bridgeMod; vector = centerVertex - bridgePoint; bridgePoint = bridgePoint + vector * (1f - firstRingPercentage); bool isWall = hexMap.IsWallAt(column, row, direction.Last()); Color slopeColor = baseColor; if (isWall) { slopeColor = hexMap.tileset.steepSlopeColor; } AddHexPointData(metrics, bridgePoint, center, slopeColor, cornerHeights[corner.GetInt()]); } } // Make triangles, accounting for one corner and one side at a time // we actually need quads, so pairs of triangles foreach (HexCorner corner in HexCornerUtils.AllCorners()) { // inside point is [corner], outside point is [corner * 3 + ORStart] int outerCorner = (int)corner * 3 + ringStartIndex + 0; // left bridge point is [corner * 3 + ORStart + 1], right bridge point is [corner * 3 + ORStart + 2] int rightBridgePoint = (int)corner * 3 + ringStartIndex + 2; int leftBridgePoint = (int)corner * 3 + ringStartIndex + 1; // corner.Next() is the point CCW int nextRightBridgePoint = (int)corner.Next() * 3 + ringStartIndex + 2; // first half of the quad triangles.Add((int)corner); triangles.Add((int)corner.Next()); triangles.Add(nextRightBridgePoint); // second half of the quad triangles.Add(nextRightBridgePoint); triangles.Add(leftBridgePoint); triangles.Add((int)corner); // span from the quad to the corner with a quad triangles.Add(leftBridgePoint); triangles.Add(outerCorner); triangles.Add((int)corner); // second part of corner span triangles.Add(rightBridgePoint); triangles.Add((int)corner); triangles.Add(outerCorner); } #endregion return(Build()); }
// Triangulate a cell for the direction given void Triangulate(HexDirection direction, HexCell cell) { Vector3 center = cell.Position; EdgeVertices e = new EdgeVertices(center + HexMetrics.GetFirstSolidCorner(direction), center + HexMetrics.GetSecondSolidCorner(direction)); if (cell.HasRiver) { if (cell.HasRiverThroughEdge(direction)) { e.v3.y = cell.StreamBedY; if (cell.HasRiverBeginOrEnd) { TriangulateWithRiverBeginOrEnd(direction, cell, center, e); } else { TriangulateWithRiver(direction, cell, center, e); } } else { TriangulateAdjacentToRiver(direction, cell, center, e); } } else { TriangulateWithoutRiver(direction, cell, center, e); if (!cell.IsUnderwater && !cell.HasRoadThroughEdge(direction)) { features.AddFeature(cell, (center + e.v1 + e.v5) * (1f / 3f)); } } // Make the connection with the neightbors if (direction <= HexDirection.SE) { TriangulateConnection(cell, direction, e); } if (cell.IsUnderwater) { TriangulateWater(direction, cell, center); } }
void TriangulateWaterShore(HexDirection direction, HexCell cell, HexCell neighbor, Vector3 center) { // Triangulate as a fan EdgeVertices e1 = new EdgeVertices(center + HexMetrics.GetFirstWaterCorner(direction), center + HexMetrics.GetSecondWaterCorner(direction)); water.AddTriangle(center, e1.v1, e1.v2); water.AddTriangle(center, e1.v2, e1.v3); water.AddTriangle(center, e1.v3, e1.v4); water.AddTriangle(center, e1.v4, e1.v5); // Triangulate the strip Vector3 center2 = neighbor.Position; center2.y = center.y; EdgeVertices e2 = new EdgeVertices(center2 + HexMetrics.GetSecondSolidCorner(direction.Opposite()), center2 + HexMetrics.GetFirstSolidCorner(direction.Opposite())); if (cell.HasRiverThroughEdge(direction)) { TriangulateEstuary(e1, e2, cell.HasIncomingRiver && cell.IncomingRiver == direction); } 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); } // Don't forget the corner triangle HexCell nextNeighbor = cell.GetNeighbor(direction.Next()); if (nextNeighbor != null) { Vector3 v3 = nextNeighbor.Position + (nextNeighbor.IsUnderwater ? HexMetrics.GetFirstWaterCorner(direction.Previous()) : HexMetrics.GetFirstSolidCorner(direction.Previous())); v3.y = center.y; waterShore.AddTriangle(e1.v5, e2.v5, v3); waterShore.AddTriangleUV(new Vector2(0f, 0f), new Vector2(0f, 1f), new Vector2(0f, nextNeighbor.IsUnderwater ? 0f : 1f)); } }
void TriangulateRoadAdjacentToRiver(HexDirection direction, HexCell cell, Vector3 center, EdgeVertices e) { bool hasRoadThroughEdge = cell.HasRoadThroughEdge(direction); bool previousHasRiver = cell.HasRiverThroughEdge(direction.Previous()); bool nextHasRiver = cell.HasRiverThroughEdge(direction.Next()); Vector2 interpolators = GetRoadInterpolators(direction, cell); Vector3 roadCenter = center; // push the road center in the opposite direction of river if (cell.HasRiverBeginOrEnd) { roadCenter += HexMetrics.GetSolidEdgeMiddle(cell.RiverBeginOrEndDirection.Opposite()) * (1f / 3f); } // Check if river is a straight else if (cell.IncomingRiver == cell.OutgoingRiver.Opposite()) { Vector3 corner; if (previousHasRiver) { // Skip triangulation if there is no road on this side on=f the river if (!hasRoadThroughEdge && !cell.HasRoadThroughEdge(direction.Next())) { return; } corner = HexMetrics.GetSecondSolidCorner(direction); } else { // Skip triangulation if there is no road on this side on=f the river if (!hasRoadThroughEdge && !cell.HasRoadThroughEdge(direction.Previous())) { return; } corner = HexMetrics.GetFirstSolidCorner(direction); } roadCenter += corner * 0.5f; // Add bridge once per cell, if there is a road on both sides if (cell.IncomingRiver == direction.Next() && (cell.HasRoadThroughEdge(direction.Next2()) || cell.HasRoadThroughEdge(direction.Opposite()))) { features.AddBridge(roadCenter, center - corner * 0.5f); } center += corner * 0.25f; } // Check if river is a zigzag else if (cell.IncomingRiver == cell.OutgoingRiver.Previous()) { roadCenter -= HexMetrics.GetSecondCorner(cell.IncomingRiver) * 0.2f; } else if (cell.IncomingRiver == cell.OutgoingRiver.Next()) { roadCenter -= HexMetrics.GetFirstCorner(cell.IncomingRiver) * 0.2f; } // Check if we are inside of a curve else if (previousHasRiver && nextHasRiver) { if (!hasRoadThroughEdge) { return; } Vector3 offset = HexMetrics.GetSolidEdgeMiddle(direction) * HexMetrics.innerToOutter; roadCenter += offset * 0.7f; center += offset * 0.5f; } // We are outside of a curve else { HexDirection middle; if (previousHasRiver) { middle = direction.Next(); } else if (nextHasRiver) { middle = direction.Previous(); } else { middle = direction; } if (!cell.HasRoadThroughEdge(middle.Previous()) && !cell.HasRoadThroughEdge(middle) && !cell.HasRoadThroughEdge(middle.Next())) { return; } Vector3 offset = HexMetrics.GetSolidEdgeMiddle(middle); roadCenter += offset * 0.25f; // Add bridge once per cell, if there is a road on both sides if (direction == middle && cell.HasRoadThroughEdge(direction.Opposite())) { features.AddBridge(roadCenter, center - offset * (HexMetrics.innerToOutter * 0.7f)); } } Vector3 mL = Vector3.Lerp(roadCenter, e.v1, interpolators.x); Vector3 mR = Vector3.Lerp(roadCenter, e.v5, interpolators.y); TriangulateRoad(roadCenter, mL, mR, e, hasRoadThroughEdge); if (previousHasRiver) { TriangulateRoadEdge(roadCenter, center, mL); } if (nextHasRiver) { TriangulateRoadEdge(roadCenter, mR, center); } }
// Fill the gap between each cell in the direction given void TriangulateConnection(HexCell cell, HexDirection direction, EdgeVertices e1) { HexCell neighbor = cell.GetNeighbor(direction); if (neighbor == null) { return; } // Build the bridge Vector3 bridge = HexMetrics.GetBridge(direction); bridge.y = neighbor.Position.y - cell.Position.y; EdgeVertices e2 = new EdgeVertices(e1.v1 + bridge, e1.v5 + bridge); bool hasRiver = cell.HasRiverThroughEdge(direction); bool hasRoad = cell.HasRoadThroughEdge(direction); if (hasRiver) { e2.v3.y = neighbor.StreamBedY; if (!cell.IsUnderwater) { if (!neighbor.IsUnderwater) { // Normal river TriangulateRiverQuad(e1.v2, e1.v4, e2.v2, e2.v4, cell.RiverSurfaceY, neighbor.RiverSurfaceY, 0.8f, cell.HasIncomingRiver && cell.IncomingRiver == direction); } else if (cell.Elevation > neighbor.WaterLevel) { // Waterfall from cell to neighbor TriangulateWaterfallInWater(e1.v2, e1.v4, e2.v2, e2.v4, cell.RiverSurfaceY, neighbor.RiverSurfaceY, neighbor.WaterSurfaceY); } } else if (!neighbor.IsUnderwater && neighbor.Elevation > cell.WaterLevel) { // Waterfall from neighbor to cell TriangulateWaterfallInWater(e2.v4, e2.v2, e1.v4, e1.v2, neighbor.RiverSurfaceY, cell.RiverSurfaceY, cell.WaterSurfaceY); } } if (cell.GetEdgeType(direction) == HexEdgeType.Slope) { TriangulateEdgeTerraces(e1, cell, e2, neighbor, hasRoad); } else { TriangulateEdgeStrip(e1, cell.Color, e2, neighbor.Color, hasRoad); } features.AddWall(e1, cell, e2, neighbor, hasRiver, hasRoad); // Filling the gap HexCell nextNeighbor = cell.GetNeighbor(direction.Next()); if (direction <= HexDirection.E && nextNeighbor != null) { Vector3 v5 = e1.v5 + HexMetrics.GetBridge(direction.Next()); v5.y = nextNeighbor.Position.y; // Find the lowest cell if (cell.Elevation <= neighbor.Elevation) { if (cell.Elevation <= nextNeighbor.Elevation) { TriangulateCorner(e1.v5, cell, e2.v5, neighbor, v5, nextNeighbor); // cell is the lowest } else { TriangulateCorner(v5, nextNeighbor, e1.v5, cell, e2.v5, neighbor); // nextNeighbor is the lowest } } else if (neighbor.Elevation <= nextNeighbor.Elevation) { TriangulateCorner(e2.v5, neighbor, v5, nextNeighbor, e1.v5, cell); // neighbor is the lowest } else { TriangulateCorner(v5, nextNeighbor, e1.v5, cell, e2.v5, neighbor); // nextNeighbor is the lowest } } }
public HexEdgeType GetEdgeType(HexCell otherCell) { return(HexMetrics.GetEdgeType(Elevation, otherCell.Elevation)); }
public HexEdgeType GetEdgeType(HexDirection direction) { return(HexMetrics.GetEdgeType(Elevation, _neighbors[(int)direction].Elevation)); }