public AreaCollisionHelper(Area area) { collisionTiles = new List<CollisionTile>(); boxCollisionPoints = new List<Vector3>(); SetArea(area); }
public List<Area> GetAreaNeighbors(Area area) { lock (AreaCollection.CollectionLock) { var neighbors = new List<Area>(); // Get all neighbours of the current area foreach (var neighbor in AreaCollection.Areas.Concat(AreaCache.Areas)) { if (neighbor.Info.Location.X >= area.Info.Location.X - 1 && neighbor.Info.Location.X <= area.Info.Location.X + 1 && neighbor.Info.Location.Y >= area.Info.Location.Y - 1 && neighbor.Info.Location.Y <= area.Info.Location.Y + 1 && neighbor.Info.Location.Z >= area.Info.Location.Z - 1 && neighbor.Info.Location.Z <= area.Info.Location.Z + 1) { if (neighbor.Info.LocationId != area.Info.LocationId) { neighbors.Add(neighbor); } } } return neighbors; } }
private void HandleArea(Area area, Area areaMask, byte[] mask) { var location2i = new Vector2i(area.Info.Location.X, area.Info.Location.Z); random = new Random(location2i.GetHashCode()); NoiseAreaType noiseAreaType; // Loop through all columns of the area for (int z = 0; z < Area.Size.Z; z++) { int currentZ = z * Area.Size.X; for (int x = 0; x < Area.Size.X; x++) { // Skip unaffected columns if (mask[currentZ + x] == 0) { continue; } // Get the heights of the two areas at the current column location int areaHeight = areaTilesGenerators[area.Info.Type].GetHeight(location2i, x, z); int maskAreaHeight = areaTilesGenerators[areaMask.Info.Type].GetHeight(location2i, x, z); int newAreaHeight; // For the outer edges of the mask affected by this threshold, clamp the area height to the areaMask height for a rounded/smoother visual effect if (mask[currentZ + x] > AreaEdgeThreshold) { newAreaHeight = areaTilesGenerators[areaMask.Info.Type].GetHeight(location2i, x, z); noiseAreaType = areaMask.Info.Type; } else { float maskPercentage = mask[currentZ + x] / (float)AreaEdgeThreshold; newAreaHeight = Math.Max(0, areaHeight + (int)((maskAreaHeight - areaHeight) * maskPercentage)); // Find out if we should use the theme from the area or the areaMask for this tile column (weighted for a gradient effect by using the mask[]) noiseAreaType = random.Next(256) < mask[currentZ + x] ? areaMask.Info.Type : area.Info.Type; } // Lower the column if (maskAreaHeight < areaHeight) { newAreaHeight = Math.Max(0, newAreaHeight - area.Info.TileHeight.Start); areaTilesGenerators[noiseAreaType].TrimHeight(ref area, x, z, newAreaHeight); } // Raise the column else if (maskAreaHeight > areaHeight) { newAreaHeight = Math.Min(Area.Size.Y - 1, newAreaHeight - areaMask.Info.TileHeight.Start); areaTilesGenerators[noiseAreaType].ExpandHeight(ref area, x, z, newAreaHeight); } } } }
public void Merge(ref Area area, Area areaMask, byte[] mask) { if (area.Info.IsEmpty) { HandleEmptyArea(area, areaMask, mask); } else { HandleArea(area, areaMask, mask); } }
public void GenerateSurface(ref Area area) { GenerateNoiseMountains(area.Info.Location.X, area.Info.Location.Z); int index = 0; // Down/up (vertically) for (int tileY = 0; tileY < Area.Size.Y; tileY++) { int currentTileY = (Area.Size.Y - (tileY + 1)) + (area.Info.Location.Y * Area.Size.Y); // In/out for (int tileZ = 0; tileZ < Area.Size.Z; tileZ++) { int currentZ = tileZ * Area.Size.Z; // Left/right (horizontally) for (int tileX = 0; tileX < Area.Size.X; tileX++) { int currentSurfaceIndex = currentZ + tileX; int currentSurfaceY = surface[currentSurfaceIndex] - 1; // Above the ground? if (currentTileY > currentSurfaceY) { // Empty tile area.Info.Tiles[index].Type = TileType.Empty; } // At the exact ground level? else if (currentTileY == currentSurfaceY) { area.Info.Tiles[index].Type = GetSurfaceTileType(area.Info.Location, tileX, tileY, tileZ); } // Below the ground level else { area.Info.Tiles[index].Type = GetSurfaceTileType(area.Info.Location, tileX, tileY, tileZ); } index++; } } } }
private void FindCollidingTiles(Area area, ref List<Vector3> points) { // Loop through all corners of the bounding box for (int i = 0; i < points.Count; i++) { // If the current point is inside the area if (area.Info.BoundingBox.Contains(points[i]) == ContainmentType.Contains) { // Calculate the point indices within the area tile array int x = Math.Min((Area.Size.X - 1), (int)(points[i].X - area.Info.BoundingBox.Min.X)); int y = Math.Min((Area.Size.Y - 1), (int)(area.Info.BoundingBox.Max.Y - points[i].Y)); int z = Math.Min((Area.Size.Z - 1), (int)(points[i].Z - area.Info.BoundingBox.Min.Z)); int index = (Area.LevelSize * y) + (z * (int)Area.Size.Z) + x; // Get the tile var tile = area.Info.Tiles[index]; // If the tile is visible and hasnt been added yet if (tile.Type != TileType.Empty && !CollisionTilesContains(x, y, z)) { var result = new CollisionTile() { Id = index.ToString(), Tile = tile, BoundingBox = new BoundingBox(new Vector3(area.Info.BoundingBox.Min.X + x * Tile.Size.X, area.Info.BoundingBox.Max.Y - ((y + 1) * Tile.Size.Y), area.Info.BoundingBox.Min.Z + z * Tile.Size.Z), new Vector3((area.Info.BoundingBox.Min.X + x * Tile.Size.X) + Tile.Size.X, area.Info.BoundingBox.Max.Y - (y * Tile.Size.Y), (area.Info.BoundingBox.Min.Z + z * Tile.Size.Z) + Tile.Size.Z)) }; result.BoundingBox.Min -= new Vector3(0.05f); result.BoundingBox.Max += new Vector3(0.05f); collisionTiles.Add(result); } } } }
public void GenerateSurface(ref Area area) { for (int y = 0; y < Area.Size.Y; y++) { int currentY = y * Area.LevelSize; for (int z = 0; z < Area.Size.Z; z++) { int currentZ = z * Area.Size.X; for (int x = 0; x < Area.Size.X; x++) { int currentIndex = currentY + currentZ + x; area.Info.Tiles[currentIndex].Type = TileType.Empty; //if (y == Area.Size.Y - 1) //{ // area.Info.Tiles[currentIndex].Type = TileType.Stone; //} //else if (y == Area.Size.Y - 2 && x == 16 && z == 16) //{ // area.Info.Tiles[currentIndex].Type = TileType.Stone; //} //else if (y > 20 && x == 17 && z == 16) //{ // area.Info.Tiles[currentIndex].Type = TileType.Stone; //} //else if (y > 20 && x == 15 && z == 16) //{ // area.Info.Tiles[currentIndex].Type = TileType.Stone; //} //else if (y > 23 && x == 16 && z == 17) //{ // area.Info.Tiles[currentIndex].Type = TileType.Stone; //} } } } }
public void ExpandHeight(ref Area area, int x, int z, int height) { if (height < 1) { return; } int index = (z * Area.Size.X) + ((Area.Size.Y - 1) * Area.LevelSize) + x; for (int y = 0; y < Area.Size.Y; y++) { if (y < height || height == Area.Size.Y - 1) { area.Info.Tiles[index].Type = TileType.Stone; } else if (y == height) { area.Info.Tiles[index].Type = GetSurfaceTileType(area.Info.Location, x, y, z); } index -= Area.LevelSize; } }
/// <summary> /// Sets the area to perform collision tests with /// </summary> /// <param name="area"></param> public void SetArea(Area area) { collisionArea = area; }
/// <summary> /// Calculates if the area is visible within a camera viewport or not /// </summary> /// <param name="area">The area</param> /// <param name="camera">The camera to check against</param> /// <returns>True if the area is visible, false if not</returns> public bool IsAreaVisible(Area area, CameraBase camera) { if (area.Info.IsEmpty) { return false; } // Calculate the distance from the area to the camera areaToCameraDistance = (area.Info.Center - camera.Position).Length(); // Is the area within stand-on or next-to distance if (areaToCameraDistance < areasAlwaysVisibleWithinDistance) { return true; } // Calculate the normal from the area pointing towards the camera position areaLookAtNormal = (camera.Position - area.Info.Center); areaLookAtNormal.Normalize(); return Vector3.Dot(camera.LookAtNormal, areaLookAtNormal) <= -0.5f; }
private void MoveVisibleAreaToCache(Area area) { area.Model.Clear(); AreaCollection.Remove(area); AreaCache.Add(area); }
public void TrimHeight(ref Area area, int x, int z, int height) { int index = (z * Area.Size.X) + ((Area.Size.Y - 1) * Area.LevelSize) + x; for (int y = 0; y < Area.Size.Y; y++) { // Above the surface if (y >= height) { area.Info.Tiles[index].Type = TileType.Empty; } // At the surface else if (y == height - 1) { area.Info.Tiles[index].Type = GetSurfaceTileType(area.Info.Location, x, y, z); } // Below the surface else { area.Info.Tiles[index].Type = TileType.Stone; } index -= Area.LevelSize; } }
public int GetHeight(Vector2i location, int x, int z) { if (!heightMapCache.HeightMaps.ContainsKey(location)) { var area = new Area(); area.Info.Location = new Vector3i(location.X, 0, location.Y); GenerateSurface(ref area); } return heightMapCache.HeightMaps[location][(z * Area.Size.X) + x]; }
public void GenerateUnderground(ref Area area) { for (int i = 0; i < area.Info.Tiles.Length; i++) { area.Info.Tiles[i].Type = TileType.Mud; } }
private void HandleEmptyArea(Area area, Area areaMask, byte[] mask) { var location2i = new Vector2i(area.Info.Location.X, area.Info.Location.Z); bool isEmpty = true; // Loop through all columns of the area for (int z = 0; z < Area.Size.Z; z++) { int currentZ = z * Area.Size.X; for (int x = 0; x < Area.Size.X; x++) { // Skip unaffected columns if (mask[currentZ + x] == 0) { continue; } // Get the heights of the two areas at the current column location int areaHeight = areaTilesGenerators[area.Info.Type].GetHeight(location2i, x, z); int maskAreaHeight = areaTilesGenerators[areaMask.Info.Type].GetHeight(location2i, x, z); // Calculate the new height based on the weight between the two areas float maskPercentage = mask[currentZ + x] / 255f; int delta = Math.Max(0, (int)((maskAreaHeight - areaHeight) * maskPercentage)); int newHeight = areaHeight + delta; // Completely filled column if (newHeight > area.Info.TileHeight.End) { isEmpty = false; areaTilesGenerators[area.Info.Type].ExpandHeight(ref area, x, z, newHeight); } // The column height is within the bounds of the area else if (delta > 0) { newHeight -= area.Info.TileHeight.Start; areaTilesGenerators[area.Info.Type].ExpandHeight(ref area, x, z, newHeight); if (isEmpty) { isEmpty = newHeight > 0; } } } } area.Info.IsEmpty = isEmpty; }
private bool CanSpawnInArea(Area area, BoundingBox boundingBox) { // Handle empty areas if (area.Info.IsEmpty) { SnapBoundingBoxToBottomOfArea(area, ref boundingBox); info.IsAtBottomOfArea = true; info.Position = boundingBoxHelper.GetCenter(boundingBox); return true; } areaCollisionHelper.SetArea(area); float boundsHeight = boundingBox.Max.Y - boundingBox.Min.Y; // Loop through all tiles in the area and perform collision checks against the bounding box, looping from the bottom of the area and up for (int y = 0; y < Area.Size.Y; y++) { int worldY = area.Info.Location.Y * Area.Size.Y; // Adjust the bounding box height boundingBox.Min.Y = worldY + y; boundingBox.Max.Y = boundingBox.Min.Y + boundsHeight; // Get all intersecting tiles for the bounding box at the current height var intersectingTiles = areaCollisionHelper.GetIntersectingTiles(boundingBox); // Found a suitable position? if (intersectingTiles.Count == 0) { //ImageExporter.AreaSliceOnXAxisToBitmap("AreaSpawnPointHelper_Plains.png", area, 16); info.IsAtBottomOfArea = (y == 0); info.Position = boundingBoxHelper.GetCenter(boundingBox); return true; } } // Failed to find a suitable spawnpoint in this area return false; }
public Area Generate(int x, int y, int z) { // Initialize the area var area = new Area(); area.Info.Location = new Vector3i(x, y, z); // Generate the area types surrounding this location noiseManager.World.Generators[NoiseWorldType.Default].Generate(x - 1, y - 1); byte[] areaTypes = noiseManager.World.Generators[NoiseWorldType.Default].Output; // Surface level terrain if (y >= 0) { NoiseAreaType areaTheme; if (x < 0) { areaTheme = NoiseAreaType.Plains; noiseManager.Areas.Generators[NoiseAreaType.Plains].Generate(x, z); surface = noiseManager.Areas.Generators[NoiseAreaType.Plains].Output; } else { areaTheme = NoiseAreaType.Mountains; noiseManager.Areas.Generators[NoiseAreaType.Mountains].Generate(x, z); surface = noiseManager.Areas.Generators[NoiseAreaType.Mountains].Output; } // Down/up (vertically) for (int tileY = 0; tileY < Area.Size.Y; tileY++) { int currentY = tileY * Area.LevelSize; int currentTileY = (Area.Size.Y - (tileY + 1)) + (y * Area.Size.Y); // In/out for (int tileZ = 0; tileZ < Area.Size.Z; tileZ++) { int currentZ = tileZ * Area.Size.Z; // Left/right (horizontally) for (int tileX = 0; tileX < Area.Size.X; tileX++) { int currentIndex = currentY + currentZ + tileX; int currentSurfaceIndex = currentZ + tileX; int currentSurfaceY = surface[currentSurfaceIndex]; // Above the ground? if (currentTileY > currentSurfaceY) { // Empty tile area.Info.Tiles[currentIndex].Type = TileType.Empty; } // At the exact ground level? else if (currentTileY == currentSurfaceY) { if (areaTheme == NoiseAreaType.Plains) { area.Info.Tiles[currentIndex].Type = TileType.Grass; } else if (areaTheme == NoiseAreaType.Mountains) { area.Info.Tiles[currentIndex].Type = TileType.Sand; } } // Below the ground level else { area.Info.Tiles[currentIndex].Type = TileType.Sand; } } } } } // Underground level terrain else { for (int i = 0; i < area.Info.Tiles.Count(); i++) { area.Info.Tiles[i].Type = TileType.Sand; } } UpdateAreaInfo(ref area.Info); return area; }
private void SnapBoundingBoxToBottomOfArea(Area area, ref BoundingBox boundingBox) { float boundsHeight = boundingBox.Max.Y - boundingBox.Min.Y; int worldY = area.Info.Location.Y * Area.Size.Y; boundingBox.Min.Y = worldY; boundingBox.Max.Y = boundingBox.Min.Y + boundsHeight; }
public void Render(GameTime gameTime, CameraBase camera) { // Clear old content VisibleTerrainMeshes.Clear(); // Loop through all areas to see which ones should be rendered for (int i = 0; i < terrainVisibility.AreaCollection.Areas.Count; i++) { area = terrainVisibility.AreaCollection.Areas[i]; // Skip areas not visible to the camera if (!terrainVisibility.IsAreaVisible(area, camera)) { continue; } // Loop through all meshes within the current area and add them to the visible groups foreach (KeyValuePair<TileType, TerrainMesh> meshPair in area.Model.Meshes) { // Initialize the current mesh-group key if needed if (!VisibleTerrainMeshes.ContainsKey(meshPair.Key)) { VisibleTerrainMeshes.Add(meshPair.Key, new List<TerrainMesh>()); } meshPair.Value.Offset.X = area.Info.Location.X * Area.Size.X; meshPair.Value.Offset.Y = (area.Info.Location.Y * Area.Size.Y); meshPair.Value.Offset.Z = area.Info.Location.Z * Area.Size.Z; // Add the mesh to the visible groups VisibleTerrainMeshes[meshPair.Key].Add(meshPair.Value); } } // Initiate rendering var device = context.Graphics.Device; var effect = context.Graphics.Effect as BasicEffect; RenderStateContext.SetRenderStates(device); // Loop through all visible meshes foreach (var meshCollection in VisibleTerrainMeshes) { // Set the current texture effect.Texture = context.Resources.Textures["Tile" + ((ushort)meshCollection.Key).ToString()]; // Loop through all meshes using the current texture for (int i = 0; i < meshCollection.Value.Count; i++) { mesh = meshCollection.Value[i]; // Skip empty meshes if (mesh.VertexCount == 0 || mesh.VertexBuffer == null) { continue; } // Set the vertex and index data to be rendered device.SetVertexBuffer(mesh.VertexBuffer); device.Indices = mesh.IndexBuffer; // Translate the mesh effect.World = Matrix.CreateTranslation(mesh.Offset); // Loop through all effect passes for (int j = 0; j < context.Graphics.Effect.CurrentTechnique.Passes.Count; j++) { // Apply the current effect pass context.Graphics.Effect.CurrentTechnique.Passes[j].Apply(); // Render the mesh device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, mesh.VertexCount, 0, mesh.IndexCount / 3); } } } }