Solar Wind 3D Sample

This sample for Silverilght 5 uses the new 3D features to draw the Earth with day and night transitions, atmosphere layers, and population density overlays. It demonstrates advanced concepts like mipmaps, texture blending, multiple drawing passes, sampler states...

C# (10.4 MB)
 
 
 
 
 
4.4 Star
(8)
9,133 times
Add to favorites
9/12/2011
E-mail Twitter del.icio.us Digg Facebook
// ===================================================================================
//  Copyright (c) Microsoft Corporation.  All rights reserved.
//  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY
//  OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
//  LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
//  FITNESS FOR A PARTICULAR PURPOSE.
// ===================================================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Media.Imaging;
using System.Windows.Resources;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Primitives3D;

namespace SolarWind
{
    /// <summary>
    /// Represents and draws the Earth
    /// </summary>
    public class Earth
    {
        /// <summary>
        /// The sphere mesh representing the Earth
        /// </summary>
        SpherePrimitive mesh;

        /// <summary>
        /// The atmosphere layer
        /// </summary>
        EarthAtmosphereLayer atmosphereLayer;

        /// <summary>
        /// The slightly more dense mesh representing the population overlay
        /// </summary>
        EarthPopulationLayer populationLayer;

        /// <summary>
        /// The magnetic field
        /// </summary>
        MagneticField field;

        // Shaders
        VertexShader earthVertexShader;
        PixelShader earthPixelShader;

        // Textures
        Texture2D dayTexture;
        Texture2D nightTexture;
        Texture2D nightLightsTexture;
        Texture2D normalTexture;
        Texture2D maskTexture;

        // c0: WorldMatrix = Transform
        // c4: WorldInverseTransposeMatrix = WorldInverseTransposeMatrix
        // c8: WorldViewProjectionMatrix = Transform * camera.ViewTransform * camera.ProjectionTransform
        // c12: ViewInverseMatrix = Matrix.Invert(camera.ViewTransform));
        // c16: TotalSeconds = (float)time.Seconds + (float)time.Milliseconds/1000.0f;        
        public struct EarthVertexShaderConstants
        {
            public Matrix WorldMatrix;
            public Matrix WorldInverseTransposeMatrix;
            public Matrix WorldViewProjectionMatrix;
            public Matrix ViewInverseMatrix;
            public Vector4 TotalSeconds;
        }

        /// <summary>
        /// The constants to pass into the shaders each frame
        /// </summary>
        EarthVertexShaderConstants earthConstants;

        /// <summary>
        /// World transform of the Earth model
        /// </summary>
        public Matrix Transform { get; set; }

        /// <summary>
        /// Toggles whether the magnetic field is visible
        /// </summary>
        public bool MagneticFieldVisible { get; set; }

        /// <summary>
        /// Toggles whether the atmosphere is visible
        /// </summary>
        public bool AtmosphereVisible { get; set; }

        /// <summary>
        /// Toggles whether the population density layer is visible
        /// </summary>
        public bool PopulationDensityVisible { get; set; }

        /// <summary>
        /// Toggles whether to render using wireframe or solid shading
        /// </summary>
        public bool ShowWireframe 
        {
            get
            {
                return showWireframe;
            }
            set
            {
                showWireframe = value;

                rasterizerState = new RasterizerState
                {
                    CullMode = CullMode.CullCounterClockwiseFace,
                    FillMode = showWireframe ? FillMode.WireFrame : FillMode.Solid,
                };
            }
        }

        /// <summary>
        /// Private field to keep wireframe state
        /// </summary>
        bool showWireframe;

        /// <summary>
        /// Private field to keep rasterizer state based on wireframe mode
        /// </summary>
        RasterizerState rasterizerState;

        public Earth()
        {
            Transform = Matrix.CreateWorld(new Vector3(), Vector3.Forward, Vector3.Up);

            AtmosphereVisible = true;
            MagneticFieldVisible = false;
            PopulationDensityVisible = false;
            ShowWireframe = false;
        }

        public void LoadContent()
        {
            LoadEarthContent();

            atmosphereLayer = new EarthAtmosphereLayer(mesh);
            atmosphereLayer.LoadContent();

            populationLayer = new EarthPopulationLayer();
            populationLayer.LoadContent();

            field = new MagneticField();
            field.LoadContent();
        }

        void LoadEarthContent()
        {
            // Load mesh
            mesh = new SpherePrimitive(10.0f, 50);

            //
            // Load shaders
            //
            earthVertexShader = ContentManager.LoadVertexShader("Shaders/Earth_vs.vs");
            earthPixelShader = ContentManager.LoadPixelShader("Shaders/Earth_ps.ps");

            //
            // Load textures
            //
            dayTexture = ContentManager.LoadTexture("Textures/Earth/EarthDay.png", true);
            nightTexture = ContentManager.LoadTexture("Textures/Earth/EarthNight.png", true);
            nightLightsTexture = ContentManager.LoadTexture("Textures/Earth/EarthNightLights.png", true);
            normalTexture = ContentManager.LoadTexture("Textures/Earth/EarthNormal.png", true);
            maskTexture = ContentManager.LoadTexture("Textures/Earth/EarthMask.png", true);
        }

        public void Draw(GraphicsDevice device, SceneTime time, Camera camera)
        {
            // All Earth pass shaders use the same constants so update them each frame
            UpdateEarthShaderConstants(device, time, camera);

            //
            // Pass 1 - Draw Earth
            //

            // Set textures in order of samplers
            device.Textures[0] = dayTexture;
            device.Textures[1] = nightTexture;
            device.Textures[2] = nightLightsTexture;
            device.Textures[3] = normalTexture;
            device.Textures[4] = maskTexture;

            // Set states to control sampling
            device.SamplerStates[0] = SamplerState.AnisotropicClamp;
            device.SamplerStates[1] = SamplerState.AnisotropicClamp;
            device.SamplerStates[2] = SamplerState.AnisotropicClamp;
            device.SamplerStates[3] = SamplerState.AnisotropicClamp;
            device.SamplerStates[4] = SamplerState.AnisotropicClamp;            

            // Set device states
            device.BlendState = BlendState.Opaque;
            device.RasterizerState = rasterizerState;

            // Configure shading pipeline
            device.SetVertexShader(earthVertexShader);
            device.SetPixelShader(earthPixelShader);

            // Draw the main earth pass
            mesh.Draw(device);

            // Draw atmosphere layer
            if (AtmosphereVisible)
                atmosphereLayer.Draw(device);

            // Draw population layer
            if (PopulationDensityVisible)
                populationLayer.Draw(device);

            // Draw magnetic field
            if (MagneticFieldVisible)
                field.Draw(device, time, camera);
        }

        // Define these static values to avoid per frame allocations on the heap
        static Vector4 showWireframeValue = new Vector4(1.0f, 0f, 0f, 0f);
        static Vector4 hideWireframeValue = new Vector4();

        /// <summary>
        /// Updates the shader constants each frame for the earth shaders
        /// </summary>
        /// <param name="device">Graphics device</param>
        /// <param name="time">Scene time</param>
        /// <param name="camera">Camera</param>
        void UpdateEarthShaderConstants(GraphicsDevice device, SceneTime time, Camera camera)
        {            
            // Get the inverse transpose matrix
            Matrix WorldInverseTransposeMatrix = Matrix.Invert(Transform);
            WorldInverseTransposeMatrix = Matrix.Transpose(WorldInverseTransposeMatrix);

            // Set the primary vertex constants
            earthConstants.WorldMatrix = Transform;
            earthConstants.WorldInverseTransposeMatrix = Matrix.Transpose(Matrix.Invert(Transform));
            earthConstants.WorldViewProjectionMatrix = Transform * camera.ViewTransform * camera.ProjectionTransform;
            earthConstants.ViewInverseMatrix = Matrix.Invert(camera.ViewTransform);
            earthConstants.TotalSeconds.X = (float)time.TotalTime.TotalSeconds;
            device.SetVertexShaderConstantFloat4(0, ref earthConstants);

            // Set the pixel shader constants (to achieve solid lighting in wireframe mode)
            if(ShowWireframe)
                device.SetPixelShaderConstantFloat4(0, ref showWireframeValue);
            else
                device.SetPixelShaderConstantFloat4(0, ref hideWireframeValue);
        }
    }
}