private bool HasWaterInDirection( IHexCell cell, HexDirection direction, IEnumerable <IHexCell> oceanCells ) { var neighbor = Grid.GetNeighbor(cell, direction); return(neighbor != null && (neighbor.Terrain.IsWater() || oceanCells.Contains(neighbor))); }
//The fact that we're building sections only from the NE, E, and SE //cell edges is very important. A lot of the code here is structured //as it is to deal with this reality. public void RefreshRiverSections() { sections.Clear(); SectionBetweenCells.Clear(); CellEdgeContourCanon.Clear(); foreach (var cell in Grid.Cells) { for (HexDirection direction = HexDirection.NE; direction <= HexDirection.SE; direction++) { if (RiverCanon.HasRiverAlongEdge(cell, direction)) { var neighbor = Grid.GetNeighbor(cell, direction); Vector3 start = cell.AbsolutePosition + RenderConfig.GetFirstCorner(direction); Vector3 end = cell.AbsolutePosition + RenderConfig.GetSecondCorner(direction); RiverFlow flow = RiverCanon.GetFlowOfRiverAtEdge(cell, direction); //Our control points need to operate differently if we're at endpoints, //and both ControlOne and ControlTwo might have alternate behavior. //We need to check both ends of the section for endpoints bool previousCellRiver = RiverCanon.HasRiverAlongEdge(cell, direction.Previous()); bool previousNeighborRiver = RiverCanon.HasRiverAlongEdge(neighbor, direction.Previous2()); bool hasPreviousEndpoint = !previousCellRiver && !previousNeighborRiver; bool hasNextEndpoint = !RiverCanon.HasRiverAlongEdge(cell, direction.Next()) && !RiverCanon.HasRiverAlongEdge(neighbor, direction.Next2()); var newRiverSection = new RiverSection() { AdjacentCellOne = cell, AdjacentCellTwo = neighbor, DirectionFromOne = direction, Start = start, End = end, FlowFromOne = flow, HasPreviousEndpoint = hasPreviousEndpoint, HasNextEndpoint = hasNextEndpoint }; SectionBetweenCells[new Tuple <IHexCell, IHexCell>(cell, neighbor)] = newRiverSection; sections.Add(newRiverSection); } } } }
public void TriangulateRoads(IHexCell cell, IHexMesh roadsMesh) { if (cell.HasRoads) { var cellHash = NoiseGenerator.SampleHashGrid(cell.AbsolutePositionXZ); var directionsWithRoad = EnumUtil.GetValues <HexDirection>().Where( direction => Grid.HasNeighbor(cell, direction) && Grid.GetNeighbor(cell, direction).HasRoads ).ToList(); while (directionsWithRoad.Any()) { var startDirection = directionsWithRoad.First(); directionsWithRoad.Remove(startDirection); BezierSpline spline; if (directionsWithRoad.Any()) { var endDirection = GetBestDirection(startDirection, directionsWithRoad, cellHash); directionsWithRoad.Remove(endDirection); spline = BuildSplineBetween(cell, startDirection, endDirection); } else { spline = BuildSplineToCenter(cell, startDirection); } RenderSpline(spline, roadsMesh); } } }
public void TriangulateMarshes(IHexCell center, IHexMesh mesh) { foreach (var direction in EnumUtil.GetValues <HexDirection>()) { if (center.Vegetation == CellVegetation.Marsh) { TriangulateMarshCenter(center, direction, mesh); } if (direction > HexDirection.SE) { continue; } IHexCell right = Grid.GetNeighbor(center, direction); IHexCell left = Grid.GetNeighbor(center, direction.Previous()); if (right == null) { continue; } float centerV = center.Vegetation == CellVegetation.Marsh ? 1f : 0f; float leftV = left != null && left.Vegetation == CellVegetation.Marsh ? 1f : 0f; float rightV = right != null && right.Vegetation == CellVegetation.Marsh ? 1f : 0f; if (centerV != 0f || rightV != 0f) { TriangulateMarshEdge(center, right, centerV, rightV, direction, mesh); } if (direction > HexDirection.E) { continue; } if (left == null) { continue; } if (centerV != 0f || leftV != 0f || rightV != 0f) { TriangulateMarshCorner(center, left, right, centerV, leftV, rightV, direction, mesh); } } }
public void TriangulateContoursBetween( IHexCell center, IHexCell right, HexDirection direction, Color centerWeights, Color rightWeights, IHexMesh mesh ) { var centerRightContour = CellContourCanon.GetContourForCellEdge(center, direction); var rightCenterContour = CellContourCanon.GetContourForCellEdge(right, direction.Opposite()); int centerRightIndex = 1, rightCenterIndex = rightCenterContour.Count - 1; while (centerRightIndex < centerRightContour.Count && rightCenterIndex > 0) { mesh.AddQuad( centerRightContour[centerRightIndex - 1].ToXYZ(), centerRightContour[centerRightIndex].ToXYZ(), rightCenterContour[rightCenterIndex].ToXYZ(), rightCenterContour[rightCenterIndex - 1].ToXYZ() ); mesh.AddQuadColor(centerWeights, rightWeights); centerRightIndex++; rightCenterIndex--; } for (; centerRightIndex < centerRightContour.Count; centerRightIndex++) { mesh.AddTriangle( centerRightContour[centerRightIndex - 1].ToXYZ(), rightCenterContour[0].ToXYZ(), centerRightContour[centerRightIndex].ToXYZ() ); mesh.AddTriangleColor(centerWeights, rightWeights, centerWeights); } for (; rightCenterIndex > 0; rightCenterIndex--) { mesh.AddTriangle( centerRightContour.Last().ToXYZ(), rightCenterContour[rightCenterIndex].ToXYZ(), rightCenterContour[rightCenterIndex - 1].ToXYZ() ); mesh.AddTriangleColor(centerWeights, rightWeights, rightWeights); } if (RiverCanon.HasRiverAlongEdge(right, direction.Next2())) { var nextRight = Grid.GetNeighbor(center, direction.Next()); var rightNextRightContour = CellContourCanon.GetContourForCellEdge(right, direction.Next2()); var nextRightRightContour = CellContourCanon.GetContourForCellEdge(nextRight, direction.Previous()); mesh.AddTriangle( centerRightContour.Last().ToXYZ(), rightNextRightContour.Last().ToXYZ(), nextRightRightContour.First().ToXYZ() ); mesh.AddTriangleColor(centerWeights, rightWeights, rightWeights); } }
public void RationalizeRiverContoursInCorner(IHexCell center, HexDirection direction) { IHexCell left = Grid.GetNeighbor(center, direction.Previous()); //Contours will only fall short or overlap when we're at a river confluence, //so we can save ourselves time by checking for that if (left != null && RiverCanon.HasRiverAlongEdge(center, direction.Previous()) && RiverCanon.HasRiverAlongEdge(center, direction) && RiverCanon.HasRiverAlongEdge(left, direction.Next()) ) { IHexCell right = Grid.GetNeighbor(center, direction); RiverSection centerLeftSection = RiverSectionCanon.GetSectionBetweenCells(center, left); RiverSection centerRightSection = RiverSectionCanon.GetSectionBetweenCells(center, right); bool shouldCullContours = true; //Rationalizing contours also doesn't need to occur between segments //that are adjacent segments of the same river, which would put us on //the inside of a river curve. So we make some checks to exclude that //possibility, as well foreach (var river in RiverAssemblyCanon.Rivers) { int leftIndex = river.IndexOf(centerLeftSection); int rightIndex = river.IndexOf(centerRightSection); bool areAdjacentInRiver = ((leftIndex + 1) == rightIndex) || ((rightIndex + 1) == leftIndex); if (leftIndex != -1 && rightIndex != -1 && areAdjacentInRiver) { shouldCullContours = false; break; } } if (shouldCullContours) { ContourRationalizer.RationalizeCellContours(center, direction); } } }
public void TriangulateCultureInDirection( IHexCell center, HexDirection direction, IHexMesh cultureMesh ) { var centerOwner = CivTerritoryLogic.GetCivClaimingCell(center); if (centerOwner == null) { return; } IHexCell left = Grid.GetNeighbor(center, direction.Previous()); IHexCell right = Grid.GetNeighbor(center, direction); IHexCell nextRight = Grid.GetNeighbor(center, direction.Next()); if (right == null) { return; } if (CivTerritoryLogic.GetCivClaimingCell(right) != centerOwner) { TriangulateCultureAlongContour(center, direction, centerOwner.Template.Color, cultureMesh); } else { var centerRightContour = CellEdgeContourCanon.GetContourForCellEdge(center, direction); var rightCenterContour = CellEdgeContourCanon.GetContourForCellEdge(right, direction.Opposite()); if (rightCenterContour.Count <= 3) { TriangulateCultureCorners_FlatEdge( center, left, right, nextRight, direction, centerOwner, centerRightContour, rightCenterContour, cultureMesh ); } else { TriangulateCultureCorners_River( center, left, right, nextRight, direction, centerOwner, centerRightContour, cultureMesh ); } } }
public void GetOrientationDataFromColors( PointOrientationData dataToUse, byte[] indexBytes, Color32 orientationColor, Color weightsColor, Color duckColor ) { indexBytes[0] = orientationColor.r; indexBytes[1] = orientationColor.g; int index = BitConverter.ToInt16(indexBytes, 0) - 1; var center = index >= 0 && index < Grid.Cells.Count ? Grid.Cells[index] : null; HexDirection sextant = (HexDirection)orientationColor.b; if (center != null) { dataToUse.IsOnGrid = true; dataToUse.Center = center; dataToUse.Left = Grid.GetNeighbor(center, sextant.Previous()); dataToUse.Right = Grid.GetNeighbor(center, sextant); dataToUse.NextRight = Grid.GetNeighbor(center, sextant.Next()); dataToUse.CenterWeight = weightsColor.r; dataToUse.LeftWeight = weightsColor.g; dataToUse.RightWeight = weightsColor.b; dataToUse.NextRightWeight = weightsColor.a; float weightSum = weightsColor.r + weightsColor.g + weightsColor.b + weightsColor.a; dataToUse.RiverWeight = Mathf.Clamp01(1f - weightSum); dataToUse.ElevationDuck = duckColor.r; } else { dataToUse.Clear(); } }
public void TriangulateCellWeights(IHexCell center, IHexMesh weightsMesh) { foreach (var direction in EnumUtil.GetValues <HexDirection>()) { bool hasCenterRightRiver = RiverCanon.HasRiverAlongEdge(center, direction); IHexCell right = Grid.GetNeighbor(center, direction); if (hasCenterRightRiver) { TriangulateCellWeights_River(center, right, direction, hasCenterRightRiver, weightsMesh); } else { TriangulateCellWeights_NoRiver(center, right, direction, weightsMesh); } } }
private List <List <IHexCell> > AssembleFarmBlobs() { var retval = new List <List <IHexCell> >(); var unassignedFarms = new HashSet <IHexCell>(Grid.Cells.Where(FarmCellFilter)); var candidateQueue = new Queue <IHexCell>(); while (unassignedFarms.Count > 0) { var blobCenter = unassignedFarms.First(); var newBlob = new List <IHexCell>(); candidateQueue.Enqueue(blobCenter); while (candidateQueue.Count > 0) { var activeFarm = candidateQueue.Dequeue(); newBlob.Add(activeFarm); unassignedFarms.Remove(activeFarm); foreach (var direction in EnumUtil.GetValues <HexDirection>()) { var neighbor = Grid.GetNeighbor(activeFarm, direction); if (unassignedFarms.Contains(neighbor) && BlobExpandFilter(activeFarm, neighbor, direction)) { candidateQueue.Enqueue(neighbor); } } } retval.Add(newBlob); } return(retval); }
public void AddRiverToCell(IHexCell cell, HexDirection edge, RiverFlow flow) { if (!CanAddRiverToCell(cell, edge, flow)) { throw new InvalidOperationException("CanAddRiverToCell must return true on the given arguments"); } GetPresenceArray(cell)[(int)edge] = true; GetDirectionArray(cell)[(int)edge] = flow; var neighborAtEdge = Grid.GetNeighbor(cell, edge); if (neighborAtEdge != null) { GetPresenceArray(neighborAtEdge)[(int)(edge.Opposite())] = true; GetDirectionArray(neighborAtEdge)[(int)(edge.Opposite())] = flow.Opposite(); CellSignals.GainedRiveredEdge.OnNext(neighborAtEdge); } CellSignals.GainedRiveredEdge.OnNext(cell); }
public void TriangulateOrientation(IHexCell center, IHexMesh orientationMesh) { short indexOffset = (short)(center.Index + 1); foreach (var direction in EnumUtil.GetValues <HexDirection>()) { byte[] rg = BitConverter.GetBytes(indexOffset); byte b = (byte)direction; var cellColor = new Color32(rg[0], rg[1], b, 0); var centerContour = CellContourCanon.GetContourForCellEdge(center, direction); for (int i = 1; i < centerContour.Count; i++) { orientationMesh.AddTriangle( center.AbsolutePosition, centerContour[i - 1].ToXYZ(), centerContour[i].ToXYZ() ); orientationMesh.AddTriangleColor(cellColor); } if (direction <= HexDirection.SE && RiverCanon.HasRiverAlongEdge(center, direction)) { var right = Grid.GetNeighbor(center, direction); ContourTriangulator.TriangulateContoursBetween( center, right, direction, cellColor, cellColor, orientationMesh ); if (direction <= HexDirection.E && RiverCanon.HasRiverAlongEdge(right, direction.Previous2())) { } } } }
public RiverSection GetNextActiveSectionForRiver( RiverSection activeSection, RiverSection lastSection, HashSet <RiverSection> unassignedSections ) { IHexCell center = activeSection.AdjacentCellOne; IHexCell left = Grid.GetNeighbor(activeSection.AdjacentCellOne, activeSection.DirectionFromOne.Previous()); IHexCell right = activeSection.AdjacentCellTwo; IHexCell nextRight = Grid.GetNeighbor(activeSection.AdjacentCellOne, activeSection.DirectionFromOne.Next()); RiverSection centerLeftSection = SectionCanon.GetSectionBetweenCells(center, left); //For a section to be a valid next-step, it must be non-null and unassigned. //But we also don't want to grab a river adjacent to the previous river, //since this could cause weird behavior (go up a river, and then immediately //back down it). We thus forbid the acquisition of such rivers. //We also need to take flow into account, since it's not valid for a river //to suddenly flip its direction if (centerLeftSection != null && unassignedSections.Contains(centerLeftSection) && !SectionCanon.IsNeighborOf(centerLeftSection, lastSection) && SectionCanon.AreSectionFlowsCongruous(activeSection, centerLeftSection) ) { return(centerLeftSection); } RiverSection leftRightSection = SectionCanon.GetSectionBetweenCells(left, right); if (leftRightSection != null && unassignedSections.Contains(leftRightSection) && !SectionCanon.IsNeighborOf(leftRightSection, lastSection) && SectionCanon.AreSectionFlowsCongruous(activeSection, leftRightSection) ) { return(leftRightSection); } RiverSection centerNextRightSection = SectionCanon.GetSectionBetweenCells(center, nextRight); if (centerNextRightSection != null && unassignedSections.Contains(centerNextRightSection) && !SectionCanon.IsNeighborOf(centerNextRightSection, lastSection) && SectionCanon.AreSectionFlowsCongruous(activeSection, centerNextRightSection) ) { return(centerNextRightSection); } RiverSection rightNextRightSection = SectionCanon.GetSectionBetweenCells(right, nextRight); if (rightNextRightSection != null && unassignedSections.Contains(rightNextRightSection) && !SectionCanon.IsNeighborOf(rightNextRightSection, lastSection) && SectionCanon.AreSectionFlowsCongruous(activeSection, rightNextRightSection) ) { return(rightNextRightSection); } return(null); }
//Center should always be a water cell of some variety. //We need to extend water borders into land cells to make //sure the water goes all the way up to the shore. public void TriangulateWaterForCell(IHexCell center, Transform localTransform, IHexMesh mesh) { Vector3 localCenterPos = localTransform.InverseTransformPoint(center.AbsolutePosition); localCenterPos.y = RenderConfig.WaterY; foreach (var direction in EnumUtil.GetValues <HexDirection>()) { Color centerColor, leftColor, rightColor; GetWaterColors(center, direction, out centerColor, out leftColor, out rightColor); TriangulateWaterCenter(center, mesh, direction, localCenterPos, centerColor); var right = Grid.GetNeighbor(center, direction); if (right == null || (right.Terrain.IsWater() && direction > HexDirection.SE)) { continue; } Vector3 localRightPos = localTransform.InverseTransformPoint(right.AbsolutePosition); localRightPos.y = RenderConfig.WaterY; if (center.Terrain.IsWater()) { TriangulateWaterEdge( center, localCenterPos, centerColor, right, localRightPos, rightColor, direction, mesh ); } else if (right.Terrain.IsWater()) { TriangulateWaterEdge( right, localRightPos, rightColor, center, localCenterPos, centerColor, direction.Opposite(), mesh ); } var left = Grid.GetNeighbor(center, direction.Previous()); //We can't use the normal paradigm for corners because //we aren't triangulating from every cell. We need to //triangulate every land corner, and then exclude water //corners, which this check does. if (left == null || (right.Terrain.IsWater() && direction > HexDirection.E) || (left.Terrain.IsWater() && direction > HexDirection.SW) ) { continue; } Vector3 localLeftPos = localTransform.InverseTransformPoint(left.AbsolutePosition); localLeftPos.y = RenderConfig.WaterY; TriangulateWaterCorner( center, localCenterPos, centerColor, right, localRightPos, rightColor, left, localLeftPos, leftColor, direction, mesh ); } }
public void BuildNonRiverContour(IHexCell center, HexDirection direction) { if (RiverCanon.HasRiverAlongEdge(center, direction)) { return; } var contourPoints = new List <Vector2>(); IHexCell left = Grid.GetNeighbor(center, direction.Previous()); IHexCell right = Grid.GetNeighbor(center, direction); IHexCell nextRight = Grid.GetNeighbor(center, direction.Next()); bool hasCenterLeftRiver = RiverCanon.HasRiverAlongEdge(center, direction.Previous()); bool hasCenterNextRightRiver = RiverCanon.HasRiverAlongEdge(center, direction.Next()); bool hasLeftRightRiver = left != null && RiverCanon.HasRiverAlongEdge(left, direction.Next()); bool hasNextRightRightRiver = nextRight != null && RiverCanon.HasRiverAlongEdge(nextRight, direction.Previous()); if (hasCenterLeftRiver) { ICollection <Vector2> centerLeftContour = CellEdgeContourCanon.GetContourForCellEdge(center, direction.Previous()); contourPoints.Add(centerLeftContour.Last()); } else if (hasLeftRightRiver) { ICollection <Vector2> rightLeftContour = CellEdgeContourCanon.GetContourForCellEdge(right, direction.Previous2()); contourPoints.Add(rightLeftContour.First()); } else { contourPoints.Add(center.AbsolutePositionXZ + RenderConfig.GetFirstCornerXZ(direction)); } if (hasCenterNextRightRiver || (hasNextRightRightRiver && RiverCanon.GetFlowOfRiverAtEdge(nextRight, direction.Previous()) == RiverFlow.Clockwise) ) { ICollection <Vector2> centerNextRightContour = CellEdgeContourCanon.GetContourForCellEdge(center, direction.Next()); contourPoints.Add(centerNextRightContour.First()); } else if (hasNextRightRightRiver) { //We need to add two points here in case the edge is up against the //outflow of a river. We've decided to give the outflow over entirely //to one of the cells, but other constructions are possible. ICollection <Vector2> rightNextRightContour = CellEdgeContourCanon.GetContourForCellEdge(right, direction.Next2()); ICollection <Vector2> nextRightRightContour = CellEdgeContourCanon.GetContourForCellEdge(nextRight, direction.Previous()); contourPoints.Add(rightNextRightContour.Last()); contourPoints.Add(nextRightRightContour.First()); } else { contourPoints.Add(center.AbsolutePositionXZ + RenderConfig.GetSecondCornerXZ(direction)); } CellEdgeContourCanon.SetContourForCellEdge(center, direction, contourPoints); }
private RiverPathResults CreateRiverEndpoint( IHexCell previousCell, IHexCell endingCell, IEnumerable <IHexCell> oceanCells, HashSet <IHexCell> cellsAdjacentToRiver ) { //It's possible (though unlikely) that our endpointwd is a water cell. If //it is, we don't need to do anything.e if (IsWater(endingCell, oceanCells)) { return(RiverPathResults.Water); } //If it's not water, we next attempt to connect it to any rivers on the cell //or to nearby cells that are submerged in water. We can check and handle //both cases at the same time. var directionToEnd = GetDirectionOfNeighbor(previousCell, endingCell); var directionToPrevious = directionToEnd.Opposite(); var bottomLeftNeighbor = Grid.GetNeighbor(endingCell, directionToEnd.Previous2()); var topLeftNeighbor = Grid.GetNeighbor(endingCell, directionToEnd.Previous()); var straightAcrossNeighbor = Grid.GetNeighbor(endingCell, directionToEnd); var topRightNeighbor = Grid.GetNeighbor(endingCell, directionToEnd.Next()); var bottomRightNeighbor = Grid.GetNeighbor(endingCell, directionToEnd.Next2()); //Case triggers when there's a river or water along the bottom-left edge of //endingCell. We can treat it like a sharp CCW turn towards //the cell below and to the left of endingCell if (RiverCanon.HasRiverAlongEdge(endingCell, directionToEnd.Previous2()) || IsWater(bottomLeftNeighbor, oceanCells) ) { return(CreateRiverAlongCell_SharpCCWTurn( previousCell, endingCell, bottomLeftNeighbor, directionToPrevious, directionToEnd.Previous2(), oceanCells, cellsAdjacentToRiver )); } //Case triggers when there's a river or water along the bottom-right edge of //endingCell. We can treat it like a sharp CW //turn towards the cell below and to the right of endingCell if (RiverCanon.HasRiverAlongEdge(endingCell, directionToEnd.Next2()) || IsWater(bottomRightNeighbor, oceanCells) ) { return(CreateRiverAlongCell_SharpCWTurn( previousCell, endingCell, bottomRightNeighbor, directionToPrevious, directionToEnd.Next2(), oceanCells, cellsAdjacentToRiver )); } //Case triggers when there's a river or water along the top-left edge of //endingCell. In this case, we can treat this like a gentle CCW //turn towards the cell above and to the left of endingCell if (RiverCanon.HasRiverAlongEdge(endingCell, directionToEnd.Previous()) || IsWater(topLeftNeighbor, oceanCells) ) { return(CreateRiverAlongCell_GentleCCWTurn( previousCell, endingCell, topLeftNeighbor, directionToPrevious, directionToEnd.Previous(), oceanCells, cellsAdjacentToRiver )); } //Case triggers when there's a river or water along the top-right edge of //endingCell. We can treat it like a gentle CW //turn towards the cell above and to the right of endingCell if (RiverCanon.HasRiverAlongEdge(endingCell, directionToEnd.Next()) || IsWater(topRightNeighbor, oceanCells) ) { return(CreateRiverAlongCell_GentleCWTurn( previousCell, endingCell, topRightNeighbor, directionToPrevious, directionToEnd.Next(), oceanCells, cellsAdjacentToRiver )); } //Case triggers when there's a river or water along the top edge of //endingCell. In this case, we can treat this like a //straight-across section towards the cell above endingCell if (RiverCanon.HasRiverAlongEdge(endingCell, directionToEnd) || IsWater(straightAcrossNeighbor, oceanCells) ) { return(CreateRiverAlongCell_StraightAcross( previousCell, endingCell, straightAcrossNeighbor, directionToPrevious, directionToEnd, oceanCells, cellsAdjacentToRiver )); } return(RiverPathResults.Fail); }