Пример #1
0
        /// <summary>
        /// Draw the fringe tiles such as grass and rocks.
        /// </summary>
        /// <param name="spriteBatch">The sprite batch that is being drawn.</param>
        /// <param name="material">The terrain material.</param>
        /// <param name="terrainBounds">The bounds of the terrain block in terrain units.</param>
        /// <param name="groundNodes">The ground nodes which represents the walkable ground in the terrain on which the
        /// fringe sprites will be rendered.</param>
        private void DrawTerrainFringe(
            SpriteBatch spriteBatch,
            TerrainMaterial material,
            Square terrainBounds,
            Dictionary<Point, LinkedPathNode> groundNodes)
        {
            // Determine which segments (if any) of this terrain block are ground nodes
            var groundPoints = new List<Tuple<int, int>>();
            int groundY = terrainBounds.Y - 1;
            int currentStart = -1;
            int currentLength = 0;
            for (int x = terrainBounds.X; x < terrainBounds.Right; x++)
            {
                // Check if this point is a ground node
                if (groundNodes.ContainsKey(new Point(x, groundY)))
                {
                    // This is a ground node, so add it to the current point set
                    if (currentStart != -1)
                    {
                        currentLength++;
                    }
                    else
                    {
                        currentStart = x;
                        currentLength = 1;
                    }
                }
                else
                {
                    // This is not a ground node. If a range was being build, add it to the collection
                    if (currentStart != -1)
                    {
                        groundPoints.Add(Tuple.Create(currentStart, currentLength));
                        currentStart = -1;
                    }
                }
            }

            // Add the last x range
            if (currentStart != -1)
            {
                groundPoints.Add(Tuple.Create(currentStart, currentLength));
                currentStart = -1;
            }

            // If there are no ground points for this terrain block, there is nothing to render
            if (groundPoints.Count == 0)
            {
                return;
            }

            // Get the variations of the lower fringe sprites
            // TODO: Use the TerrainMaterial value, rather than just using mud here
            var lowerFringeRects = new Dictionary<int, Rectangle>();
            foreach (int variation in this.resources.GetSpriteVariations("terrain", "lowerfringe", "mud"))
            {
                lowerFringeRects.Add(variation, this.resources.GetSpriteRectangle("terrain", "lowerfringe", "mud", variation));
            }

            // Get the variations of the upper fringe sprites
            // TODO: Use the TerrainMaterial value, rather than just using mud here
            var upperFringeRects = new Dictionary<int, Rectangle>();
            foreach (int variation in this.resources.GetSpriteVariations("terrain", "upperfringe", "mud"))
            {
                upperFringeRects.Add(variation, this.resources.GetSpriteRectangle("terrain", "upperfringe", "mud", variation));
            }

            // Do nothing if no sprites exist for this terrain material
            if (lowerFringeRects.Count == 0 && upperFringeRects.Count == 0)
            {
                return;
            }

            // Determine the offset of the left side of the tile
            int offsetX = terrainBounds.X % Const.TileSize;

            // Draw the sprites
            foreach (Tuple<int, int> range in groundPoints)
            {
                int startX = range.Item1;
                int width = range.Item2;

                for (int x = startX; x < startX + width; x += Const.TileSize)
                {
                    // Calculate the x position of the tile
                    int tileX = x - offsetX;

                    // Get a random sprite seeded by the x/y tile coordinate
                    int lowerFringeIndex = new Random(tileX ^ terrainBounds.Y).Next(0, lowerFringeRects.Count);
                    Rectangle lowerFringeRect = lowerFringeRects.ElementAt(lowerFringeIndex).Value;
                    int upperFringeIndex = new Random(tileX ^ terrainBounds.Y).Next(0, upperFringeRects.Count);
                    Rectangle upperFringeRect = upperFringeRects.ElementAt(upperFringeIndex).Value;

                    // Clip the left bounds
                    if (tileX < startX)
                    {
                        int diff = startX - tileX;
                        tileX += diff;
                        lowerFringeRect.X += diff;
                        lowerFringeRect.Width -= diff;
                        upperFringeRect.X += diff;
                        upperFringeRect.Width -= diff;
                    }

                    // Clip the right bounds
                    if (x + lowerFringeRect.Width > startX + width)
                    {
                        int newWidth = startX + width - x;
                        lowerFringeRect.Width = newWidth;
                        upperFringeRect.Width = newWidth;
                    }

                    // Create the dest rectangles
                    var lowerFringeDestRect =
                        new Rectangle(tileX, terrainBounds.Y, lowerFringeRect.Width, lowerFringeRect.Height);
                    var upperFringeDestRect = new Rectangle(
                        tileX,
                        terrainBounds.Y - upperFringeRect.Height, // Upper fringe is above ground, so offset by its height
                        upperFringeRect.Width,
                        upperFringeRect.Height);

                    // Draw the sprites
                    spriteBatch.Draw(this.resources.SpriteSheet, lowerFringeDestRect, lowerFringeRect, Color.White);
                    spriteBatch.Draw(this.resources.SpriteSheet, upperFringeDestRect, upperFringeRect, Color.White);
                }
            }
        }
Пример #2
0
 /// <summary>
 /// Determine whether this square contains the given square.
 /// </summary>
 /// <param name="other">The square to test.</param>
 /// <returns>True if this square contains the given square.</returns>
 public bool Contains(Square other)
 {
     return
         other.Top >= this.Top &&
         other.Left >= this.Left &&
         other.Bottom <= this.Bottom &&
         other.Right <= this.Right;
 }
Пример #3
0
 /// <summary>
 /// Determine whether this square intersects the given square.
 /// </summary>
 /// <param name="other">The square to test.</param>
 /// <returns>True if this square intersects the given square.</returns>
 public bool Intersects(Square other)
 {
     return
         !(this.Bottom <= other.Top ||
         this.Top >= other.Bottom ||
         this.Right <= other.Left ||
         this.Left >= other.Right);
 }
Пример #4
0
        /// <summary>
        /// Populate the quad tree quadrants in the given bounds from the bit map data.
        /// <para />
        /// This function will be recursively called the on the four sub-quadrants of this quadrant. If all of the sub-
        /// quandrants share the same material, then no data is added to the quad tree and the material is returned.
        /// The data will be set at a higher level in the quad tree hierarchy. The reason for this is because if the
        /// parent quadrant is also fully uniform, then any quadrants below it (this one) would have be removed,
        /// wasting processor time.
        /// <para />
        /// If any of the sub-quadrants are of a fully uniform material (yet not *all* sub-quadrants like in the above
        /// case) then the data for those sub-quadrants is set in the quad tree here, making those sub-quadrants leaf
        /// nodes (an example of this could be Q1 and Q2 returning TerrainMaterial.None, Q3 returning
        /// TerrainMaterial.Mud, and Q4 returning null since it is a 'mixed' quadrant and the data is set at a lower
        /// level. In this situation Q1, Q2 and Q3 would have their data set at this level).
        /// </summary>
        /// <param name="quadTree">The quad tree to populate.</param>
        /// <param name="bounds">The bounds of the quadrant whose sub-quandrants are being populated.</param>
        /// <param name="bitmapData">The bit map color data.</param>
        /// <param name="bitmapWidth">The width in pixels of the bitmap image.</param>
        /// <param name="currentTime">The current time used as the terrain creation time.</param>
        /// <returns>The material value if all sub-quadrants in this bound are filled with the same material; Null if
        /// the sub-quadrants have various materials.</returns>
        private TerrainMaterial? PopulateQuadrants(
            ClipQuadTree<TerrainData> quadTree,
            Square bounds,
            Color[] bitmapData,
            int bitmapWidth,
            TimeSpan currentTime)
        {
            if (bounds.Length > 1)
            {
                // Get the sub-quadrant bounds of this quadrant
                Square topLeftBounds = bounds.GetTopLeftQuadrant();
                Square topRightBounds = bounds.GetTopRightQuadrant();
                Square bottomLeftBounds = bounds.GetBottomLeftQuadrant();
                Square bottomRightBounds = bounds.GetBottomRightQuadrant();

                // Populate the sub quadrants or if they are uniform get the single material they contain
                TerrainMaterial? topLeftTerrain =
                    this.PopulateQuadrants(quadTree, topLeftBounds, bitmapData, bitmapWidth, currentTime);
                TerrainMaterial? topRightTerrain =
                    this.PopulateQuadrants(quadTree, topRightBounds, bitmapData, bitmapWidth, currentTime);
                TerrainMaterial? bottomLeftTerrain =
                    this.PopulateQuadrants(quadTree, bottomLeftBounds, bitmapData, bitmapWidth, currentTime);
                TerrainMaterial? bottomRightTerrain =
                    this.PopulateQuadrants(quadTree, bottomRightBounds, bitmapData, bitmapWidth, currentTime);

                // Check if all of the quadrants are uniform and all have the same material
                if (topLeftTerrain.HasValue &&
                    topLeftTerrain == topRightTerrain &&
                    topRightTerrain == bottomLeftTerrain &&
                    bottomLeftTerrain == bottomRightTerrain)
                {
                    // All quadrants have the same material. Return this to create the quadrant at a higher level
                    return topLeftTerrain;
                }
                else
                {
                    // The quadrants do not all share the same material. However, they may themselves be uniform in
                    // which case their quadrant values need to be set in the quad tree
                    if (topLeftTerrain.HasValue)
                    {
                        // Quadrant has a uniform color
                        quadTree.SetData(
                            new TerrainData(topLeftTerrain.Value, currentTime), topLeftBounds, null);
                    }

                    if (topRightTerrain.HasValue)
                    {
                        // Quadrant has a uniform color
                        quadTree.SetData(
                            new TerrainData(topRightTerrain.Value, currentTime), topRightBounds, null);
                    }

                    if (bottomLeftTerrain.HasValue)
                    {
                        // Quadrant has a uniform color
                        quadTree.SetData(
                            new TerrainData(bottomLeftTerrain.Value, currentTime), bottomLeftBounds, null);
                    }

                    if (bottomRightTerrain.HasValue)
                    {
                        // Quadrant has a uniform color
                        quadTree.SetData(
                            new TerrainData(bottomRightTerrain.Value, currentTime), bottomRightBounds, null);
                    }

                    // Return null since there isnt a single color shared between all the quadrants
                    return null;
                }
            }
            else
            {
                // Get the terrain material at this pixel
                if (bounds.X < bitmapWidth)
                {
                    int dataIndex = bounds.X + (bounds.Y * bitmapWidth);
                    if (dataIndex < bitmapData.Length)
                    {
                        return TerrainMaterialConverter.GetValue(bitmapData[dataIndex]);
                    }
                    else
                    {
                        // The index is out of bounds for this quadrant. This will happen if the bitmap image is not a
                        // square with a power-of-2 length. Just return TerrainMaterial.None
                        return TerrainMaterial.None;
                    }
                }
                else
                {
                    // The index is out of bounds for this quadrant. This will happen if the bitmap image is not a
                    // square with a power-of-2 length. Just return TerrainMaterial.None
                    return TerrainMaterial.None;
                }
            }
        }