Creating a Simple 3D Game with XNA/Adding a Sky Sphere

Creating your SkySphere Renderer edit

In order to render the skysphere, we will create a new rendering class similar to the one used to render the fish, but modified to allow the model to render without an armature structure in place. First, create a new class file by right clicking on your project, selecting 'Add'/'New Item' and selecting 'Class'. Change the class and file name to 'GenericModelRenderer'.

Change the default using statements to the following.

using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using SkinnedModel;

And ensure that the class is using the XNA namespace by affixing the class name with

: Microsoft.Xna.Framework.Game

.

Add the following class variables and constructor. public Model currentModel; public Vector3 Translation = new Vector3(0, 0, 0); public Vector3 Rotation = new Vector3(0, 0, 0); public float Scale = 1.0f;

public GenericModelRenderer(Model currentModelInput)
{
    currentModel = currentModelInput;
}

Unlike the previous code, much less initialisation is required as we do not need to initialise the animation player. You'll see a similar trend with this classes equivalent to the 'ModelDraw' method.

public void ModelDraw(GraphicsDevice device, Vector3 cameraPosition, Vector3 cameraTarget, float farPlaneDistance)
{
    Matrix[] transforms = new Matrix[currentModel.Bones.Count];
    currentModel.CopyAbsoluteBoneTransformsTo(transforms);

    // Compute camera matrices.
    Matrix view = Matrix.CreateLookAt(cameraPosition, cameraTarget, Vector3.Right);

    //Calculate the aspect ratio, set the aspect axis and screen zoom.
    int aspectConstraint = 1;  /* 0 = Maintain Vertical FOV, 1 = Conditional Aspect Ratio, 2 = Maintain Horizontal FOV */
    float aspectRatio = (float)device.Viewport.Width / (float)device.Viewport.Height;
    float aspectOrigin = 16.0f / 9.0f; /* Aspect ratio condition in which changes axis direction if the current display is below this. Default is 1. */
    float zoomFactor = 1.0f;
    
    switch (aspectConstraint)
    {
       case 1:
         if (aspectRatio < aspectOrigin)
           {
              zoomFactor = zoomFactor * (aspectRatio / aspectOrigin);
           }
         break;
    
       case 2:
         zoomFactor = zoomFactor * (aspectRatio / aspectOrigin);
         break;
    }

    Matrix projection = Matrix.CreatePerspectiveFieldOfView(2.0f * Math.Atan(Math.Tan(MathHelper.ToRadians(45.0f / 2.0f) / zoomFactor), aspectRatio, 1.0f, farPlaneDistance);

    // Draw the model. A model can have multiple meshes, so loop.
    foreach (ModelMesh mesh in currentModel.Meshes)
    {
        // This is where the mesh orientation is set, as well as our camera and projection.
        foreach (BasicEffect effect in mesh.Effects)
        {
            effect.World = transforms[mesh.ParentBone.Index] *
                Matrix.CreateRotationX(Rotation.X) *
                Matrix.CreateRotationY(Rotation.Y) *
                Matrix.CreateRotationZ(Rotation.Z) *
                Matrix.CreateScale(Scale) *
                Matrix.CreateWorld(Translation, Vector3.Forward, Vector3.Up);

            effect.View = view;
            effect.Projection = projection;
        }
        mesh.Draw();
    }
}

Notice that the draw method uses the same arguments as the other fish code, can be used in more or less the same way.

Using Your Renderer edit

Place a new folder in your models folder called 'SkySphere', and add your model and texture. Go back into your main 'Game1' file, and add the new class variable

GenericModelRenderer SkySphere; //Your SkySphere model

.

Go into your LoadContent file, and add the following lines to initialise the class and set up the model for rendering.

currentModel = Content.Load<Model>("Models\\SkySphere\\SkyModel");
SkySphere = new GenericModelRenderer(currentModel);//Add the skysphere

And in the draw method, add the following line about the same place as your fish models draw method.

SkySphere.ModelDraw(GraphicsDevice, cameraPosition, cameraTarget, farPlaneDistance);

Render the code, and if you're set up is the same as mine, you should see a screen like this.

 

You might notice this isn't quite what we need. We can adjust the position and the scale by adjusting the public variables in the SkySphere class. In my case, I added the following changes after the classes initialisation in the LoadContent() method.

SkySphere.Translation.X = 120.0f;
SkySphere.Translation.Z = 15.0f;
SkySphere.Scale = 30.0f;

Adjust to taste, and you should see a screen similar to mine here.