/// <summary>
        /// Submit a billboard to be drawn. This must be done
        ///  every frame or Draw() will do nothing.
        /// </summary>
        /// <param name="camera">Camera looking into scene.</param>
        /// <param name="node">BillboardNode</param>
        public void AddBatch(Camera camera, BillboardNode node)
        {
            // Create billboard matrix
            Matrix constrainedBillboard = Matrix.CreateConstrainedBillboard(
                Vector3.Zero,
                -camera.TransformedReference,
                Vector3.Up,
                null,
                null);

            // Create billboard transform
            Matrix transform =
                Matrix.CreateScale(node.Size.X, node.Size.Y, 1f) *
                constrainedBillboard *
                node.Matrix;

            // Calc distance between node and camera
            float distance = Vector3.Distance(
                node.TransformedBounds.Center,
                camera.Position);

            // Add to batch
            BillboardBatchItem batchItem = new BillboardBatchItem(
                distance,
                node.TextureKey,
                transform,
                node.LightIntensity);

            batch.Add(batchItem);
        }
        /// <summary>
        /// Adds RMB scenery flats to block node.
        /// </summary>
        /// <param name="block">DFBlock</param>
        /// <param name="blockNode">BlockNode.</param>
        /// <param name="sceneryArchive">Scenery texture archive index.</param>
        private void AddRMBSceneryFlats(ref DFBlock block, BlockNode blockNode, int sceneryArchive)
        {
            // Flags
            TextureManager.TextureCreateFlags flags =
                TextureManager.TextureCreateFlags.Dilate |
                TextureManager.TextureCreateFlags.PreMultiplyAlpha;
            if (Core.GraphicsProfile == GraphicsProfile.HiDef)
            {
                flags |= TextureManager.TextureCreateFlags.MipMaps;
            }

            // Add block scenery
            for (int y = 0; y < 16; y++)
            {
                for (int x = 0; x < 16; x++)
                {
                    // Get scenery item
                    DFBlock.RmbGroundScenery scenery =
                        block.RmbBlock.FldHeader.GroundData.GroundScenery[x, y];

                    // Ignore 0 as this appears to be a marker/waypoint of some kind
                    if (scenery.TextureRecord > 0)
                    {
                        // Load flat
                        int     textureKey;
                        Vector2 startSize;
                        Vector2 finalSize;
                        if (true == LoadDaggerfallFlat(
                                sceneryArchive,
                                scenery.TextureRecord,
                                flags,
                                out textureKey,
                                out startSize,
                                out finalSize))
                        {
                            // Calcuate position
                            Vector3 position = new Vector3(
                                x * tileSide,
                                (finalSize.Y / 2) - 4,
                                -rmbSide + y * tileSide);

                            // Create billboard node
                            BillboardNode billboardNode = new BillboardNode(
                                BillboardNode.BillboardType.ClimateScenery,
                                textureKey,
                                finalSize);
                            billboardNode.Position = position;
                            blockNode.Add(billboardNode);
                        }
                    }
                }
            }
        }
        /// <summary>
        /// Adds miscellaneous RMB flats to block node.
        /// </summary>
        /// <param name="block">DFBlock</param>
        /// <param name="blockNode">BlockNode.</param>
        private void AddRMBMiscFlats(ref DFBlock block, BlockNode blockNode)
        {
            // Iterate through all misc flat records
            foreach (DFBlock.RmbBlockFlatObjectRecord obj in block.RmbBlock.MiscFlatObjectRecords)
            {
                // Get flat type
                BillboardNode.BillboardType billboardType =
                    GetFlatType(obj.TextureArchive);

                // Flags
                TextureManager.TextureCreateFlags flags =
                    TextureManager.TextureCreateFlags.Dilate |
                    TextureManager.TextureCreateFlags.PreMultiplyAlpha;
                if (Core.GraphicsProfile == GraphicsProfile.HiDef)
                {
                    flags |= TextureManager.TextureCreateFlags.MipMaps;
                }

                // Load flat
                int     textureKey;
                Vector2 startSize;
                Vector2 finalSize;
                if (true == LoadDaggerfallFlat(
                        obj.TextureArchive,
                        obj.TextureRecord,
                        flags,
                        out textureKey,
                        out startSize,
                        out finalSize))
                {
                    // Calcuate position
                    Vector3 position = new Vector3(
                        obj.XPos,
                        -obj.YPos + (finalSize.Y / 2) - 4,
                        -rmbSide + -obj.ZPos);

                    // Create billboard node
                    BillboardNode billboardNode = new BillboardNode(
                        billboardType,
                        textureKey,
                        finalSize);
                    billboardNode.Position = position;
                    blockNode.Add(billboardNode);

                    // Add point light node
                    if (billboardType == BillboardNode.BillboardType.Light)
                    {
                        AddPointLight(position, PointLightNode.ExteriorRadius, blockNode);
                    }
                }
            }
        }
        /// <summary>
        /// Adds RDB flat to scene node.
        /// </summary>
        /// <param name="obj">RdbObject.</param>
        /// <param name="blockNode">BlockNode.</param>
        private void AddRDBFlat(DFBlock.RdbObject obj, BlockNode blockNode)
        {
            // Get flat type
            BillboardNode.BillboardType billboardType =
                GetFlatType(obj.Resources.FlatResource.TextureArchive);

            // Add light if needed

            // Load flat
            int     textureKey;
            Vector2 startSize;
            Vector2 finalSize;

            if (true == LoadDaggerfallFlat(
                    obj.Resources.FlatResource.TextureArchive,
                    obj.Resources.FlatResource.TextureRecord,
                    TextureManager.TextureCreateFlags.Dilate |
                    TextureManager.TextureCreateFlags.PreMultiplyAlpha,
                    out textureKey,
                    out startSize,
                    out finalSize))
            {
                // Foliage (TEXTURE.500 and up) do not seem to use scaling
                // in dungeons. Revert scaling.
                if (obj.Resources.FlatResource.TextureArchive > 499)
                {
                    finalSize = startSize;
                }

                // Calcuate position
                Vector3 position = new Vector3(
                    obj.XPos,
                    -obj.YPos,
                    -obj.ZPos);

                // Create billboard node
                BillboardNode billboardNode = new BillboardNode(
                    billboardType,
                    textureKey,
                    finalSize);
                billboardNode.Position = position;
                blockNode.Add(billboardNode);

                // Add point light node
                if (billboardType == BillboardNode.BillboardType.Light)
                {
                    AddPointLight(position, PointLightNode.DungeonRadius, blockNode);
                }
            }
        }
Example #5
0
 /// <summary>
 /// Batch a billboard node for rendering.
 /// </summary>
 /// <param name="node">BillboardNode.</param>
 protected void BatchBillboardNode(BillboardNode node)
 {
     // Batch billboard
     billboardManager.AddBatch(camera, node);
 }
Example #6
0
        /// <summary>
        /// Update node.
        ///  This method requires some clean-up and optimisation.
        ///  Currently rebuilding matrices on every update whether node
        ///  has changed or not. This is done for simplicity and will
        ///  be improved later.
        /// </summary>
        /// <param name="node">SceneNode.</param>
        /// <param name="matrix">Cumulative Matrix.</param>
        /// <returns>Transformed and merged BoundingSphere.</returns>
        /// <param name="elapsedTime">Elapsed time since last frame.</param>
        private BoundingSphere UpdateNode(SceneNode node, Matrix matrix, TimeSpan elapsedTime)
        {
            Matrix cumulativeMatrix = Matrix.Identity;

            // Create node transforms
            Matrix rotationX   = Matrix.CreateRotationX(node.Rotation.X);
            Matrix rotationY   = Matrix.CreateRotationY(node.Rotation.Y);
            Matrix rotationZ   = Matrix.CreateRotationZ(node.Rotation.Z);
            Matrix translation = Matrix.CreateTranslation(node.Position);

            // Create action transforms
            Matrix actionTranslation = Matrix.Identity;

            if (node.Action.Enabled)
            {
                // Progress actions
                if (node.Action.ActionState == SceneNode.ActionState.RunningForwards)
                {
                    // Progress action
                    node.Action.RunTime += elapsedTime.Milliseconds;
                    if (node.Action.RunTime >= node.Action.Duration)
                    {
                        node.Action.RunTime     = node.Action.Duration;
                        node.Action.ActionState = SceneNode.ActionState.End;
                    }
                }
                else if (node.Action.ActionState == SceneNode.ActionState.RunningBackwards)
                {
                    // Progress action
                    node.Action.RunTime -= elapsedTime.Milliseconds;
                    if (node.Action.RunTime <= 0)
                    {
                        node.Action.RunTime     = 0;
                        node.Action.ActionState = SceneNode.ActionState.Start;
                    }
                }

                float scale = (float)node.Action.RunTime / (float)node.Action.Duration;
                float xrot  = node.Action.Rotation.X * scale;
                float yrot  = node.Action.Rotation.Y * scale;
                float zrot  = node.Action.Rotation.Z * scale;
                float xtrn  = node.Action.Translation.X * scale;
                float ytrn  = node.Action.Translation.Y * scale;
                float ztrn  = node.Action.Translation.Z * scale;

                // Create action transforms
                Matrix actionRotationX = Matrix.CreateRotationX(xrot);
                Matrix actionRotationY = Matrix.CreateRotationY(yrot);
                Matrix actionRotationZ = Matrix.CreateRotationZ(zrot);
                actionTranslation = Matrix.CreateTranslation(xtrn, ytrn, ztrn);

                // Apply action transforms
                Matrix.Multiply(ref cumulativeMatrix, ref actionRotationY, out cumulativeMatrix);
                Matrix.Multiply(ref cumulativeMatrix, ref actionRotationX, out cumulativeMatrix);
                Matrix.Multiply(ref cumulativeMatrix, ref actionRotationZ, out cumulativeMatrix);
            }

            // Apply node transforms.
            // Rotation order is Y*X*Z which seems to be correct in all observed cases.
            Matrix.Multiply(ref cumulativeMatrix, ref rotationY, out cumulativeMatrix);
            Matrix.Multiply(ref cumulativeMatrix, ref rotationX, out cumulativeMatrix);
            Matrix.Multiply(ref cumulativeMatrix, ref rotationZ, out cumulativeMatrix);
            Matrix.Multiply(ref cumulativeMatrix, ref actionTranslation, out cumulativeMatrix);
            Matrix.Multiply(ref cumulativeMatrix, ref translation, out cumulativeMatrix);
            Matrix.Multiply(ref cumulativeMatrix, ref matrix, out cumulativeMatrix);

            // Transform bounds
            BoundingSphere bounds = node.LocalBounds;

            Vector3.Transform(ref bounds.Center, ref cumulativeMatrix, out bounds.Center);

            // Update child nodes
            foreach (SceneNode child in node.Children)
            {
                bounds = BoundingSphere.CreateMerged(
                    bounds,
                    UpdateNode(child, cumulativeMatrix, elapsedTime));
            }

            // Animate a light node
            if (node is PointLightNode)
            {
                PointLightNode pointLightNode = (PointLightNode)node;
                pointLightNode.AnimTimer += elapsedTime.Milliseconds;
                if (pointLightNode.AnimTimer > 60)
                {
                    pointLightNode.AnimTimer = 0;
                    pointLightNode.AnimScale = MathHelper.Clamp(
                        pointLightNode.AnimScale + (float)(rnd.NextDouble() - 0.5f) * 0.15f,
                        0.70f,
                        1.0f);
                }
            }

            // Calculate point lighting on a billboard node.
            // The light and billboard must have the same parent or
            // they will not be compared.
            // TODO: Find a better way to do this.
            if (node is BillboardNode)
            {
                BillboardNode billboardNode = (BillboardNode)node;
                billboardNode.LightIntensity = 0f;
                foreach (SceneNode child in node.Parent.Children)
                {
                    if (child is PointLightNode)
                    {
                        PointLightNode pointLightNode = (PointLightNode)child;
                        if (pointLightNode.TransformedBounds.Intersects(billboardNode.TransformedBounds))
                        {
                            float distance = Vector3.Distance(
                                pointLightNode.TransformedBounds.Center,
                                billboardNode.TransformedBounds.Center);

                            float attenuation = MathHelper.Clamp(
                                1.0f - distance / pointLightNode.Radius,
                                0,
                                1);

                            billboardNode.LightIntensity = MathHelper.Clamp(
                                billboardNode.LightIntensity + attenuation,
                                0,
                                1);
                        }
                    }
                }
            }

            // Store transformed bounds
            node.TransformedBounds = bounds;

            // Store cumulative matrix
            node.Matrix = cumulativeMatrix;

            return(bounds);
        }