Home > Tutorials > Displaying 3D Objects
1.3. Colors
In this tutorial you will create a cube and see the result of defining the color on a vertex basis.



Click here to go to the forum discussion of this tutorial


 

1.3.1. Prerequisites
default.jpg
Before you start this tutorial make sure that you have:
  • Installed XNA Game Studio 3.1
  • Downloaded the starter project from the downloads section
  • Successfully run the starter project
   
 

1.3.2. The using statements
In this tutorial we the only using statements, other than the ones included in the starter project are:

using BetaCell.Environment.Mesh;
using BetaCell.Util.Procedural;
using BetaCell.Common.Vertex;
using BetaCell.Behavior.Procedural;


 

1.3.3. Game Attributes 
The attributes that come with the starter project are explained in tutorial section 1.1.4.

In this tutorial we are going to be needing a cube:

BCMesh cube; 

 

Advertisement
 

1.3.4. The Initialize method 
The initialization work done in the starter project can be seen in [1.1.6].
 
default.jpg
A vertex in a 3D scene must hold information about its position; for example, in the figure the verices (in blue) are positioned so that the faces that use them form a cube. In general, a vertex can hold information other than the position; for example if you want to apply a texture to the cube, the the vertex must hold information about the texture coordinate that correspond to it in the 2D texture. In the boxes figure, each box has different per vertex data, a vertex of the cube in the upper left part has only position data, a vertex of the one in th lower left part has position and normal data, the normal is needed to perform the lightning calculations. A vertex of the cube in the upper right part has position, normal and texture coordinate data, and finally a vertex in the cube of the lower right part has position, texture, tangent, binormal and normal data (normal mapping is explained later in the tutorials)

default.jpg
Now that you know how a vertex work, you should know how they are finally drawn. We could create a three vertices and pass them to the GPU to draw a face, and repeat the process for all the faces in a mesh, but that would be extremely slow, because the time needed to establish a communication channel between the CPU and the GPU is way higher than the time that the GPU spends to draw a single face.

The idea is to create an array with all the vertices that are going to be used in the drawing of an object, and pass the whole array to the graphics card, then pass another array defining the faces of the object.


In BetaCell you create a container that has two main responsibilities; hold the vertex data and create the previously mentioned array that is called a VertexBuffer in XNA.

In the tutorial, two options are shown. The first option is:

cube = ProceduralModelers.CUBE_MODELER.createCube(10, 10, 10,
                                   new BCVertexPosColorContainer());

BCMeshGraphicUtil.init(cube, device);

The createCube always creates a cube with the center at the origin (0,0,0) and receives the width, height and depth respectively as the first three parameters.

The interesting parameter is the fourth one; its a vertex container that holds the position and the color information. BetaCell comes with some general vertex containers to let programmers with little background in computer graphics have a little experience before creating vertex containers by composition; more experienced programmers will prefer the following vertex container declaration.

First an array that will hold the usages of the container is created:

BCVertexUsage[] usages = new BCVertexUsage[2];

The BetaCell.Common.Vertex.BCVertexUsage class represents a usage that a vertex can have, some usages are position, normal, texture, color, etc.

Then the position and color usages are set to the array:

usages[0] = BCVertexUsage.POSITION;
usages[1] = BCVertexUsage.COLOR;

Notice how the different usages are static constants of the BCVertexUsage class.

Now a BetaCell.Common.Vertex.BCGeneralVertexContainer is created with the usages array as a parameter.

BCGeneralVertexContainer generalPosColor =
    new BCGeneralVertexContainer(usages);

Finally the cube is created with the same call, except that the last parameter isn't a BCVertexPosColorContainer but the more flexible BCGeneralVertexContainer.

cube = ProceduralModelers.CUBE_MODELER.createCube(10, 10, 10, generalPosColor);
BCMeshGraphicUtil.init(cube, device);

The rest is just call the init method [1.1.6].

 

1.3.5. The Draw method
Nothing new on the Draw method, just draws the cube:

cube.Draw(gameTime);

 

1.3.6. Conclusion
It's very important to understand the concept of the vertex container because it's going to be used in other tutorials.

Click here to go to the forum discussion of this tutorial

 

Advertisement
 

1.3.7. Complete source code
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

using BetaCell.Debug;
using BetaCell.Util.GlobalInfo.Content;
using BetaCell.Environment.Camera;
using BetaCell.Util.GlobalInfo;

using BetaCell.Environment.Mesh;
using BetaCell.Util.Procedural;
using BetaCell.Common.Vertex;
using BetaCell.Behavior.Procedural;

namespace Starter
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        //Game attributes
        GraphicsDeviceManager graphics;
        GraphicsDevice device;
        ContentManager content;

        BCFirstPersonHumanCamera camera;
        //end game attributes

        //This tutorial's attributes
        BCMesh cube;

        BCProceduralRotator rotator;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            graphics.PreferredDepthStencilFormat = DepthFormat.Depth24Stencil8;
            content = new ContentManager(Services);
#if(XBOX360)
            graphics.PreferredBackBufferWidth = 1280;
            graphics.PreferredBackBufferHeight = 720;
#else
            graphics.PreferredBackBufferWidth = 852;
            graphics.PreferredBackBufferHeight = 480;
#endif
        }

        protected override void Initialize()
        {
            //base initialization
            device = graphics.GraphicsDevice;

            BCLogger.instance.init(device, content);
            BCInitializationManager.initialize(content);

            OnActivated(null, null);

            recreateScene();

            base.Initialize();
        }

        private void recreateScene()
        {
            //-------------
            //cube
            //-------------

            //Easy way to create the vertex container
            cube = ProceduralModelers.CUBE_MODELER.createCube(10, 10, 10, new BCVertexPosColorContainer());
            BCMeshGraphicUtil.init(cube, device);

            //Flexible way to create the container

            //Define an array with 2 usages
            BCVertexUsage[] usages = new BCVertexUsage[2];

            //Set the position and color usages
            usages[0] = BCVertexUsage.POSITION;
            usages[1] = BCVertexUsage.COLOR;

            //Create the container with the usages
            BCGeneralVertexContainer generalPosColor =
                new BCGeneralVertexContainer(usages);

            //Create the cube with the general vertex container
            cube = ProceduralModelers.CUBE_MODELER.createCube(10, 10, 10, generalPosColor);
            BCMeshGraphicUtil.init(cube, device);

        }

        protected override void OnActivated(object sender, EventArgs args)
        {
            buildViewMatrix();
            base.OnActivated(sender, args);
        }

        void buildViewMatrix()
        {
            Vector3 pos = new Vector3(30, 10, 30);
            Vector3 look = new Vector3(-3, -1, -3);
            look.Normalize();

            camera = new BCFirstPersonHumanCamera(look, pos,
                MathHelper.PiOver4,
                (float)this.Window.ClientBounds.Width / (float)this.Window.ClientBounds.Height,
                1f, 200);

            BCGlobalInfo.instance.setMatrix(
                BCGlobalInfo.VIEW_INDEX,
                camera.getViewMatrix(-1)
            );

            BCGlobalInfo.instance.setMatrix(
                BCGlobalInfo.PROJECTION_INDEX,
                camera.getProjectionMatrix(-1)
            );
        }

        protected override void Update(GameTime gameTime)
        {
        }

        protected override void Draw(GameTime gameTime)
        {
            graphics.GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.White, 1.0f, 0);

            cube.Draw(gameTime);

            BCLogger.instance.printFPS(gameTime);
            BCLogger.instance.flush();
            base.Draw(gameTime);
        }
    }
}