/// <summary>
        /// Gets static geometry.
        /// </summary>
        /// <returns>Static geometry builder.</returns>
        public StaticGeometryBuilder GetStaticGeometry()
        {
            // Create builder
            StaticGeometryBuilder builder = new StaticGeometryBuilder(core.GraphicsDevice);

            // Convert vertices to required format
            List <VertexPositionNormalTextureBump> vertices = new List <VertexPositionNormalTextureBump>(primitive.Vertices.Count);

            foreach (var vertex in primitive.Vertices)
            {
                vertices.Add(new VertexPositionNormalTextureBump(
                                 vertex.Position,
                                 vertex.Normal,
                                 Vector2.Zero,
                                 Vector3.Zero,
                                 Vector3.Zero));
            }

            // Add geometry
            StaticGeometryBuilder.BatchData batchData = new StaticGeometryBuilder.BatchData
            {
                Vertices = vertices,
                Indices  = primitive.Indices,
            };
            builder.AddToBuilder((uint)MaterialManager.NullTextureKey, batchData, this.Matrix);

            return(builder);
        }
        /// <summary>
        /// Add billboard to collection.
        /// </summary>
        /// <param name="archive">Texture archive.</param>
        /// <param name="record">Texture index.</param>
        /// <param name="position">Position relative to parent entity.</param>
        public void AddBillboard(int archive, int record, Vector3 position)
        {
            MaterialManager.TextureCreateFlags flags =
                MaterialManager.TextureCreateFlags.Dilate |
                MaterialManager.TextureCreateFlags.PreMultiplyAlpha |
                MaterialManager.TextureCreateFlags.MipMaps;

            // Load flat
            BaseMaterialEffect material;
            Vector2            startSize, finalSize;

            LoadDaggerfallFlat(archive, record, flags, out material, out startSize, out finalSize);

            // Calcuate final position
            Vector3 finalPosition = new Vector3(position.X, -position.Y + (finalSize.Y / 2) - 4, -position.Z);

            // Information about the billboard is packed into unused parts of the vertex format.
            // This allows us to send a huge static batch of billboards and correctly position, rotate, and scale
            // each one for the camera.
            for (int i = 0; i < 4; i++)
            {
                billboardVertices[i].Tangent  = finalPosition;
                billboardVertices[i].Binormal = new Vector3(finalSize.X, finalSize.Y, 0);
            }

            // Add to batch
            staticGeometry.AddToBuilder(material.ID, billboardVertices, billboardIndices, Matrix.Identity);
        }
        /// <summary>
        /// Adds exterior ground tiles to the batch.
        /// </summary>
        /// <param name="blockData">Block data.</param>
        private void AddRMBGroundTiles(ref DFBlock blockData)
        {
            // Make ground slightly lower to minimise depth-fighting on ground aligned polygons
            const float groundHeight = 0f;

            // Corner positions
            Vector3 topLeftPos, topRightPos, bottomLeftPos, bottomRightPos;
            Vector2 topLeftUV, topRightUV, bottomLeftUV, bottomRightUV;

            // Create vertices. These will be updated for each tile based on position and UV orientation.
            VertexPositionNormalTextureBump[] vertices = new VertexPositionNormalTextureBump[4];

            // Create indices. These are the same for every tile.
            int[] indices = new int[] { 0, 1, 2, 1, 3, 2 };

            // Loop through tiles
            int   tileCount     = 16;
            float tileDimension = 256.0f * ModelManager.GlobalScale;

            for (int y = 0; y < tileCount; y++)
            {
                for (int x = 0; x < tileCount; x++)
                {
                    // Get source tile data
                    DFBlock.RmbGroundTiles tile = blockData.RmbBlock.FldHeader.GroundData.GroundTiles[x, y];

                    // Set random terrain marker back to grass
                    int textureRecord = (tile.TextureRecord > 55) ? 2 : tile.TextureRecord;

                    // Create material
                    BaseMaterialEffect material = core.ModelManager.CreateModelMaterial(
                        (int)DFLocation.ClimateTextureSet.Exterior_Terrain,
                        textureRecord);
                    material.SamplerState0 = SamplerState.AnisotropicClamp;

                    // Create vertices for this quad
                    topLeftPos     = new Vector3(x * tileDimension, groundHeight, y * tileDimension);
                    topRightPos    = new Vector3(topLeftPos.X + tileDimension, groundHeight, topLeftPos.Z);
                    bottomLeftPos  = new Vector3(topLeftPos.X, groundHeight, topLeftPos.Z + tileDimension);
                    bottomRightPos = new Vector3(topLeftPos.X + tileDimension, groundHeight, topLeftPos.Z + tileDimension);

                    // Set UVs
                    if (tile.IsRotated && !tile.IsFlipped)
                    {
                        // Rotate only
                        topLeftUV     = new Vector2(1, 0);
                        topRightUV    = new Vector2(1, 1);
                        bottomLeftUV  = new Vector2(0, 0);
                        bottomRightUV = new Vector2(0, 1);
                    }
                    else if (tile.IsFlipped && !tile.IsRotated)
                    {
                        // Flip only
                        topLeftUV     = new Vector2(1, 1);
                        topRightUV    = new Vector2(0, 1);
                        bottomLeftUV  = new Vector2(1, 0);
                        bottomRightUV = new Vector2(0, 0);
                    }
                    else if (tile.IsRotated && tile.IsRotated)
                    {
                        // Rotate and flip
                        topLeftUV     = new Vector2(0, 1);
                        topRightUV    = new Vector2(0, 0);
                        bottomLeftUV  = new Vector2(1, 1);
                        bottomRightUV = new Vector2(1, 0);
                    }
                    else
                    {
                        // No rotate or flip
                        topLeftUV     = new Vector2(0, 0);
                        topRightUV    = new Vector2(1, 0);
                        bottomLeftUV  = new Vector2(0, 1);
                        bottomRightUV = new Vector2(1, 1);
                    }

                    // Set vertices
                    vertices[0] = new VertexPositionNormalTextureBump(topLeftPos, Vector3.Up, topLeftUV, Vector3.Zero, Vector3.Zero);
                    vertices[1] = new VertexPositionNormalTextureBump(topRightPos, Vector3.Up, topRightUV, Vector3.Zero, Vector3.Zero);
                    vertices[2] = new VertexPositionNormalTextureBump(bottomLeftPos, Vector3.Up, bottomLeftUV, Vector3.Zero, Vector3.Zero);
                    vertices[3] = new VertexPositionNormalTextureBump(bottomRightPos, Vector3.Up, bottomRightUV, Vector3.Zero, Vector3.Zero);

                    // Add to builder
                    staticGeometry.AddToBuilder(material.ID, vertices, indices, Matrix.Identity);
                }
            }
        }