XNA for C#
DirectX 9 for C#
DirectX 9 for C++
DirectX 9 for VB
Forum
   
My XNA Book
      
       Go to section on this site

Additional info


Latest Forum posts

 Account settings
  Posted by: Anonymous
  When: 07/05/2014 at 09:48:39

 forced subtitle
  Posted by: Applefly
  When: 07/05/2014 at 06:00:48

 convert DVD into PMS
  Posted by: Applefly
  When: 07/05/2014 at 05:55:25

 DVD to Digital Copy easily
  Posted by: VIKIVannessa
  When: 05/05/2014 at 06:52:29

 DVD on Xbox 360/Xbox One Console
  Posted by: VIKIVannessa
  When: 05/05/2014 at 06:51:47

 Extract .Srt Subtitles
  Posted by: Applefly
  When: 04/05/2014 at 03:54:38

 Encode Movie collection
  Posted by: Applefly
  When: 04/05/2014 at 03:52:41

 Convert DVD to WMV
  Posted by: Applefly
  When: 29/04/2014 at 05:53:50

 rip DVDs into digital files
  Posted by: Applefly
  When: 29/04/2014 at 05:51:20

 iTunes movies/music to Kindle Fire
  Posted by: ciciyu80
  When: 29/04/2014 at 05:10:20


Ads

Creating a floorplan

Now we’ve seen how we can import simple images into DirectX and have DirectX display them on triangles, it’s not that difficult to create a large amount of images. It’s more important to find a way to get the computer to define all of the vertices for us.

As a small example, let’s simply create a raster of 3x3 images, with the center image missing. This means 8 images, thus 16 triangles, and 48 vertices. Instead of defining all these vertices manually, let’s create a variable int_Floorplan, at the top of your code :

 private int[,] int_Floorplan;
 private int WIDTH;
 private int HEIGHT;
 private int differentbuildings = 5;
 
 private CustomVertex.PositionNormalTextured[] verticesarray;
 ArrayList verticeslist = new ArrayList();

We declare 2 more variables to store the size of the floor to be created. Of course, these values have to correspond with the size of the int_Floorplan array. We’ve already indicated how many different kinds of buildings we will have in our game, which we will store in the differentbuildings variable. Replace your vertices variable from the previous chapter by the verticesarray variable. Use this name so you won’t get confused later in this series, when we’ll be using more vertices. Also notice that this time we’ll be adding normal data to our vertices, so later on we can easily switch on lights (as you’ve seen in Series 1). You’ll find some info on the arraylist a few paragraphs below.

We’ll need a small method that fills the int_Floorplan array with data:

 private void LoadFloorplan()
 {
  WIDTH = 3;
  HEIGHT = 3;
 
  int_Floorplan = new int[,]
  {
  {0,0,0},
  {0,1,0},
  {0,0,0},
  };
 }

In this data, a 0 means ‘draw a floor texture’ and a 1 means to leave that tile open. Later in this series, a 1 will mean a building, and a 0 ‘not a building’. This method contains all the flexibility of our program: simply changing a 0 to a 1 will result in an extra building drawn. Load this method from within the main method, immediately before you call the VertexDeclaration method:

 our_directx_form.LoadFloorplan();

Now we’ll have to update our VertexDeclaration method, so it reads the data inside the array and automatically creates the corresponding vertices. In the last chapter you’ve learned how to paste images on triangles. This time, we’re going to load 1 texture image file, that is composed of several images next to each other. The leftmost part of the texture will be the floor tile, and then a wall and a roofing image for each different type of building. You can download my texturefile (link) to see what I mean. It is in short one image that contains multiple images. In the texture, I've included self-drawn images as well as real-life pictures, so later on you'll be able to see the difference. Make sure you replace the filename in your LoadTexturesAndMaterials method.

We’ll start the method by defining how many images are contained in the big texture image, 1 floor image and 2 images for every building type (have another look at the texturempap.jpg file to better understand this):

 float imagesintexture = 1 + differentbuildings * 2;

Now the method has to scan the data from the int_Floorplan and draw a floorimage everytime a 0 is found:

 for (int x = 0; x < WIDTH; x++)
 {
  for (int y = 0; y < HEIGHT; y++)
  {
  if (int_Floorplan[x, y] == 0)
  {
  verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x, y, 0), new Vector3(0, 0, 1), 1f / imagesintexture, 1));
  verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x + 1, y, 0), new Vector3(0, 0, 1), 0, 1));
  verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x + 1, y + 1, 0), new Vector3(0, 0, 1), 0, 0));
 
  verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x + 1, y + 1, 0), new Vector3(0, 0, 1), 0, 0));
  verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x, y + 1, 0), new Vector3(0, 0, 1), 1f / imagesintexture, 0));
  verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x, y, 0), new Vector3(0, 0, 1), 1f / imagesintexture, 1));
  }
  }
 }

As you can see, we’ve been using the Arraylist. This is simply a kind of array of which you do not need to specify the length, you can simply add elements to it by using the Add method. Very useful in cases like this, because initially we don’t know how many surfaces/triangles we will need to draw. Another related advantage is that we can get the amount of elements stored in the list by reading the arraylist.Count variable. This will come in handy when we need to specify the amount of triangles to draw.

Every time a 0 is encountered, 2 triangles are defined. The normal vectors are pointing upwards (0,0,1), and the correct portion of the texture image is pasted over the image: the rectangle between (0,0) and (1/imagesintexture,1). Have another look at the texture file to fully understand this.

When this arraylist has been created, we need to convert it to a usual array. This can be done very easily with the ToArray command of the Arraylist. We only need to specify the kind of data stored in the list, and typecast it.

 verticesarray = (CustomVertex.PositionNormalTextured[])verticeslist.ToArray(typeof(CustomVertex.PositionNormalTextured));

With this method finished, we simply need to load our new texture file into our texture variable in our LoadTextureAndMaterials method. Because later on we’ll be using some more textures, let’s call our ‘texture’ variable ‘scenerytexture’ from now on (you also need to change the name of this variable where we declare it):

 scenerytexture = TextureLoader.FromFile(device, "texturemap.jpg");

Remember you can download the file here (link). Now we need to update the OnPaint method to draw from the new verticesarray:

 device.VertexFormat = CustomVertex.PositionNormalTextured.Format;
 device.SetTexture(0, scenerytexture);
 device.DrawUserPrimitives(PrimitiveType.TriangleList, verticeslist.Count / 3, verticesarray);

Don’t forget to indicate that we’ve added Normal data to our vertices. The last line uses the verticeslist.Count value to derive the amount of triangles to draw.

This code should be runnable! You should see a small square with a hole in the middle, just as you defined in the LoadFloorPlan method. It might be a good idea to repoposition the camera a bit:

 device.Transform.View = Matrix.LookAtLH(new Vector3(3, -2, 5), new Vector3(2, 1, 0), new Vector3(0, 0, 1));

This should give you the following image:




DirectX Tutorial 3 - The floorplan

If you appreciate the amount of time I spend creating and updating
these pages, feel free to donate -- any amount is welcome !



Click here to go to the forum on this chapter!

Or click on one of the topics on this chapter to go there:
  • Wrong Image Inserted And Uploaded !
          Your link has the wrong image that is texturemap.j...
  • ArrayList
          I was wondering whether it would be as functional ...
  • texture bug
          Hi, when I tried the code the texture on the floo...


    Try playing around with the contents of the int_Floorplan variable. Remember you need to change the WIDTH and HEIGHT variables when you want to add extra rows or columns!

    This chapter we’ve seen how to load parts of a texture and how to use the Arraylist, a handy feature of C#. You might already notice the lines between the floor tiles, this is because the height and width of our texture image aren’t a power of 2. More on this later. The code:

     using System;
     using System.Drawing;
     using System.Collections;
     using System.ComponentModel;
     using System.Windows.Forms;
     using System.Data;
     using Microsoft.DirectX;
     using Microsoft.DirectX.Direct3D;
     using D3D = Microsoft.DirectX.Direct3D;
     
     namespace DirectX_Tutorial
     {
         public class WinForm : System.Windows.Forms.Form
         {
             private int[,] int_Floorplan;
             private int WIDTH;
             private int HEIGHT;
             private int differentbuildings = 5;
     
             private System.ComponentModel.Container components = null;
             private D3D.Device device;
             private Texture scenerytexture;
             private Material material;
     
             private CustomVertex.PositionNormalTextured[] verticesarray;
             ArrayList verticeslist = new ArrayList();
     
             public WinForm()
             {
                 InitializeComponent();
                 this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.Opaque, true);
             }
     
             public void InitializeDevice()
             {
                 PresentParameters presentParams = new PresentParameters();
                 presentParams.Windowed = true;
                 presentParams.SwapEffect = SwapEffect.Discard;
                 presentParams.AutoDepthStencilFormat = DepthFormat.D16;
                 presentParams.EnableAutoDepthStencil = true;
                 device = new D3D.Device(0, D3D.DeviceType.Hardware, this, CreateFlags.SoftwareVertexProcessing, presentParams);
     
                 device.RenderState.Lighting = false;
             }
     
             protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
             {
                 device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.DarkSlateBlue, 1.0f, 0);
                 device.BeginScene();
     
                 device.VertexFormat = CustomVertex.PositionNormalTextured.Format;
                 device.SetTexture(0, scenerytexture);
                 device.DrawUserPrimitives(PrimitiveType.TriangleList, verticeslist.Count / 3, verticesarray);
     
                 device.EndScene();
                 device.Present();
                 this.Invalidate();            
             }
     
             private void SetUpCamera()
             {
                 device.Transform.Projection = Matrix.PerspectiveFovLH((float)Math.PI / 4, this.Width / this.Height, 0.3f, 500f);
                 device.Transform.View = Matrix.LookAtLH(new Vector3(3, -2, 5), new Vector3(2, 1, 0), new Vector3(0, 0, 1));
             }
     
             private void VertexDeclaration()
             {
                 float imagesintexture = 1 + differentbuildings * 2;
     
                 for (int x = 0; x < WIDTH; x++)
                 {
                     for (int y = 0; y < HEIGHT; y++)
                     {
                         if (int_Floorplan[x, y] == 0)
                         {
                             verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x, y, 0), new Vector3(0, 0, 1), 1f / imagesintexture, 1));
                             verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x + 1, y, 0), new Vector3(0, 0, 1), 0, 1));
                             verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x + 1, y + 1, 0), new Vector3(0, 0, 1), 0, 0));
     
                             verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x + 1, y + 1, 0), new Vector3(0, 0, 1), 0, 0));
                             verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x, y + 1, 0), new Vector3(0, 0, 1), 1f / imagesintexture, 0));
                             verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x, y, 0), new Vector3(0, 0, 1), 1f / imagesintexture, 1));
                         }
                     }
                 }
                 verticesarray = (CustomVertex.PositionNormalTextured[])verticeslist.ToArray(typeof(CustomVertex.PositionNormalTextured));
             }
     
             private void LoadTexturesAndMaterials()
             {
                 material = new Material();
     
                 material.Diffuse = Color.White;
                 material.Ambient = Color.White;
     
                 device.Material = material;
     
                 scenerytexture = TextureLoader.FromFile(device, "texturemap.jpg");
             }
     
             private void LoadFloorplan()
             {
                 WIDTH = 3;
                 HEIGHT = 3;
     
                 int_Floorplan = new int[,]
                 {
                     {0,0,0},
                     {0,1,0},
                     {0,0,0},
                 };
             }
     
             protected override void Dispose(bool disposing)
             {
                 if (disposing)
                 {
                     if (components != null)
                     {
                         components.Dispose();
                     }
                 }
                 base.Dispose(disposing);
             }
     
             private void InitializeComponent()
             {
                 this.components = new System.ComponentModel.Container();
                 this.Size = new System.Drawing.Size(500, 500);
                 this.Text = "Riemer's DirectX Tutorial using C# -- Season 2";
             }
     
             static void Main()
             {
                 using (WinForm our_directx_form = new WinForm())
                 {
                     our_directx_form.InitializeDevice();
                     our_directx_form.SetUpCamera();
                     our_directx_form.LoadFloorplan();
                     our_directx_form.VertexDeclaration();
                     our_directx_form.LoadTexturesAndMaterials();
                     Application.Run(our_directx_form);
                 }
             }
         }
     }
     


    Google
     
    Webwww.riemers.net


    If you appreciate the amount of time I spend creating and updating
    these pages, feel free to donate -- any amount is welcome !



    - Website design & XNA + DirectX code : Riemer Grootjans -
    ©2003 - 2011 Riemer Grootjans
  • Translations

    This site in English
    This site in Korean
    This site in Czech

    Microsoft MVP Award



    2007 - 2011 MVP Award
    DirectX - XNA

    Contents

    News
    Home
    Forum
    XNA 2.0 Recipes Book (8)
    XNA 3.0 Recipes Book (8)
    Downloads
    Extra Reading (3)
    Matrices: geometrical
    Matrix Mathematics
    Homogenous matrices
    Community Projects (1)
    Tutorials (160)
    XNA 4.0 using C# (89)
    DirectX using C# (54)
    Series 1:Terrain (14)
    Series 2: Flightsim (19)
    Starting code
    Textures
    The floorplan
    Creating the 3D City
    Meshloading from file
    Ambient light
    Action
    Flight kinematics
    Collision detection
    Skybox
    Texture filtering
    Adding targets
    Point sprites
    Alpha blending
    DirectSound
    Sounds in 3D
    Playing MP3 files
    Displaying text
    Going fullscreen
    Series 3: HLSL (19)
    Short Tuts (2)
    DirectX using C++ (15)
    DirectX using VB (2)
    -- Expand all --


    Thank you!

    Support this site --
    any amount is welcome !

    Stay up-to-date

    I don't have the time to keep a News section, so stay informed about the updates by clicking on this RSS file!