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

Importing height data from .bmp files

Since this is one of the most common questions I'm being mailed, I'm writing a small chapter on importing height maps from a .bmp file instead of .raw files. You can easily edit/create your own .bmp files using MS Paint or other standard software.
 
The reason why I chose the .raw file to start with, is that every byte you read from the file is useful information. A .bmp file includes lots of other information, such as the filesize, the width and height of the image and so on. This makes importing data from .bmp a little more difficult than importing from .raw files, since now we have to cut away all the useless info. I will include a small summary on the structure of .bmp-files. If you're not interested in this, simply copy the code you find at the bottom of this page.
 
In a .bmp-file, the actual pixel-data is preceded by a header. All values are between 0 and 255, but you don't find decimal values in a .bmp file, you find the ASCII representations of the values. As an example, when you open any .bmp with a simple text-editor like notepad, the first 2 letters always are BM. These correspond to the numbers 66 and 77 in the ASCII table. (Try opening a text editor and hold ALT while typing 66 or 77.) The rest of the file probably won't be readable, because in the ASCII table most numbers between 0 and 255 don't correspondent to a letter/number. When we use the ReadByte()method, it returns the ASCII value of the byte read. So, if we would read the first byte of any .bmp-file (always a B), it would return 66.
 
Here is a description of the header :
ByteNrValue
1-266 77Always BM
3-6X X X XFilesize
7-100 0 0 0Always 0
11-14X X X XOffset to pixeldata
15-1840 0 0 0Always 40
19-22X X X XImage width in pixels
23-26X X X XImage height in pixels
27-281 0Always 1
29-3224 0 0 0Bits per pixel
33-360 0 0 0Compression, usually 0
37-534x(0 0 0 0)No longer used

The most important entries include the image width, height and the bytenr where the actual pixel-data can be found. The actual pixeldata can be put immediately after the header, thus at byte 54. This is usually done, for example MS paint puts it there. The pixel data contains 24 bits per pixel, this corresponds to 3 bytes, which each can contain a value between 0 and 255. There is one byte for the Red, the Green and the Blue value for each pixel.
 
So, if we want to use the pixeldata as heightdata, first we have to find the offset to the actual data, and then simply sum the 3 colorvalues to become the gray value of the pixel, which will be our height. Delete the contents of your LoadHeightData() method and declare these variables:

  int offset;
 byte dummy;

 We will store the offset in the first variable, and need the 'dummy' variable to temporarily store the bytes read we don't need. Next we’re going to open our .bmp file:

 FileStream fs = new FileStream("heightmap.bmp",FileMode.Open, FileAccess.Read);
 BinaryReader r = new BinaryReader(fs);

 Now it's time to scroll to the byte that indicates the offset to the actual pixeldata. To do this, simply read 10 bytes to position our reader at byte 11, the first offset byte.

 
for (int i = 0;i< 10;i++){
    dummy = r.ReadByte();
}

The following 4 bytes represent the offset. Since every byte can only represent a value between 0 and 255, the first byte has to be multiplied by 1, the second by 256, the next by 256*256 and so on:

  offset = r.ReadByte();
 offset += r.ReadByte()*256;
 offset += r.ReadByte()*256*256;
 offset += r.ReadByte()*256*256*256;

 Next we scroll further another 4 bytes to byte 19, where we find the WIDTH and the HEIGHT of the image: 


for (int i = 0;i< 4;i++){
    dummy = r.ReadByte();
}
 
WIDTH = r.ReadByte();
WIDTH += r.ReadByte()*256;
WIDTH += r.ReadByte()*256*256;
WIDTH += r.ReadByte()*256*256*256;
 
HEIGHT = r.ReadByte();
HEIGHT += r.ReadByte()*256;
HEIGHT += r.ReadByte()*256*256;
HEIGHT += r.ReadByte()*256*256*256;

Now we can initialise our heightData array and scroll further to the pixeldata:

 heightData = new int[WIDTH,HEIGHT];

for (int i = 0;i< (offset - 26);i++){
    dummy = r.ReadByte();
}

 Now we have the exact width and height, we are going to store the sum of the 3 colors as the height for a pixel. Divide to normalize.

 
for (int i = 0;i< HEIGHT;i++){

    for (int y=0; y< WIDTH; y++)    {
        int height =(int)(r.ReadByte());
        height +=(int)(r.ReadByte());
        height +=(int)(r.ReadByte());
        height /= 8;
        heightData[WIDTH-1-y,HEIGHT-1-i] = height;
    }
}

This is it! Now download a new example terrain here (link) and place it in your ‘Debug’ directory. Because this is twice the size of our first map, we are going to position the camera a bit further from the center. Also notice that the far plane has been adjusted to 250f, because otherwise every pixel further away from the camera than 50f would not be displayed:

  device.Transform.Projection = Matrix.PerspectiveFovLH((float)Math.PI/4, this.Width/this.Height, 1f, 250f);
 device.Transform.View = Matrix.LookAtLH(new Vector3(80,0,120), new Vector3(-20,0,0), new Vector3(0,0,1));

Now run this code, this is what you should see:




DirectX Tutorial 10 - Importing bmp files

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:
  • invalidcallexception
          i am having a problem at vb = new VertexBuffer(...
  • More efficient byte cycling
          Hey there. May I start off by saying that these tu...
  • BMP as textures
          I have a program developed with Windows Presentati...
  • textureing by a bmp file
          hi. i have creat a flat earth by below code. but...
  • Easier way for reading files
          An easier and less efficient way of reading images...
  • Partial solution to large heightmap
          Bellow is the code to draw several meshes, each on...
  • Black Screen Of Death - Help!
          i've been trying this for a week now and i cant g...
  • Using a large heightmap
          I tried to use a heightmap of size 256x256 and an ...
  • Parsing BMP Files
          I found that the BMP reader needed some revisions....
  • Error when using my own BMP files
          When ever I create my own BMP I get a EndOfStreamE...
  • Seek and Read functions
          I am curious why you don't use the Seek() method ...



    Although the contents of this chapter was not really DirectX related, I don’t think you’ll consider it a waste of time – now you can draw your own terrains in a simple program such as MS Paint! You can even change the width or the height of the image in paint (Image->Attributes), save it and load it from your C# program!

    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 System.IO;
     using Microsoft.DirectX.DirectInput;
     
     namespace DirectX_Tutorial
     {
     
         public class WinForm : System.Windows.Forms.Form
         {
             private int WIDTH = 64;
             private int HEIGHT = 64;
             private Microsoft.DirectX.Direct3D.Device device;
             private System.ComponentModel.Container components = null;
             private float angle = 0f;
             private CustomVertex.PositionColored[] vertices;
             private int[,] heightData;
             private int[] indices;
             private IndexBuffer ib;
             private VertexBuffer vb;
             private Microsoft.DirectX.DirectInput.Device keyb;
     
             public WinForm()
             {
                 InitializeComponent();
                 this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.Opaque, true);
             }
     
             public void InitializeDevice()
             {
                 PresentParameters presentParams = new PresentParameters();
                 presentParams.Windowed = true;
                 presentParams.SwapEffect = SwapEffect.Discard;
                 device = new Microsoft.DirectX.Direct3D.Device(0, Microsoft.DirectX.Direct3D.DeviceType.Hardware, this, CreateFlags.SoftwareVertexProcessing, presentParams);
                 device.RenderState.FillMode = FillMode.WireFrame;
                 device.RenderState.CullMode = Cull.None;
             }
     
             public void InitializeKeyboard()
             {
                 keyb = new Microsoft.DirectX.DirectInput.Device(SystemGuid.Keyboard);
                 keyb.SetCooperativeLevel(this, CooperativeLevelFlags.Background | CooperativeLevelFlags.NonExclusive);
                 keyb.Acquire();
             }
     
             private void CameraPositioning()
             {
                 device.Transform.Projection = Matrix.PerspectiveFovLH((float)Math.PI / 4, this.Width / this.Height, 1f, 250f);
      device.Transform.View = Matrix.LookAtLH(new Vector3(80, 0, 120), new Vector3(-20, 0, 0), new Vector3(0, 0, 1));
                 device.RenderState.Lighting = false;
                 device.RenderState.CullMode = Cull.None;
             }
     
             private void VertexDeclaration()
             {
                 vb = new VertexBuffer(typeof(CustomVertex.PositionColored), WIDTH*HEIGHT, device, Usage.Dynamic | Usage.WriteOnly, CustomVertex.PositionColored.Format, Pool.Default);
                 vertices = new CustomVertex.PositionColored[WIDTH*HEIGHT];

                for (int x=0;x< WIDTH;x++)            {

                    for (int y=0; y< HEIGHT;y++)                {
                        vertices[x+y*WIDTH].Position = new Vector3(x, y, heightData[x,y]);
                        vertices[x+y*WIDTH].Color = Color.White.ToArgb();
                    }
                }

                vb.SetData(vertices, 0 ,LockFlags.None);
            }

            private void IndicesDeclaration()
            {
                ib = new IndexBuffer(typeof(int), (WIDTH-1)*(HEIGHT-1)*6, device, Usage.WriteOnly, Pool.Default);
                indices = new int[(WIDTH-1)*(HEIGHT-1)*6];

                for (int x=0;x< WIDTH-1;x++)            {

                    for (int y=0; y< HEIGHT-1;y++)                {
                        indices[(x+y*(WIDTH-1))*6] = (x+1)+(y+1)*WIDTH;
                        indices[(x+y*(WIDTH-1))*6+1] = (x+1)+y*WIDTH;
                        indices[(x+y*(WIDTH-1))*6+2] = x+y*WIDTH;

                        indices[(x+y*(WIDTH-1))*6+3] = (x+1)+(y+1)*WIDTH;
                        indices[(x+y*(WIDTH-1))*6+4] = x+y*WIDTH;
                        indices[(x+y*(WIDTH-1))*6+5] = x+(y+1)*WIDTH;
                    }
                }
                ib.SetData(indices, 0, LockFlags.None);
            }

            protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
            {
                device.Clear(ClearFlags.Target, Color.Black , 1.0f, 0);

                device.BeginScene();
                device.VertexFormat = CustomVertex.PositionColored.Format;
                device.SetStreamSource(0, vb, 0);
                device.Indices = ib;

                device.Transform.World = Matrix.Translation(-HEIGHT/2, -WIDTH/2, 0)*Matrix.RotationZ(angle) ;
                device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, WIDTH*HEIGHT, 0, indices.Length/3);
                device.EndScene();

                device.Present();

                this.Invalidate();

                ReadKeyboard();
            }

            private void ReadKeyboard()
            {
                KeyboardState keys = keyb.GetCurrentKeyboardState();
                if (keys[Key.Delete])
                {
                    angle+=0.03f;
                }
                if (keys[Key.Next])
                {
                    angle-=0.03f;
                }
            }


             private void LoadHeightData()
             {
                 int offset;
                 byte dummy;
     
                 FileStream fs = new FileStream("heightmap.bmp", FileMode.Open, FileAccess.Read);
                 BinaryReader r = new BinaryReader(fs);
     
                 for (int i = 0; i < 10; i++)
                 {
                     dummy = r.ReadByte();
                 }
     
                 offset = r.ReadByte();
                 offset += r.ReadByte() * 256;
                 offset += r.ReadByte() * 256 * 256;
                 offset += r.ReadByte() * 256 * 256 * 256;
     
                 for (int i = 0; i < 4; i++)
                 {
                     dummy = r.ReadByte();
                 }
     
                 WIDTH = r.ReadByte();
                 WIDTH += r.ReadByte() * 256;
                 WIDTH += r.ReadByte() * 256 * 256;
                 WIDTH += r.ReadByte() * 256 * 256 * 256;
     
                 HEIGHT = r.ReadByte();
                 HEIGHT += r.ReadByte() * 256;
                 HEIGHT += r.ReadByte() * 256 * 256;
                 HEIGHT += r.ReadByte() * 256 * 256 * 256;
     
                 heightData = new int[WIDTH, HEIGHT];
                 for (int i = 0; i < (offset - 26); i++)
                 {
                     dummy = r.ReadByte();
                 }
     
                 for (int i = 0; i < HEIGHT; i++)
                 {
                     for (int y = 0; y < WIDTH; y++)
                     {
                         int height = (int)(r.ReadByte());
                         height += (int)(r.ReadByte());
                         height += (int)(r.ReadByte());
                         height /= 8;
                         heightData[WIDTH - 1 - y, HEIGHT - 1 - i] = height;
                     }
                 }
             }
     
             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 = "DirectX Tutorial";
             }
     
             static void Main()
             {
                 using (WinForm our_directx_form = new WinForm())
                 {
                     our_directx_form.LoadHeightData();
                     our_directx_form.InitializeDevice();
                     our_directx_form.CameraPositioning();
                     our_directx_form.VertexDeclaration();
                     our_directx_form.IndicesDeclaration();
                     our_directx_form.InitializeKeyboard();
                     our_directx_form.Show();
                     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)
    Opening a window
    Linking to the Device
    Drawing a triangle
    Camera
    Rotation - Translation
    Indices
    Terrain creation
    Terrain from file
    DirectInput
    Importing bmp files
    Colored vertices
    DirectX Light basics
    Mesh creation
    Mesh lighting
    Series 2: Flightsim (19)
    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!