четверг, 15 марта 2012 г.

How To Make A Game

In this blog I will talk about how to create a computer game step by step using the Microsoft XNA. I start my story from the simple to the complex. And at the end of the story we will create a full 3D game.






As a result, we get this game:



 So let's get started and good luck to everyone who wants to learn how to create games with their own hands.

Menu Creation

Our future game would consist of three main modules. This menu, a physical module and render. First, we look at how to create a menu for our, as well as for any other game. To run the menu, we need to create the following classes:

AnimeFont.cs;
BackGround.cs;
Button.cs;
Cursor.cs;
Delegates.cs;
MainMenu.cs;

From the title it should be clear that each class will do.

namespace _MENU_
{
  class AnimeFont : DrawableGameComponent
  {
    SpriteBatch spriteBatch;
    private Texture2D Texture1;
    int PositionX1;
    int PositionY1;
    Rectangle Rectangle1;
    private Texture2D Texture2;
    int PositionX2;
    int PositionY2;
    Rectangle Rectangle2;
    private Texture2D Texture3;
    int PositionX3;
    int PositionY3;
    Rectangle Rectangle3;
    private Texture2D Texture4;
    int PositionX4;
    int PositionY4;
    Rectangle Rectangle4;
    private Texture2D gameName;
    public AnimeFont(Game game) : base(game)
    {
      spriteBatch = new SpriteBatch(Game.GraphicsDevice);
      Texture1 = Game.Content.Load<Texture2D>(@"Menu/RB1");
      PositionX1 = game.GraphicsDevice.Viewport.Width - Texture1.Width;
      PositionY1 = game.GraphicsDevice.Viewport.Height - Texture1.Height / 2;
      Texture2 = Game.Content.Load<Texture2D>(@"Menu/RB2");
      PositionX2 = -game.GraphicsDevice.Viewport.Width / 2;
      PositionY2 = game.GraphicsDevice.Viewport.Height - Texture2.Height * 2;
      Texture3 = Game.Content.Load<Texture2D>(@"Menu/RB3");
      PositionX3 = game.GraphicsDevice.Viewport.Width - Texture3.Width / 2;
      PositionY3 = 100;
      Texture4 = Game.Content.Load<Texture2D>(@"Menu/RB4");
      PositionX4 = 50;
      PositionY4 = -Game.GraphicsDevice.Viewport.Height;
      gameName = Game.Content.Load<Texture2D>(@"Menu/GameName");
    }
    public override void Initialize()
    {
      base.Initialize();
    }
    public override void Update(GameTime gameTime)
    {
      PositionY1--;
      Rectangle1 = new Rectangle(PositionX1, PositionY1, Texture1.Width, Texture1.Height);
      if (PositionY1 < -800)
      {
        PositionY1 = Game.GraphicsDevice.Viewport.Height - Texture1.Height / 6;
      }
      PositionX2++;
      Rectangle2 = new Rectangle(PositionX2, PositionY2, Texture2.Width, Texture2.Height);
      if (PositionX2 > Game.GraphicsDevice.Viewport.Width)
      {
        PositionX2 = -Game.GraphicsDevice.Viewport.Width / 2;
      }
      PositionX3--;
      Rectangle3 = new Rectangle(PositionX3, PositionY3, Texture3.Width, Texture3.Height);
      if (PositionX3 < -Game.GraphicsDevice.Viewport.Width / 2)
      {
        PositionX3 = Game.GraphicsDevice.Viewport.Width;
      }
      PositionY4++;
      Rectangle4 = new Rectangle(PositionX4, PositionY4, Texture4.Width, Texture4.Height);
      if (PositionY4 > Game.GraphicsDevice.Viewport.Height)
      {
        PositionY4 = -Game.GraphicsDevice.Viewport.Height;
      }
      base.Update(gameTime);
    }
    public override void Draw(GameTime gameTime)
    {
      spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Immediate, SaveStateMode.SaveState);
      spriteBatch.Draw(Texture1, Rectangle1, new Color(255, 255, 255, 70));
      spriteBatch.Draw(Texture2, Rectangle2, new Color(255, 255, 255, 70));
      spriteBatch.Draw(Texture3, Rectangle3, new Color(255, 255, 255, 255));
      spriteBatch.Draw(Texture4, Rectangle4, new Color(255, 255, 255, 100));
      spriteBatch.Draw(gameName, new Rectangle(60, 70, gameName.Width / 2, gameName.Height / 2), new Color(255, 255, 255, 255));
      spriteBatch.End();
      base.Draw(gameTime);
    }
  }
}

namespace _MENU_
{
  public class BackGround : Microsoft.Xna.Framework.DrawableGameComponent
  {
    private SpriteBatch spriteBatch;
    private VideoPlayer videoPlayer;
    private Texture2D BackGroundTexture = null;
    private Video BackGroundVideo = null;
    private Rectangle BackGroundRectangle;
    private string BackGroundFilePath;
    public BackGround(Game game, Rectangle BackGroundRectangle, string BackGroundFilePath) : base(game)
    {
      this.BackGroundRectangle = BackGroundRectangle;
      this.BackGroundFilePath = BackGroundFilePath;
    }
    public override void Initialize()
    {
      spriteBatch = new SpriteBatch(Game.GraphicsDevice);
      try
      {
        BackGroundTexture = Game.Content.Load<Texture2D>(BackGroundFilePath);
      }
      catch
      {
        BackGroundVideo = Game.Content.Load<Video>(BackGroundFilePath);
        videoPlayer = new VideoPlayer();
        videoPlayer.IsLooped = true;
        videoPlayer.Play(BackGroundVideo);
      }
      base.Initialize();
    }
   
    public override void Draw(GameTime gameTime)
    {
      spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Immediate, SaveStateMode.SaveState);
      if (BackGroundTexture != null)
      {
        spriteBatch.Draw(BackGroundTexture, BackGroundRectangle, Color.White);
      }
      if (BackGroundVideo != null)
      {
        spriteBatch.Draw(videoPlayer.GetTexture(), BackGroundRectangle, Color.White);
      }
      spriteBatch.End();
      base.Draw(gameTime);
    }
  }
}

namespace _MENU_
{
  public class MenuButton : Microsoft.Xna.Framework.DrawableGameComponent
  {
    private SpriteBatch spriteBatch;
    private SpriteFont spriteFont;
   
    private Texture2D ButtonTexture;
    private Rectangle ButtonRectangle;
    private Color ButtonColor = Color.White;
    private string TexturePath;
    private string SpriteFontPath = string.Empty;
    private string Name = string.Empty;
    public event OnMenuButtonClick OnButtonClick;
    public MenuButton(Game game, Rectangle ButtonRectangle, string TexturePath)
      : base(game)
    {     
      this.ButtonRectangle = ButtonRectangle;
      this.TexturePath = TexturePath;
    }
    public MenuButton(Game game, Rectangle ButtonRectangle, string TexturePath, string SpriteFontPath, string Name)
      : base(game)
    {
      this.ButtonRectangle = ButtonRectangle;
      this.TexturePath = TexturePath;
      this.SpriteFontPath = SpriteFontPath;
      this.Name = Name;
    }
    protected override void LoadContent()
    {
      spriteBatch = new SpriteBatch(GraphicsDevice);
      ButtonTexture = Game.Content.Load<Texture2D>(TexturePath);
      if (SpriteFontPath != string.Empty)
      {
        spriteFont = Game.Content.Load<SpriteFont>(SpriteFontPath);
      }
      base.LoadContent();
    }
    public override void Update(GameTime gameTime)
    {
      Rectangle MouseRectangle = new Rectangle(Mouse.GetState().X, Mouse.GetState().Y, 1, 1);
      if (MouseRectangle.Intersects(ButtonRectangle))
      {
        ButtonColor = Color.White;
        if (Mouse.GetState().LeftButton == ButtonState.Pressed)
        {
          OnButtonClick();
        }
      }
      else
      {
        ButtonColor = Color.Gray;
      }
      base.Update(gameTime);
    }
    public override void Draw(GameTime gameTime)
    {
      spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Immediate, SaveStateMode.SaveState);
      spriteBatch.Draw(ButtonTexture, ButtonRectangle, ButtonColor);
      if (Name != string.Empty)
      {
        spriteBatch.DrawString(spriteFont, Name, new Vector2(ButtonRectangle.X, ButtonRectangle.Y + 8), Color.AliceBlue);
      }
      spriteBatch.End();
      base.Draw(gameTime);
    }
  }
}


namespace _MENU_
{
  public class Cursor : Microsoft.Xna.Framework.DrawableGameComponent
  {
    SpriteBatch spriteBatch;
    private Texture2D CursorTexture;
    private Rectangle CursorRectangle;
    private string CursorFilePath;
    public Cursor(Game game, Rectangle CursorRectangle, string CursorFilePath) : base(game)
    {
      this.CursorRectangle = CursorRectangle;
      this.CursorFilePath = CursorFilePath;
    }
    public override void Initialize()
    {
      spriteBatch = new SpriteBatch(Game.GraphicsDevice);
      CursorTexture = Game.Content.Load<Texture2D>(CursorFilePath);
      base.Initialize();
    }
    public override void Update(GameTime gameTime)
    {
      CursorRectangle = new Rectangle(Mouse.GetState().X, Mouse.GetState().Y, 50, 50);
      base.Update(gameTime);
    }
    public override void Draw(GameTime gameTime)
    {
      spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Immediate, SaveStateMode.SaveState);
      spriteBatch.Draw(CursorTexture, CursorRectangle, Color.White);     
      spriteBatch.End();
      base.Draw(gameTime);
    }
  }
}


namespace _MENU_
{
  public delegate void OnMenuButtonClick();
  public delegate void SwitchMainMenuToNewGame();
  public delegate void SwitchMainMenuToOptionsMenu();
  public delegate void SwitchMainMenuToTitleMenu();
  public delegate void SwitchTitleMenuToMainMenu();
  public delegate void SwitchMainMenuToExit();
}
namespace _MENU_
{
  public class MainMenu
  {
    public event SwitchMainMenuToNewGame SwitchMainMenuToGame;
    public event SwitchMainMenuToTitleMenu SwitchMainMenuToTitleMenu;
    public event SwitchMainMenuToExit SwitchMainMenuToExit;
    BackGround FirstBackGround;
    AnimeFont AFont;
    MenuButton StartNewGameButton;
    MenuButton OptionsGameButton;
    MenuButton TitleButton;
    MenuButton ExitGameButton;
    public MainMenu(Game game)
    {
      FirstBackGround = new BackGround(game, new Rectangle(0, 0, game.GraphicsDevice.Viewport.Width, game.GraphicsDevice.Viewport.Height), @"Menu/menu");
      game.Components.Add(FirstBackGround);
      AFont = new AnimeFont(game);
      game.Components.Add(AFont);
      StartNewGameButton = new MenuButton(game, new Rectangle(80, 150, 200, 40), @"Menu/Button3", @"Menu/DefaultFont", "       START");
      StartNewGameButton.OnButtonClick += new OnMenuButtonClick(StartNewGameButton_OnButtonClick);
      game.Components.Add(StartNewGameButton);
      OptionsGameButton = new MenuButton(game, new Rectangle(80, 200, 200, 40), @"Menu/Button3", @"Menu/DefaultFont", "      OPTIONS");
      OptionsGameButton.OnButtonClick += new OnMenuButtonClick(OptionsGameButton_OnButtonClick);
      game.Components.Add(OptionsGameButton);
      TitleButton = new MenuButton(game, new Rectangle(80, 250, 200, 40), @"Menu/Button3", @"Menu/DefaultFont", "       TITLE");
      TitleButton.OnButtonClick += new OnMenuButtonClick(TitleButton_OnButtonClick);
      game.Components.Add(TitleButton);
      ExitGameButton = new MenuButton(game, new Rectangle(80, 300, 200, 40), @"Menu/Button3", @"Menu/DefaultFont", "        EXIT");
      ExitGameButton.OnButtonClick += new OnMenuButtonClick(ExitButton_OnButtonClick);
      game.Components.Add(ExitGameButton);
      game.Components.Add(new Cursor(game, new Rectangle(),@"Menu/cursor"));
    }
    void TitleButton_OnButtonClick()
    {
      SwitchMainMenuToTitleMenu();
    }
    void OptionsGameButton_OnButtonClick()
    {
    }
    void StartNewGameButton_OnButtonClick()
    {
      SwitchMainMenuToGame();
    }
    void ExitButton_OnButtonClick()
    {
      SwitchMainMenuToExit();
    }

  }
}

The result of these classes you can see in the pictures above. Using delegates used in menu, we can switch between various modes of our future game engine. Need to immediately say that this game is based on the physical engine PhysX, and actually to the NET version of these libraries:

PhysXLoader.dll
NxCooking.dll
NxCharacter.dll

These libraries are available online for download.

This class fully configures the physics of the game, and synchronizes with the game render.
namespace _GAME_
{
  public delegate void ContactCallback(Actor a, Actor b, ContactPairFlag events);
  public class PhysX
  {
    private Model _box;
    private Model _sphere;
    public Core _core;
    public static Scene _scene;
    public static Actor Bitok;
    public static Actor ball1, ball2, ball3, ball4,
                        ball5, ball6, ball7, ball8,
                        ball9, ball10, ball11, ball12,
                        ball13, ball14, ball15, ball16;
    public static Actor Border1, Border2, Border3, Border4, Border5, Border6;  
    public PhysX(Game game)
    {
      _core = new Core();
      _core.SetParameter(PhysicsParameter.VisualizationScale, 2.0f);
      _core.SetParameter(PhysicsParameter.VisualizationScale, 1.0f);
      _core.SetParameter(PhysicsParameter.VisualizeCollisionShapes, true);
      _core.SetParameter(PhysicsParameter.VisualizeClothMesh, true);
      _core.SetParameter(PhysicsParameter.VisualizeJointLocalAxes, true);
      _core.SetParameter(PhysicsParameter.VisualizeJointLimits, true);
      _core.SetParameter(PhysicsParameter.VisualizeFluidPosition, true);
      _core.SetParameter(PhysicsParameter.VisualizeFluidEmitters, false); // Slows down rendering a bit to much
      _core.SetParameter(PhysicsParameter.VisualizeForceFields, true);
      _core.SetParameter(PhysicsParameter.VisualizeSoftBodyMesh, true);
      _core.SetParameter(PhysicsParameter.DefaultSleepLinearVelocitySquared, 2.0f * 2.0f);
      _core.SetParameter(PhysicsParameter.DefaultSleepAngularVelocitySquared, 2.0f * 2.0f);
      _core.SetParameter(PhysicsParameter.SkinWidth, 0.01f);
      _core.SetParameter(PhysicsParameter.VisualizeActorAxes, true);
      SceneDescription sceneDesc = new SceneDescription();
      sceneDesc.SimulationType = SimulationType.Software;
      sceneDesc.Gravity = new Vector3(0.0f, -50f, 0.0f);
      _scene = _core.CreateScene(sceneDesc);
      // Create the default material
      Material defaultMaterial = _scene.Materials[0];
      defaultMaterial.Restitution = 0.5f;
      defaultMaterial.StaticFriction = 0.1f;
      defaultMaterial.DynamicFriction = 0.1f;
    }
   
    public void Dispose()
    {
      _core.Dispose();
      _scene.Dispose();
    }
    public void LoadPhysX(ContentManager Content)
    {
      _box = Content.Load<Model>(@"PhysicsMeshes/cube");
      _sphere = Content.Load<Model>(@"PhysicsMeshes/sphere");
      SetupPhysicsScene();
    }
    void SetupPhysicsScene()
    {
      // --- Borders ---
      Border1 = CreateBox(new Vector3(0, -15, 44), new Vector3(20.5f, 10, 2), 0);
      Border1.Name = "b";
      Border2 = CreateBox(new Vector3(0, -15, -44), new Vector3(20.5f, 10, 2), 0);
      Border2.Name = "b";
      Border3 = CreateBox(new Vector3(24, -15, 20.75f), new Vector3(2, 10, 19.75f), 0);
      Border3.Name = "b";
      Border4 = CreateBox(new Vector3(24, -15, -20.75f), new Vector3(2, 10, 19.75f), 0);
      Border4.Name = "b";
      Border5 = CreateBox(new Vector3(-24, -15, 20.75f), new Vector3(2, 10, 19.75f), 0);
      Border5.Name = "b";
      Border6 = CreateBox(new Vector3(-24, -15, -20.75f), new Vector3(2, 10, 19.75f), 0);
      Border6.Name = "b";
      // --- Planes ---
      Actor Box = CreateBox(new Vector3(0, -18, 0), new Vector3(22, 10.35f, 42), 0);
      Box = CreateBox(new Vector3(0, -50, 0), new Vector3(2500, 10f, 2500), 0);
      // --- zaplatki ---
      Box = CreateBox(new Vector3(25, -15, 45), new Vector3(6, 10f, 2), 0);
      Box.GlobalOrientation = Matrix.Transform(Box.GlobalOrientation, Quaternion.CreateFromRotationMatrix(Matrix.CreateRotationY(MathHelper.PiOver4)));
      Box = CreateBox(new Vector3(-25, -15, 45), new Vector3(2, 10f, 6), 0);
      Box.GlobalOrientation = Matrix.Transform(Box.GlobalOrientation, Quaternion.CreateFromRotationMatrix(Matrix.CreateRotationY(MathHelper.PiOver4)));
      Box = CreateBox(new Vector3(25, -15, -45), new Vector3(2, 10f, 6), 0);
      Box.GlobalOrientation = Matrix.Transform(Box.GlobalOrientation, Quaternion.CreateFromRotationMatrix(Matrix.CreateRotationY(MathHelper.PiOver4)));
      Box = CreateBox(new Vector3(-25, -15, -45), new Vector3(6, 10f, 2), 0);
      Box.GlobalOrientation = Matrix.Transform(Box.GlobalOrientation, Quaternion.CreateFromRotationMatrix(Matrix.CreateRotationY(MathHelper.PiOver4)));
      Box = CreateBox(new Vector3(27, -15, 0), new Vector3(2, 10f, 10), 0);
      Box = CreateBox(new Vector3(-27, -15, 0), new Vector3(2, 10f, 10), 0);
      // --- Balls ---
      float AngDump = 100f;
      float Mass = 300f;
      Bitok = CreateSphere(new Vector3(0, 2.6f, 20), 1, 1);
      Bitok.AngularDamping = AngDump;
      Bitok.Mass = Mass;
      Bitok.Name = "Bitok";
      Bitok.Sleep();
      ball1 = CreateSphere(new Vector3(-2, 2.6f, -20), 1, 1);
      ball1.AngularDamping = AngDump;
      ball1.Mass = Mass;
      ball1.Name = "ball1";
      ball1.Sleep();
      ball2 = CreateSphere(new Vector3(0, 2.6f, -20), 1, 1);
      ball2.AngularDamping = AngDump;
      ball2.Mass = Mass;
      ball2.Name = "ball2";
      ball2.Sleep();
      ball3 = CreateSphere(new Vector3(2, 2.6f, -20), 1, 1);
      ball3.AngularDamping = AngDump;
      ball3.Mass = Mass;
      ball3.Name = "ball3";
      ball3.Sleep();
      ball4 = CreateSphere(new Vector3(-3, 2.6f, -21.8f), 1, 1);
      ball4.AngularDamping = AngDump;
      ball4.Mass = Mass;
      ball4.Name = "ball4";
      ball4.Sleep();
      ball5 = CreateSphere(new Vector3(-1, 2.6f, -21.8f), 1, 1);
      ball5.AngularDamping = AngDump;
      ball5.Mass = Mass;
      ball5.Name = "ball5";
      ball5.Sleep();
      ball6 = CreateSphere(new Vector3(1, 2.6f, -21.8f), 1, 1);
      ball6.AngularDamping = AngDump;
      ball6.Mass = Mass;
      ball6.Name = "ball6";
      ball6.Sleep();
      ball7 = CreateSphere(new Vector3(3, 2.6f, -21.8f), 1, 1);
      ball7.AngularDamping = AngDump;
      ball7.Mass = Mass;
      ball7.Name = "ball7";
      ball7.Sleep();
      ball8 = CreateSphere(new Vector3(-4, 2.6f, -23.6f), 1, 1);
      ball8.AngularDamping = AngDump;
      ball8.Mass = Mass;
      ball8.Name = "ball8";
      ball8.Sleep();
      ball9 = CreateSphere(new Vector3(-2, 2.6f, -23.6f), 1, 1);
      ball9.AngularDamping = AngDump;
      ball9.Mass = Mass;
      ball9.Name = "ball9";
      ball9.Sleep();
      ball10 = CreateSphere(new Vector3(0, 2.6f, -23.6f), 1, 1);
      ball10.AngularDamping = AngDump;
      ball10.Mass = Mass;
      ball10.Name = "ball10";
      ball10.Sleep();
      ball11 = CreateSphere(new Vector3(2, 2.6f, -23.6f), 1, 1);
      ball11.AngularDamping = AngDump;
      ball11.Mass = Mass;
      ball11.Name = "ball11";
      ball11.Sleep();
      ball12 = CreateSphere(new Vector3(4, 2.6f, -23.6f), 1, 1);
      ball12.AngularDamping = AngDump;
      ball12.Mass = Mass;
      ball12.Name = "ball12";
      ball12.Sleep();
      ball13 = CreateSphere(new Vector3(-1, 2.6f, -18.2f), 1, 1);
      ball13.AngularDamping = AngDump;
      ball13.Mass = Mass;
      ball13.Name = "ball13";
      ball13.Sleep();
      ball14 = CreateSphere(new Vector3(1, 2.6f, -18.2f), 1, 1);
      ball14.AngularDamping = AngDump;
      ball14.Mass = Mass;
      ball14.Name = "ball14";
      ball14.Sleep();
      ball15 = CreateSphere(new Vector3(0, 2.6f, -16.4f), 1, 1);
      ball15.AngularDamping = AngDump;
      ball15.Mass = Mass;
      ball15.Name = "ball15";
      ball15.Sleep();

      _scene.UserContactReport = new ContactReport();
      _scene.SetActorGroupPairFlags(0, 0, ContactPairFlag.OnTouch);
    }
    public void UpdatePhysX(GameTime gameTime)
    {
      _scene.Simulate((float)gameTime.ElapsedGameTime.TotalMilliseconds / 300f);     
      _scene.FetchResults(SimulationStatus.RigidBodyFinished, true);
    }
    public void DrawPhysX()
    {
      foreach (Actor actor in _scene.Actors)
      {
        Vector3 actualColor = new Vector3(1, 1, 1);
        foreach (Shape shape in actor.Shapes)
        {
          switch (shape.Type)
          {
            case ShapeType.Box:
              DrawPhysicsBox((BoxShape)shape, actualColor);
              break;
            case ShapeType.Sphere:
              if (actor.IsSleeping)
              {
                DrawPhysicsSphere((SphereShape)shape, /*new Vector3(0, 0, 1)*/actualColor, true);
              }
              else
              {
                DrawPhysicsSphere((SphereShape)shape, actualColor, false);
              }
                break;
          }
        }
      }
    }
    Actor CreateKinematicBox(Vector3 pos, Vector3 boxDim, string name, float mass)
    {
      BodyDescription bodyDescription = new BodyDescription(mass);
      bodyDescription.BodyFlags = BodyFlag.Kinematic;
      ActorDescription actorDesc = new ActorDescription();
      actorDesc.GlobalPose = Matrix.CreateTranslation(pos);
      actorDesc.BodyDescription = bodyDescription;
      actorDesc.Name = name;
      actorDesc.Shapes.Add(new BoxShapeDescription(boxDim));
      return _scene.CreateActor(actorDesc);
    }
    Actor CreateBox(Vector3 pos, Vector3 boxDim, float density)
    {
      ActorDescription actorDesc = new ActorDescription();
      BodyDescription bodyDesc = new BodyDescription();
      BoxShapeDescription boxShapeDesc = new BoxShapeDescription();
      boxShapeDesc.Dimensions = new Vector3(boxDim.X, boxDim.Y, boxDim.Z);
      boxShapeDesc.LocalPose = Matrix.CreateTranslation(0, boxDim.Y, 0);
      actorDesc.Shapes.Add(boxShapeDesc);
      actorDesc.GlobalPose = Matrix.CreateTranslation(pos);
      if (density > 0)
      {
        actorDesc.BodyDescription = bodyDesc;
        actorDesc.Density = density;
      }
      //if (!actorDesc.IsValid())
      //  throw new InvalidDataException();
      return _scene.CreateActor(actorDesc);
    }
    void DrawPhysicsBox(BoxShape shape, Vector3 color)
    {
      {
        Matrix[] transforms = new Matrix[_box.Bones.Count];
        _box.CopyAbsoluteBoneTransformsTo(transforms);
        foreach (ModelMesh mesh in _box.Meshes)
        {
          foreach (BasicEffect effect in mesh.Effects)
          {
            effect.DiffuseColor = color;
            effect.EnableDefaultLighting();
            effect.World = Matrix.CreateScale(shape.Dimensions.X, shape.Dimensions.Y, shape.Dimensions.Z) * transforms[mesh.ParentBone.Index] * shape.GlobalPose;
            effect.View = Camera.ViewMatrix;
            effect.Projection = Camera.ProjectionMatrix;
          }
          mesh.Draw();
        }
      }
    }
    Actor CreateSphere(Vector3 pos, float radius, float density)
    {
      // Add a single-shape actor to the scene
      ActorDescription actorDesc = new ActorDescription();
      BodyDescription bodyDesc = new BodyDescription();
      // The actor has one shape, a sphere
      SphereShapeDescription sphereDesc = new SphereShapeDescription(radius);
      sphereDesc.LocalPose = Matrix.CreateTranslation(new Vector3(0, radius, 0));
      actorDesc.Shapes.Add(sphereDesc);
      if (density > 0)
      {
        actorDesc.BodyDescription = bodyDesc;
        actorDesc.Density = density;
      }
      actorDesc.GlobalPose = Matrix.CreateTranslation(pos);
      Actor pActor = _scene.CreateActor(actorDesc);
      return pActor;
    }
    void DrawPhysicsSphere(SphereShape sphereShape, Vector3 color, bool IsSleeping)
    {
      Matrix[] transforms = new Matrix[_sphere.Bones.Count];
      _sphere.CopyAbsoluteBoneTransformsTo(transforms);
      foreach (ModelMesh mesh in _sphere.Meshes)
      {
        foreach (BasicEffect effect in mesh.Effects)
        {
          effect.World = Matrix.CreateScale(sphereShape.Radius, sphereShape.Radius, sphereShape.Radius) * transforms[mesh.ParentBone.Index] * sphereShape.GlobalPose;
          effect.View = Camera.ViewMatrix;
          effect.Projection = Camera.ProjectionMatrix;
          effect.DiffuseColor = Color.White.ToVector3();
          //effect.DiffuseColor = color;
          effect.EnableDefaultLighting();
        }
        mesh.Draw();
      }
    }
  }
 
  public class ContactReport : UserContactReport
  {
    private List<ContactReportPair> _contactPairs;
    public struct ContactReportPair
    {
      public Actor A, B;
      public ContactCallback Callback;
      public ContactReportPair(Actor a, Actor b, ContactCallback callback)
      {
        A = a;
        B = b;
        Callback = callback;
      }
    }
    public ContactReport()
    {
      _contactPairs = new List<ContactReportPair>();
      _contactPairs.Add(new ContactReportPair(PhysX.Bitok, PhysX.Border1, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball1, PhysX.Border1, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball2, PhysX.Border1, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball3, PhysX.Border1, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball4, PhysX.Border1, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball5, PhysX.Border1, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball6, PhysX.Border1, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball7, PhysX.Border1, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball8, PhysX.Border1, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball9, PhysX.Border1, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball10, PhysX.Border1, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball11, PhysX.Border1, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball12, PhysX.Border1, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball13, PhysX.Border1, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball14, PhysX.Border1, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball15, PhysX.Border1, new ContactCallback(CapsuleAndGroundPlaneContact)));

      _contactPairs.Add(new ContactReportPair(PhysX.Bitok, PhysX.Border2, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball1, PhysX.Border2, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball2, PhysX.Border2, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball3, PhysX.Border2, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball4, PhysX.Border2, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball5, PhysX.Border2, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball6, PhysX.Border2, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball7, PhysX.Border2, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball8, PhysX.Border2, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball9, PhysX.Border2, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball10, PhysX.Border2, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball11, PhysX.Border2, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball12, PhysX.Border2, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball13, PhysX.Border2, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball14, PhysX.Border2, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball15, PhysX.Border2, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.Bitok, PhysX.Border3, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball1, PhysX.Border3, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball2, PhysX.Border3, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball3, PhysX.Border3, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball4, PhysX.Border3, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball5, PhysX.Border3, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball6, PhysX.Border3, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball7, PhysX.Border3, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball8, PhysX.Border3, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball9, PhysX.Border3, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball10, PhysX.Border3, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball11, PhysX.Border3, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball12, PhysX.Border3, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball13, PhysX.Border3, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball14, PhysX.Border3, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball15, PhysX.Border3, new ContactCallback(CapsuleAndGroundPlaneContact)));

      _contactPairs.Add(new ContactReportPair(PhysX.Bitok, PhysX.Border4, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball1, PhysX.Border4, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball2, PhysX.Border4, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball3, PhysX.Border4, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball4, PhysX.Border4, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball5, PhysX.Border4, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball6, PhysX.Border4, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball7, PhysX.Border4, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball8, PhysX.Border4, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball9, PhysX.Border4, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball10, PhysX.Border4, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball11, PhysX.Border4, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball12, PhysX.Border4, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball13, PhysX.Border4, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball14, PhysX.Border4, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball15, PhysX.Border4, new ContactCallback(CapsuleAndGroundPlaneContact)));

      _contactPairs.Add(new ContactReportPair(PhysX.Bitok, PhysX.Border5, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball1, PhysX.Border5, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball2, PhysX.Border5, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball3, PhysX.Border5, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball4, PhysX.Border5, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball5, PhysX.Border5, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball6, PhysX.Border5, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball7, PhysX.Border5, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball8, PhysX.Border5, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball9, PhysX.Border5, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball10, PhysX.Border5, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball11, PhysX.Border5, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball12, PhysX.Border5, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball13, PhysX.Border5, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball14, PhysX.Border5, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball15, PhysX.Border5, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.Bitok, PhysX.Border6, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball1, PhysX.Border6, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball2, PhysX.Border6, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball3, PhysX.Border6, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball4, PhysX.Border6, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball5, PhysX.Border6, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball6, PhysX.Border6, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball7, PhysX.Border6, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball8, PhysX.Border6, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball9, PhysX.Border6, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball10, PhysX.Border6, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball11, PhysX.Border6, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball12, PhysX.Border6, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball13, PhysX.Border6, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball14, PhysX.Border6, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball15, PhysX.Border6, new ContactCallback(CapsuleAndGroundPlaneContact)));

      _contactPairs.Add(new ContactReportPair(PhysX.Bitok, PhysX.ball1, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.Bitok, PhysX.ball2, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.Bitok, PhysX.ball3, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.Bitok, PhysX.ball4, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.Bitok, PhysX.ball5, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.Bitok, PhysX.ball6, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.Bitok, PhysX.ball7, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.Bitok, PhysX.ball8, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.Bitok, PhysX.ball9, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.Bitok, PhysX.ball10, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.Bitok, PhysX.ball11, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.Bitok, PhysX.ball12, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.Bitok, PhysX.ball13, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.Bitok, PhysX.ball14, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.Bitok, PhysX.ball15, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball1, PhysX.ball2, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball1, PhysX.ball3, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball1, PhysX.ball4, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball1, PhysX.ball5, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball1, PhysX.ball6, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball1, PhysX.ball7, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball1, PhysX.ball8, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball1, PhysX.ball9, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball1, PhysX.ball10, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball1, PhysX.ball11, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball1, PhysX.ball12, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball1, PhysX.ball13, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball1, PhysX.ball14, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball1, PhysX.ball15, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball2, PhysX.ball3, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball2, PhysX.ball4, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball2, PhysX.ball5, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball2, PhysX.ball6, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball2, PhysX.ball7, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball2, PhysX.ball8, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball2, PhysX.ball9, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball2, PhysX.ball10, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball2, PhysX.ball11, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball2, PhysX.ball12, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball2, PhysX.ball13, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball2, PhysX.ball14, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball2, PhysX.ball15, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball3, PhysX.ball4, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball3, PhysX.ball5, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball3, PhysX.ball6, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball3, PhysX.ball7, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball3, PhysX.ball8, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball3, PhysX.ball9, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball3, PhysX.ball10, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball3, PhysX.ball11, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball3, PhysX.ball12, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball3, PhysX.ball13, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball3, PhysX.ball14, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball3, PhysX.ball15, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball4, PhysX.ball5, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball4, PhysX.ball6, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball4, PhysX.ball7, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball4, PhysX.ball8, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball4, PhysX.ball9, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball4, PhysX.ball10, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball4, PhysX.ball11, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball4, PhysX.ball12, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball4, PhysX.ball13, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball4, PhysX.ball14, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball4, PhysX.ball15, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball5, PhysX.ball6, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball5, PhysX.ball7, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball5, PhysX.ball8, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball5, PhysX.ball9, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball5, PhysX.ball10, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball5, PhysX.ball11, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball5, PhysX.ball12, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball5, PhysX.ball13, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball5, PhysX.ball14, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball5, PhysX.ball15, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball6, PhysX.ball7, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball6, PhysX.ball8, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball6, PhysX.ball9, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball6, PhysX.ball10, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball6, PhysX.ball11, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball6, PhysX.ball12, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball6, PhysX.ball13, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball6, PhysX.ball14, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball6, PhysX.ball15, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball7, PhysX.ball8, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball7, PhysX.ball9, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball7, PhysX.ball10, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball7, PhysX.ball11, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball7, PhysX.ball12, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball7, PhysX.ball13, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball7, PhysX.ball14, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball7, PhysX.ball15, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball8, PhysX.ball9, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball8, PhysX.ball10, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball8, PhysX.ball11, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball8, PhysX.ball12, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball8, PhysX.ball13, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball8, PhysX.ball14, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball8, PhysX.ball15, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball9, PhysX.ball10, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball9, PhysX.ball11, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball9, PhysX.ball12, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball9, PhysX.ball13, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball9, PhysX.ball14, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball9, PhysX.ball15, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball10, PhysX.ball11, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball10, PhysX.ball12, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball10, PhysX.ball13, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball10, PhysX.ball14, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball10, PhysX.ball15, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball11, PhysX.ball12, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball11, PhysX.ball13, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball11, PhysX.ball14, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball11, PhysX.ball15, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball12, PhysX.ball13, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball12, PhysX.ball14, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball12, PhysX.ball15, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball13, PhysX.ball14, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball13, PhysX.ball15, new ContactCallback(CapsuleAndGroundPlaneContact)));
      _contactPairs.Add(new ContactReportPair(PhysX.ball14, PhysX.ball15, new ContactCallback(CapsuleAndGroundPlaneContact)));
    }
    public override void OnContactNotify(ContactPair contactInformation, ContactPairFlag events)
    {
      foreach (ContactReportPair pair in _contactPairs)
      {
        if ((pair.A == contactInformation.ActorA || pair.A == contactInformation.ActorB) && (pair.B == contactInformation.ActorA || pair.B == contactInformation.ActorB))
        {
          pair.Callback(contactInformation.ActorA, contactInformation.ActorB, events);
        }
      }
    }
    private void CapsuleAndGroundPlaneContact(Actor a, Actor b, ContactPairFlag events)
    {     
      if (b.Name == "b")
      {
        float Volume = (a.LinearVelocity.X + a.LinearVelocity.Z) / 2;
        Sound.PlayBoard(Volume);
      }
      else
      {
        float Volume = (a.LinearVelocity.X + a.LinearVelocity.Z) / 2;
        Sound.PlayBall1(Volume);
      }
    }
  }
}

As you can see, in this class also manages audio that sounds when the balls is collide in this game.

namespace _GAME_
{
  public class Sound
  {
    static SoundEffect soundEffectPopadanie;
    static SoundEffect soundEffectBitokLost;
    static SoundEffect soundEffectCue;
    static SoundEffect soundEffectBorder;
    static SoundEffect soundEffectBall1;
    static SoundEffect soundEffectBall2;
    static SoundEffect soundEffectBall3;
    static SoundEffect soundEffectBall4;
    static SoundEffect soundEffectMusik;
    static SoundEffect soundEffectAplod;
    static SoundEffectInstance soundInstancePopadanie;
    static SoundEffectInstance soundInstanceBitokLost;
    static SoundEffectInstance soundInstanceCue;
    static SoundEffectInstance soundInstanceBorder;
    static SoundEffectInstance soundInstanceBall1;
    static SoundEffectInstance soundInstanceBall2;
    static SoundEffectInstance soundInstanceBall3;
    static SoundEffectInstance soundInstanceBall4;
    static SoundEffectInstance soundInstanceMusik;
    static SoundEffectInstance soundInstanceAplod;
    Random rnd = new Random();
    public static void Init(Game game)
    {
      soundEffectBitokLost = game.Content.Load<SoundEffect>("Sound/Piu");
      soundInstanceBitokLost = soundEffectBitokLost.CreateInstance();
      soundEffectPopadanie = game.Content.Load<SoundEffect>("Sound/in");
      soundInstancePopadanie = soundEffectPopadanie.CreateInstance();
      soundEffectCue = game.Content.Load<SoundEffect>("Sound/cue");
      soundInstanceCue = soundEffectCue.CreateInstance();
      soundEffectBorder = game.Content.Load<SoundEffect>("Sound/border");
      soundInstanceBorder = soundEffectBorder.CreateInstance();
      soundEffectBall1 = game.Content.Load<SoundEffect>("Sound/ball1");
      soundInstanceBall1 = soundEffectBall1.CreateInstance();
      soundEffectBall2 = game.Content.Load<SoundEffect>("Sound/ball2");
      soundInstanceBall2 = soundEffectBall2.CreateInstance();
      soundEffectBall3 = game.Content.Load<SoundEffect>("Sound/ball3");
      soundInstanceBall3 = soundEffectBall2.CreateInstance();
      soundEffectBall4 = game.Content.Load<SoundEffect>("Sound/ball4");
      soundInstanceBall4 = soundEffectBall4.CreateInstance();
      soundEffectMusik = game.Content.Load<SoundEffect>("Sound/TwinPeaks");
      soundInstanceMusik = soundEffectMusik.CreateInstance();
      soundInstanceMusik.IsLooped = true;
      soundEffectAplod = game.Content.Load<SoundEffect>("Sound/aplod");
      soundInstanceAplod = soundEffectAplod.CreateInstance();
    }
    public static void PlayCue(float volume)
    {
      soundInstanceCue.Volume = volume;
      soundInstanceCue.Play();
    }
    public static void PlayBall1(float volume)
    {
      //int n = rnd.Next(1, 5);
      float v = Math.Abs(volume / 10);
      //if (n == 1)
      //{
      //  soundInstanceBall1.Volume = Clamp(0, 1, v);
      //  soundInstanceBall1.Play();
      //}
      //else  if (n == 2)
      //{
      //  soundInstanceBall2.Volume = Clamp(0, 1, v);
      //  soundInstanceBall2.Play();
      //}
      //else if (n == 3)
      //{
      //  soundInstanceBall3.Volume = Clamp(0, 1, v);
      //  soundInstanceBall3.Play();
      //}
      //else if (n == 4)
      //{
      //  soundInstanceBall4.Volume = Clamp(0, 1, v);
      //  soundInstanceBall4.Play();
      //}
      if (soundInstanceBall1.State == SoundState.Stopped)
      {
        soundInstanceBall1.Volume = Clamp(0, 1, v);
        soundInstanceBall1.Play();
      }
      else if (soundInstanceBall2.State == SoundState.Stopped)
      {
        soundInstanceBall2.Volume = Clamp(0, 1, v);
        soundInstanceBall2.Play();
      }
      else if (soundInstanceBall3.State == SoundState.Stopped)
      {
        soundInstanceBall3.Volume = Clamp(0, 1, v);
        soundInstanceBall3.Play();
      }
      else if (soundInstanceBall4.State == SoundState.Stopped)
      {
        soundInstanceBall4.Volume = Clamp(0, 1, v);
        soundInstanceBall4.Play();
      }
    }
    public static void PlayBoard(float volume)
    {
      float v = Math.Abs(volume / 2);
      soundInstanceBorder.Volume = Clamp(0, 1, v);
      soundInstanceBorder.Play();
    }
    public static void PlayPopadanie()
    {
      soundInstancePopadanie.Play();
    }
    public static void BitokLost()
    {
      soundInstanceBitokLost.Play();
    }
    public static void PlayMusik()
    {
      soundEffectMusik.Play();
    }
    public static void PlayAplod()
    {
      if (soundInstanceAplod.State == SoundState.Stopped)
      {
        soundInstanceAplod.Play();
      }
    }
    static float Clamp(float min, float max, float value)
    {
      if (value < min)
        value = 0;
      if (value > 1)
        value = 1;     
      return value;
    }
  }
}

To be continue...

понедельник, 12 марта 2012 г.

Практическое применение XNA. Часть 10.

Генерация примитивов (Primitives)

Сегодня я хочу рассказать об альтернативном способе "рисования" на платформе XNA. До этого, что бы отобразить объект на экране, мы использовали модель. Это в большинстве случаев, оправдано если объект имеет достаточно сложную геометрическую форму. Однако для рисования простой геометрии, в XNA есть возможность создания простых примитивов.
В этой главе мы с вами рассмотрим принципы создания простых обьектов из самых простых объектов, таких как точки и линии. Вообще любые примитивы "ручного" изготовления в XNA отрисовуются с помощью метода.
DrawUserPrimitives<>()
Начнем наше изучение с самого простейшего объекта - точки. Для того, что бы нарисовать точки нам надо создать список 3D вершин, которые определяют рисуемые точки. Для этого создадим отдельный метод с названием.

InitializePointList()

Результат так же визуально не отличается от предыдущего примера.
Так схематично можно представить индексацию или топологию примитивов.

В XNA есть еще один менее распространенный способ отрисовки треугольников. Это TriangleFan. Справедливости ради надо упомянуть и о нем. В переводе с английского слово Fun означает - веер. Уже из названия многим станет понятно, что и треугольники должны распологаться в виде веера.


На основе предыдущих примеров вы должны были понять как создаются вершины и индексы, и для тех кто это понял я предлагаю в качестве тренировки самостоятельно сделать такой пример.

В этой главе, мы рассмотрели и вершины, и полигоны. Теперь пришло время на основании этих знаний создать более сложный примитив, например куб.
Для этого создадим новый проект под названием myCube и создадим в нем уже известные и необходимые нам вершинный и индексный буферы.
private void InitializeVertices()
{
  vertexDeclaration = new VertexDeclaration(GraphicsDevice,  VertexPositionColor.VertexElements);
 vertices = new VertexPositionColor[8];
 vertices[0] = new VertexPositionColor(new Vector3(0, 300, 300), Color.White);
 vertices[1] = new VertexPositionColor(new Vector3(300, 300, 300), Color.White);
 vertices[2] = new VertexPositionColor(new Vector3(300, 0, 300), Color.White);
 vertices[3] = new VertexPositionColor(new Vector3(0, 0, 300), Color.White);
 vertices[4] = new VertexPositionColor(new Vector3(0, 300, 0), Color.White);
 vertices[5] = new VertexPositionColor(new Vector3(300, 300, 0), Color.White);
 vertices[6] = new VertexPositionColor(new Vector3(300, 0, 0), Color.White);
 vertices[7] = new VertexPositionColor(new Vector3(0, 0, 0), Color.White);
 // Вершинный буфер для точек
 vertexBuffer = new VertexBuffer(GraphicsDevice,  VertexPositionColor.SizeInBytes * (vertices.Length), BufferUsage.None);
 // передаем массив точек в вершинный буфер
vertexBuffer.SetData<VertexPositionColor>(vertices);
}
private void InitializeTriangleCube()
{
  // массив индексов для треугольников
  triangleStripIndices = new short[36] { 0, 1, 2,
                                         2, 3, 0,
                                         5, 4, 7,
                                         7, 6, 5,
                                         4, 5, 1,
                                         1, 0, 4,
                                         3, 2, 6,
                                         6, 7, 3,
                                         1, 5, 6,
                                         6, 2, 1,
                                         4, 0, 3,
                                         3, 7, 4};
    }
Количество индексов взято 36 потому что у куба шесть сторон, на каждой из них лежит по два треугольника, каждый из которых имеет три вершины. А значит получается 6 * 2 * 3 = 36. В данном случае самое важное не запутаться с расстановкой индексов. И после создания вершин и индексов отправляем данные в метод Draw
    protected override void Draw(GameTime gameTime)
    {
      GraphicsDevice.Clear(Color.CornflowerBlue);
      GraphicsDevice.RenderState.FillMode = FillMode.WireFrame;
      GraphicsDevice.RenderState.CullMode = CullMode.None;
      GraphicsDevice.VertexDeclaration = vertexDeclaration;
      effect.World = Matrix.CreateRotationX(0.01f) * Matrix.CreateRotationY(0.01f) * Matrix.CreateRotationZ(0.01f) * effect.World;
      effect.Begin();
      foreach (EffectPass pass in effect.CurrentTechnique.Passes)
      {
        pass.Begin();
        graphics.GraphicsDevice.DrawUserIndexedPrimitives(PrimitiveType.TriangleList, vertices, 0, 8, triangleStripIndices, 0, 12);
        pass.End();
      }
      effect.End();
      base.Draw(gameTime);
    }
используя PrimitiveType.TriangleList для рисования полигонов. В результате у нас получился самый настоящий куб.

На данный момент у нас получился куб состоящий из сетки, как и все трехмерные модели. Однако в компьютерных играх, модели кажутся нам объемными, стены гладкие, земля хоть и не гладкая, но и не сетчатая. Как же нам превратить наш куб в подобие вышесказанного. Если мы укажем рендеру режим заливки
GraphicsDevice.RenderState.FillMode = FillMode.Solid;
то получим белый куб, такой как на рисунке

а если помимо этого мы заставим наш шейдер выводить на экран цвет вершин, при этом между двумя разноцветными вершинами, цвет линейно интерполируется автоматически.

effect.VertexColorEnabled = true;
И в момент создания вершин мы каждой из них присвоим различные цвета
      vertices[0] = new VertexPositionColor(new Vector3(0, 300, 300), Color.White);
      vertices[1] = new VertexPositionColor(new Vector3(300, 300, 300), Color.Green);
      vertices[2] = new VertexPositionColor(new Vector3(300, 0, 300), Color.Blue);
      vertices[3] = new VertexPositionColor(new Vector3(0, 0, 300), Color.Red);
      vertices[4] = new VertexPositionColor(new Vector3(0, 300, 0), Color.Yellow);
      vertices[5] = new VertexPositionColor(new Vector3(300, 300, 0), Color.Violet);
      vertices[6] = new VertexPositionColor(new Vector3(300, 0, 0), Color.Pink);
      vertices[7] = new VertexPositionColor(new Vector3(0, 0, 0), Color.Black);
то получим раскрашенный куб с разноцветными сторонами

Завершающим этапом знакомства с генерированием примитивов можно назвать текстурирование. Создадим новый проект с названием myCubeTextured. Для корректного отображения нам понадобятся вершинный и индексный буферы.
VertexBuffer vertexBuffer;
IndexBuffer indexBuffer;
И естественно структура для хранения информации о вершинах. В отличии от предыдущего примера мы теперь будем использовать
VertexPositionTexture[] cubeVertices;
а не
VertexPositionColored[] cubeVertices;
Потому, что теперь вершина должна хранить не цвет, а координату текстуры и, само собой разумеется, позицию в трехмерном пространстве. Еще нам потребуется массив индексов, так же как и в предыдущем примере.
short[] cubeIndices;
Его величина по прежнему остаётся равной 36, по той же причине, ведь мы собираемся рисовать куб с помощью TriangleList, а не TriangleStrip. Далее следуют два метода которые заполнят данными оба буфера.
    private void InitializeVertices()
    {
      vertexDeclaration = new VertexDeclaration(GraphicsDevice, VertexPositionTexture.VertexElements);
      cubeVertices = new VertexPositionTexture[36];

      //Установка параметров точек, которые будут использованы для рисования фигуры
      Vector3 topLeftFront = new Vector3(0, 300.0f, 300.0f);
      Vector3 bottomLeftFront = new Vector3(0, 0, 300.0f);
      Vector3 topRightFront = new Vector3(300.0f, 300.0f, 300.0f);
      Vector3 bottomRightFront = new Vector3(300.0f, 0, 300.0f);
      Vector3 topLeftBack = new Vector3(0, 300.0f, 0);
      Vector3 topRightBack = new Vector3(300.0f, 300.0f, 0);
      Vector3 bottomLeftBack = new Vector3(0, 0, 0);
      Vector3 bottomRightBack = new Vector3(300.0f, 0, 0);
      // Координаты текстуры
      Vector2 textureTopLeft = new Vector2(0.0f, 0.0f);
      Vector2 textureTopRight = new Vector2(1.0f, 0.0f);
      Vector2 textureBottomLeft = new Vector2(0.0f, 1.0f);
      Vector2 textureBottomRight = new Vector2(1.0f, 1.0f);
      // Массив для хранения списка вершин
      // Он используется для передачи данных в вершинный буфер
      cubeVertices = new VertexPositionTexture[36];
      // Передняя часть фигуры
      cubeVertices[0] =
          new VertexPositionTexture(
          topLeftFront, textureTopLeft); // 0
      cubeVertices[1] =
          new VertexPositionTexture(
          bottomLeftFront, textureBottomLeft); // 1
      cubeVertices[2] =
          new VertexPositionTexture(
          topRightFront, textureTopRight); // 2
      cubeVertices[3] =
          new VertexPositionTexture(
          bottomRightFront, textureBottomRight); // 3
      // Задняя часть фигуры
      cubeVertices[4] =
          new VertexPositionTexture(
          topLeftBack, textureTopRight); // 4
      cubeVertices[5] =
          new VertexPositionTexture(
          topRightBack, textureTopLeft); // 5
      cubeVertices[6] =
          new VertexPositionTexture(
          bottomLeftBack, textureBottomRight); //6
      cubeVertices[7] =
          new VertexPositionTexture(
          bottomRightBack, textureBottomLeft); // 7
      // Верхняя часть фигуры
      cubeVertices[8] =
          new VertexPositionTexture(
          topLeftFront, textureBottomLeft); // 8
      cubeVertices[9] =
          new VertexPositionTexture(
          topRightBack, textureTopRight); // 9
      cubeVertices[10] =
          new VertexPositionTexture(
          topLeftBack, textureTopLeft); // 10
      cubeVertices[11] =
          new VertexPositionTexture(
          topRightFront, textureBottomRight); // 11
      // Нижняя часть фигуры
      cubeVertices[12] =
          new VertexPositionTexture(
          bottomLeftFront, textureTopLeft); // 12
      cubeVertices[13] =
          new VertexPositionTexture(
          bottomLeftBack, textureBottomLeft); // 13
      cubeVertices[14] =
          new VertexPositionTexture(
          bottomRightBack, textureBottomRight); // 14
      cubeVertices[15] =
          new VertexPositionTexture(
          bottomRightFront, textureTopRight); // 15
      // Левая часть фигуры
      cubeVertices[16] =
          new VertexPositionTexture(
          topLeftFront, textureTopRight); // 16
      cubeVertices[17] =
          new VertexPositionTexture(
          bottomLeftFront, textureBottomRight); // 17
      cubeVertices[18] =
          new VertexPositionTexture(
          topRightFront, textureTopLeft); // 18
      cubeVertices[19] =
          new VertexPositionTexture(
          bottomRightFront, textureBottomLeft); // 19

      // Вершинный буфер для точек
      vertexBuffer = new VertexBuffer(GraphicsDevice, VertexPositionTexture.SizeInBytes * (cubeVertices.Length), BufferUsage.None);
      // передаем массив точек в вершинный буфер
      vertexBuffer.SetData<VertexPositionTexture>(cubeVertices);
    }
Подготовили вершинный буфер
    private void InitializeIndices()
    {
      // С помощью этого массива определяем, к каким частям фигуры
      //Относятся те или иные компоненты массива cubeVertices
      cubeIndices = new short[] {
                                     0,  1,  2,  // Передняя //плоскость
                                     1,  3,  2,
                                     4,  5,  6,  // Задняя //плоскость
                                     6,  5,  7,
                                     8,  9, 10,  // Верхняя //плоскость
                                     8, 11,  9,
                                    12, 13, 14,  // Нижняя //плоскость
                                    12, 14, 15,
                                    16, 13, 17,  // Левая //плоскость
                                    10, 13, 16,
                                    18, 19, 14,  // Правая //плоскость
                                     9, 18, 14 };
      //Индексный буфер
      indexBuffer = new IndexBuffer(graphics.GraphicsDevice,
          sizeof(short) * cubeIndices.Length,
          BufferUsage.None,
          IndexElementSize.SixteenBits);
      //Добавляем данные в индексный буфер
      indexBuffer.SetData<short>(cubeIndices);
    }
Подготовили индексный буфер. В качестве эфекта для отображения используем старый добрый BasicEffect. Осталось только передать данные в метод Draw для рисования, и не забыть передать шейдеру саму текстуру, которую мы хотим видеть на сторонах нашего куба. Как все это сделать, показано ниже.
    protected override void Draw(GameTime gameTime)
    {
      GraphicsDevice.Clear(Color.CornflowerBlue);
      GraphicsDevice.RenderState.FillMode = FillMode.Solid;
      GraphicsDevice.RenderState.CullMode = CullMode.None;
      GraphicsDevice.VertexDeclaration = vertexDeclaration;     
      graphics.GraphicsDevice.Indices = indexBuffer;     
      graphics.GraphicsDevice.Vertices[0].SetSource(vertexBuffer, 0, VertexPositionTexture.SizeInBytes);
      effect.World = Matrix.CreateRotationX(0.01f) * Matrix.CreateRotationY(0.01f) * Matrix.CreateRotationZ(0.01f) * effect.World;
      effect.TextureEnabled = true;
      effect.Texture = Content.Load<Texture2D>("picture");
      effect.Begin();
      foreach (EffectPass pass in effect.CurrentTechnique.Passes)
      {
        pass.Begin();
        graphics.GraphicsDevice.DrawUserIndexedPrimitives(PrimitiveType.TriangleList, cubeVertices, 0, cubeVertices.Length, cubeIndices, 0, 12);
        pass.End();
      }
      effect.End();
      base.Draw(gameTime);
    }

И если вы всё сделали верно, то результатом станет следующее изображение.

На этом мы заканчиваем знакомство с примитивами. В качестве упражнения советую вам создать несколько собственных геометрических фигур на досуге. Принципы создания одинаковы как для кубов, так и для всех остальных объектов.







 в нем создадим два экземпляра классов
VertexDeclaration vertexDeclaration;
VertexPositionColor[] pointList;
vertexDeclaration = new VertexDeclaration(GraphicsDevice, VertexPositionColor.VertexElements);
pointList = new VertexPositionColor[8];
и заполним  pointList в цикле
for (int x = 0; x < points / 2; x++)
{
  for (int y = 0; y < 2; y++)
  {
  pointList[(x * 2) + y] = new VertexPositionColor(new Vector3(x  * 100, y * 100, 0), Color.White);
  }
}
указав каждой вершине ее позицию в пространстве и  ее цвет. После этого нам в обязательном порядке нужно  создать вершинный буфер для точек (вершин)
vertexBuffer = new VertexBuffer(GraphicsDevice, VertexPositionColor.SizeInBytes * (pointList.Length), BufferUsage.None);
и передать массив точек в вершинный буфер
vertexBuffer.SetData<VertexPositionColor>(pointList);
Не лишним будет подумать и об шейдере с помощью которого мы будем выводить на экран наши точки. Воспользуемся старым добрым Basic Effect, но его инициализацию осуществим в методе
protected override void Initialize()
а не в методе отрисовки точек. Выглядит это вот так:
  protected override void Initialize()
  {
      effect = new BasicEffect(graphics.GraphicsDevice, null);
      effect.World = Matrix.CreateTranslation
      (GraphicsDevice.Viewport.Width / 2f - 150,
       GraphicsDevice.Viewport.Height / 2f - 50, 0);
      effect.View = Matrix.CreateLookAt
      (new Vector3(0.0f, 0.0f, 1.0f),
       Vector3.Zero, Vector3.Up);
      effect.Projection = Matrix.CreateOrthographicOffCenter
      (0, (float)GraphicsDevice.Viewport.Width,
      (float)GraphicsDevice.Viewport.Height, 0, 1.0f, 1000.0f);
     
      InitializePointList();
      base.Initialize();
  }
как видите все параметры касающиеся матриц создаются и присваиваются здесь. После подготовки нашего эффекта нам остаётся лишь передать наш PointList в метод Draw для отрисовки.
protected override void Draw(GameTime gameTime)
{
  GraphicsDevice.Clear(Color.CornflowerBlue);
  GraphicsDevice.VertexDeclaration = vertexDeclaration;
  GraphicsDevice.RenderState.PointSize = 10;
   effect.Begin();
   foreach (EffectPass pass in effect.CurrentTechnique.Passes)
   {
     pass.Begin();
         graphics.GraphicsDevice.DrawUserPrimitives(PrimitiveType.PointLi  st, pointList, 0, 8);
       pass.End();
    }
      effect.End();
  base.Draw(gameTime);
}
как видите здесь мы установили размер точки равной 10 , что бы лучше было видно, и методом DrawUserPrimitives() нарисовали наши вершины.
при этом передавая первым параметром тип примитива, в нашем случае это точки (тип примитива определяет как данные в переданном массиве вершин будут интерпретированы и показаны), вторым параметром данные о точках (позиция и цвет) хранящиеся в массиве pointList, третьим параметром номер примитива с которого начинать рисовать (у нас это может быть значение от 0 до 8), и накнец, четвертым параметром количество примитивов для отрисовки начиная с указанного. Исходный код примера находится на диске в папке Samples / myPoints.
Аналогично точкам можно рисовать линии. Рисуются они от точки до точки по кратчайшему расстоянию. Для наглядной реализации мы добавим к нашему примеру метод создания индексов для линий.
    private void InitializeLineList()
    {
      // массив индексов для линий
      lineListIndices = new short[(8 * 2) - 2];
      // заполняем индексы
      for (int i = 0; i < 8 - 1; i++)
      {
        lineListIndices[i * 2] = (short)(i);
        lineListIndices[(i * 2) + 1] = (short)(i + 1);
      }
    }
Другими словами, данный код равнозначен следующему и определяет линии соеденяющие наши точки - pointList[0] и pointList[1], pointList[1] и pointList[2], и так далее 7 линий между 8-ми точек.
lineListIndices = new short[14]{ 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7 };
а в методе Draw вместо функции
DrawUserPrimitives()
применим функцию
DrawUserIndexedPrimitives()
и наш метод для рисования линий станет таким
protected override void Draw(GameTime gameTime)
{
  GraphicsDevice.Clear(Color.CornflowerBlue);
  GraphicsDevice.VertexDeclaration = vertexDeclaration;
  effect.Begin();
  foreach (EffectPass pass in  effect.CurrentTechnique.Passes)
  {
     pass.Begin();
     graphics.GraphicsDevice.DrawUserIndexedPrimitives
     (PrimitiveType.LineList,
      pointList,
      0,
      8,
      lineListIndices,
      0,
      7);
      pass.End();
   }
   effect.End();
  base.Draw(gameTime);
}
где первым параметром передается тип примитива LineList, далее все так же как и с точками до пятого параметра,а им передаётся LineListIndices массив индексов, шестым и седьмым параметрами номер и количество линий. В качестве шейдера по прежнему используется BasicEffect. На рисунке ниже изображен результат этих изменений.
 Следующий пример показывает как вместо линий рисовать, так называемую, ленту линий или LineStrip.
Вся разница между этим и предыдущим примером заключается в том, что для ленты нам понадобится в двое меньше индексов так как лента подразумевает последовательно соединенные примитивы (точки). Метод для создания индексов теперь будет выглядеть так.
    private void InitializeLineStrip()
    {
      // массив индексов для линий
      lineStripIndices = new short[8];
      // заполняем индексы
      for (int i = 0; i < 8; i++)
      {
        lineStripIndices[i] = (short)(i);
      }
    }
или
lineStripIndices = new short[8]{ 0, 1, 2, 3, 4, 5, 6, 7 };
Важно сказать, что рисование лент с точки зрения производительности более выгодное, так как индексы выводимых вершин содержат меньшее количество повторений. Результат изображен на рисунке ниже и внешне ничем не отличается от предыдущего примера.
Следующим этапом будет создание треугольника или полигона. Как уже упоминалось ранее из полигонов состоят практически все трехмерные модели. Сразу скажу, что треугольники, подобно линиям, тоже можно рисовать как из списка так и лентой. Следующий пример показывает способ рисования из списка или TriangleList. Создаем новый массив индексов, в котором будет пронумерованный список треугольников, по 3 вершины на один треугольник. Массив вершин не меняем. Метод для этого выглядит так.
 private void InitializeTriangleList()
{
   int width = 4;
   int height = 2;
      // массив индексов для треугольников
      triangleListIndices = new short[(width - 1) * (height - 1)  * 6];
  for (int x = 0; x < width - 1; x++)
  {
    for (int y = 0; y < height - 1; y++)
    {
          triangleListIndices[(x + y * (width - 1)) * 6] = (short)(2 * x);
          triangleListIndices[(x + y * (width - 1)) * 6 + 1] = (short)(2 * x + 1);
          triangleListIndices[(x + y * (width - 1)) * 6 + 2] = (short)(2 * x + 2);
          triangleListIndices[(x + y * (width - 1)) * 6 + 3] = (short)(2 * x + 2);
          triangleListIndices[(x + y * (width - 1)) * 6 + 4] = (short)(2 * x + 1);
          triangleListIndices[(x + y * (width - 1)) * 6 + 5] = (short)(2 * x + 3);
        }
      }
    }
Этот код индексирует вершины так что каждые 3 точки у нас образуют один треугольник, причем в каждом треугольнике есть общие вершины с соседним. Ниже аналог этого кода
triangleListIndices = new short[18]{ 0, 1, 2, 2, 1, 3, 2, 3, 4, 4, 3, 5, 4, 5, 6, 6, 5, 7 };
Выводим список треугольников на экран используя функцию DrawUserIndexedPrimitives() которая в качестве первого аргумента принимает тип выводимого примитива - PrimitiveType.TriangleList. И еще, перед отрисовкой важно указать видеокарте режим рендеринга.
GraphicsDevice.RenderState.FillMode = FillMode.WireFrame;
GraphicsDevice.RenderState.CullMode = CullMode.None;
Здесь мы назначили режим отображения обьектов в виде сетки.
В результате  метод Draw будет выглядеть так:
    protected override void Draw(GameTime gameTime)
    {
      GraphicsDevice.Clear(Color.CornflowerBlue);
      GraphicsDevice.RenderState.FillMode = FillMode.WireFrame;
      GraphicsDevice.RenderState.CullMode = CullMode.None;
      GraphicsDevice.VertexDeclaration = vertexDeclaration;
      effect.Begin();
      foreach (EffectPass pass in effect.CurrentTechnique.Passes)
      {
        pass.Begin();
        graphics.GraphicsDevice.DrawUserIndexedPrimitives(PrimitiveType.TriangleList, pointList, 0, 8, triangleListIndices, 0, 6);
        pass.End();
      }
      effect.End();
      base.Draw(gameTime);
    }
а итоговое изображение так:
Следующий пример демонстрирует рисование тех же треугольников, но с помощью ленты, а не списка. Как вы уже наверное догадались, изменений потребует лишь метод индексирующий вершины. Этот метод будет выглядеть так
    private void InitializeTriangleStrip()
    {
      // массив индексов для треугольников
      triangleStripIndices = new short[8];
      // заполняем индексы
      for (int i = 0; i < 8; i++)
      {
        triangleStripIndices[i] = (short)i;
      }
    }
он практически идентичен методу InitializeLineStrip()из примера myLineStrip. И результат его деятельности так же можно представить в виде
triangleStripIndices = new short[8]{ 0, 1, 2, 3, 4, 5, 6, 7 };
В лентах, треугольники последовательно соеденены между собой, так что индексов нам нужно намного меньше – каждый последующий треугольник использует две вершины предыдущего треугольника. Это наиболее эффективный способ вывода треугольников.

Physically Based Rendering