- 24ビットBMPファイルをテクスチャとして読み込む -
テクスチャ画像ファイル名はMTLファイルに書かれています。
今回はこの画像をOpenGLで読み込む方法です。なお、画像ファイルはBMP(24bit)形式で読み込みます。

ビットマップファイルはDIB形式といわれる画像ファイルです。
ファイルの構造は以下のようになっています。

今回使うビットマップファイルは24ビットのビットマップファイルなので、
RGBQUAD構造体は存在しません。


[ BITMAPを扱うときに使う構造体について ]

・BITMAPFILEHEADER構造体
ビットマップファイルの先頭にこの構造体の大きさのデータが格納されています。
定義は以下のようになっています。

typedef struct tagBITMAPFILEHEADER {
        WORD    bfType;
        DWORD   bfSize;
        WORD    bfReserved1;
        WORD    bfReserved2;
        DWORD   bfOffBits;
} BITMAPFILEHEADER, FAR *LPBITMAPFILEHEADER;

bfTypeは、"BM"の2文字を持ちます。
bfSizeは、ビットマップファイルのサイズです。
bfOffBitsは、ビットマップファイルの先頭からピクセルデータまでのオフセット値です。
bfReserved1とbfReserved2は使われていないメンバ変数なので常に0です。

・BITMAPINFOHEADER構造体
ビットマップファイルの情報を保持する構造体です。
定義は以下のようになっています。

typedef struct tagBITMAPINFOHEADER{
        DWORD      biSize;
        LONG       biWidth;
        LONG       biHeight;
        WORD       biPlanes;
        WORD       biBitCount;
        DWORD      biCompression;
        DWORD      biSizeImage;
        LONG       biXPelsPerMeter;
        LONG       biYPelsPerMeter;
        DWORD      biClrUsed;
        DWORD      biClrImportant;
} BITMAPINFOHEADER, FAR *LPBITMAPINFOHEADER


使用するメンバ変数がどんなデータを持つか示します。

biWidth:ビットマップファイルの横幅。
biHeight:ビットマップファイルの縦幅。
biBitCount:1ピクセルあたりの色数です。1, 4, 8, 24のどれかが入ります。
biClrUsed:イメージで使われる色数。この値によって、RGBQUAD構造体の数が決まる。
      biClrUsed==0 かつ biBitCountが1なら2個のRGBQUAD構造体が存在し、、
	   biBitCountが4なら16個、8なら256個のRGBQUAD構造体が存在する。
	   biClrUsedが0以外の時はその個数のRGBQUAD構造体が存在する。

今回扱うビットマップは24bitのフルカラービットマップですので、
biClrUsedは0、biBitCountは24が格納されており、RGBQUAD構造体は存在しません。



[ 構造体 ]
//BMP読み込み用:BMPのデータ
struct BMPFile
{
	HANDLE hMem;		//BMPファイルのピクセルデータのハンドル(GlobalFreeする時に必要)
	char * bmpPixelBuffer;	//BMPのピクセルデータを格納する配列
	int bmpWid;		//BMPファイルの横幅
	int bmpHei;		//BMPファイルの縦幅
};

[ 関数のソース ]
//BMPファイルを読み込む
BMPFile * LoadDIB(const char *filename)
{
    HANDLE hF;          //BMPファイルのハンドル
    HANDLE hMem_FileHeader;     //BMPファイル内のBITMAPFILEHEADERのハンドル
    HANDLE hMem_InfoHeader;     //BMPファイル内のBITMAPINFOHEADERのハンドル
    LPBITMAPFILEHEADER lpBf;    //BITMAPFILEHEADERへのポインタ。BITMAPFILEHEADERデータを保持する
    LPBITMAPINFOHEADER lpBi;    //BITMAPINFOHEADERへのポインタ。BITMAPINFOHEADERデータを保持
    WORD wBitCount;

    DWORD dwClrUsed;    //イメージで使われる色数
    DWORD dwOffBits;    //BMPファイルの先頭からピクセルデータまでのバイト数
    DWORD dwResult;     //ReadFile()で実際に読み込まれたバイト数
    DWORD dwSizeImage;  //ピクセルデータだけのバイト数

    char szFType[3];    //BMPファイルの先頭が"BM"になっているかの確認用配列
    BMPFile * bmpfile;  //returnするBMPFile構造体
    BOOL freeErr = FALSE;   //GlobalFree()が失敗したらTRUEを格納し、エラー処理へ行くためのフラグ

    hF = CreateFile(filename, GENERIC_READ , FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hF == INVALID_HANDLE_VALUE)
    {
       //エラー処理
    }
    
    //BITMAPFILEHEADERの分のメモリ領域を作成してロック
    hMem_FileHeader = GlobalAlloc(GHND, sizeof(BITMAPFILEHEADER));
    if(hMem_FileHeader == NULL)
    {
        //エラー処理
    }
    lpBf = (LPBITMAPFILEHEADER)GlobalLock(hMem_FileHeader);

    //BMPファイルの先頭のBITMAPFILEHEADER構造体を読み込む
    ReadFile(hF, (LPBITMAPFILEHEADER)lpBf, sizeof(BITMAPFILEHEADER), &dwResult, NULL);
    
    szFType[0] = LOBYTE(lpBf->bfType);  //"B"の文字
    szFType[1] = HIBYTE(lpBf->bfType);  //"M"の文字
    szFType[2] = '\0';          //文字列の終端を示すヌル文字
    dwOffBits = lpBf->bfOffBits;        //BMPファイルの先頭からピクセルデータの先頭までのバイト数

    //BITMAPINFOHEADERの分のメモリ領域を作成し、ロック
    hMem_InfoHeader = GlobalAlloc(GHND, sizeof(BITMAPINFOHEADER));
    if(hMem_InfoHeader == NULL)
    {
        //エラー処理
    }
    lpBi = (LPBITMAPINFOHEADER)GlobalLock(hMem_InfoHeader);

    //2番目に書かれている構造体であるBITMAPINFOHEADERを読み込む
    ReadFile(hF, (LPBITMAPINFOHEADER)lpBi, sizeof(BITMAPINFOHEADER), &dwResult, NULL);

    //BMPFile構造体の領域を作成し、先頭アドレスを取得
    bmpfile = (BMPFile *)GlobalAlloc(GMEM_FIXED, sizeof(BMPFile));
    if(bmpfile == NULL)
    {
        GlobalUnlock(hMem_FileHeader);
        if(GlobalFree(hMem_FileHeader))
        {
            freeErr = TRUE;
        }

        GlobalUnlock(hMem_InfoHeader);
        if(GlobalFree(hMem_InfoHeader))
        {
            freeErr = TRUE;
        }

        if(freeErr == TRUE)
        {
            //エラー処理
        }
    }

    bmpfile->bmpWid = lpBi->biWidth;            //BMPファイルの横幅
    bmpfile->bmpHei = lpBi->biHeight;           //BMPファイルの縦幅
    wBitCount = lpBi->biBitCount;   //何ビットのBMPファイルなのかが入ってる
    dwClrUsed = lpBi->biClrUsed;    //イメージで使われる色数
    dwSizeImage = lpBf->bfSize - lpBf->bfOffBits;   //ピクセルデータだけのバイト数

    //24ビットのBMPかどうかを確認。これ以外は読まない
    if (dwClrUsed != 0 || wBitCount != 24 || strcmp(szFType, "BM") != 0)
    {
        errMsg = filename;
        //エラー処理
    }

    //ピクセルデータを格納するメモリ領域を作成してロック
    bmpfile->hMem = GlobalAlloc(GHND, dwSizeImage);
    bmpfile->bmpPixelBuffer = (char *)GlobalLock(bmpfile->hMem);

    //hFはここでピクセルデータの先頭を指しているから読み込む
    ReadFile(hF, bmpfile->bmpPixelBuffer, dwSizeImage, &dwResult, NULL);

    GlobalUnlock(bmpfile->hMem);

    GlobalUnlock(hMem_FileHeader);
    if(GlobalFree(hMem_FileHeader))
    {
        freeErr = TRUE;
    }

    GlobalUnlock(hMem_InfoHeader);
    if(GlobalFree(hMem_InfoHeader))
    {
        freeErr = TRUE;
    }

    //GlobalFreeのどれかが失敗した場合はエラー処理へいく
    if(freeErr == TRUE)
    {
        //エラー処理
    }

    CloseHandle(hF);

    return bmpfile;
}

・CreateFile関数
この関数でビットマップファイルにアクセスしています。

CreateFile(filename, GENERIC_READ , FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

第1引数:filename
ビットマップファイル名を指定します。

第2引数:GENERIC_READ
ファイルを読み込む時はこの値を指定します。
書き込むときはGENERIC_WRITEを指定します。どちらを指定してもファイルポインタの移動可能です。

第3引数:FILE_SHARE_READ
このファイルを共有できるかどうかの指定です。
つまり、複数のアプリや1つのアプリで同じファイルへのアクセスが可能かどうかの指定です。
FILE_SHARE_READを指定すると、1つのアプリケーションで同じファイルを何度も読むことができます。
0にすると共有できないようになります。
この指定はBrightObjectでファイルを保存し、同じTLOやOJPを読んだときにテクスチャファイルが開かれないのを回避するためです。

第5引数:OPEN_EXISTING
ファイルの作成モードを指定します。

OPEN_EXISTINGを指定すると既に存在するファイルを開きます。ファイルが無いとエラーになります。
他には以下のような値も指定できます。

CREATE_NEW:新規ファイルを作成。既に同じ名前のファイルがある場合はエラー。
CREATE_ALWAYS:新規ファイルを作成。既に同じ名前のファイルがある場合は上書き。
OPEN_ALWAYS:ファイルを開く。ファイルが無い場合は新しくファイルを作成する。
TRUNCATE_EXISTING:ファイルを開き、0バイトにする。ファイルが無い場合はエラー。

第6引数:FILE_ATTRIBUTE_NORMAL
ファイル属性を指定します。FILE_ATTRIBUTE_NORMALはファイル属性無し。
他には、FILE_ATTRIBUTE_READONLYで読み取り専用などがあります。

NULLの箇所は基本的にNULLを指定すればOKです。


・GlobalAlloc関数
WindowsAPIでメモリ領域を動的に取得する関数です。

GlobalAlloc(GHND, sizeof(BITMAPFILEHEADER));

第1引数は決まったフラグを与えます。上の場合はメモリ領域を取得した後、その領域を0で初期化します。
ちなみにC言語のmallocと同じように使うには第1引数にGMEM_FIXEDを指定します。
これを指定すると確保したメモリ領域のアドレスが移動しないようになるので、結果としてmallocと同じ動作になります。
第2引数は確保したいメモリサイズを指定します。
使用したらGlobalFree関数で領域を開放します。

・GlobalLock関数
GlobalAlloc関数で確保したメモリ領域のアドレスが移動しないようにアドレスを固定するための関数です。
GMEM_FIXEDを指定しない場合で、メモリ領域が移動しては困る場合に使用します。
引数にはGlobalAlloc関数の戻り値を指定します。
アドレスの固定を解除するにはGlobalUnlock関数を使います。

・ReadFile関数
ファイルからデータを読み込みます。

ReadFile(hF, bmpfile->bmpPixelBuffer, dwSizeImage, &dwResult, NULL);

引数は先頭から、ファイルへのポインタ、読み取ったデータを格納するバッファの先頭アドレス、
ファイルから読み取るバイト数、読み取ったバイト数を格納する変数のアドレス。最後はNULLでOKです。
ファイルを使い終わったらCloseHandle関数にファイルポインタを指定してファイルを閉じます。


最初に説明したBITMAPFILEHEADER、BITMAPINFOHEADERと
上で説明した関数がわかってくれば、上記の関数のソースでやってることも大体見えてくると思います。

ざっくり説明しておきますと、
CreateFileでビットマップファイルを開き、BITMAPFILEHEADERの1つ分のメモリ領域を確保してロック。
確保した領域にビットマップファイルからファイルヘッダ部分を読み込み、"BM"の文字と
ビットマップファイル先頭からピクセルデータまでのオフセット値を取得します。

BITMAPINFOHEADER1つ分のメモリ領域を確保してロックしたあと、ビットマップファイル情報を取得。
BMPFile構造体1つ分のメモリ領域を確保してビットマップの横幅・縦幅・ピクセルあたりのビット数・
イメージで使われる色数(24bitなので0)ビットマップファイル内のピクセルデータだけのバイト数を取得します。

24ビットのビットマップファイルであることを判定し、ピクセルデータを格納するメモリ領域を確保してロック。
ビットマップファイルのピクセルデータの読み込みは、

ReadFile(hF, bmpfile->bmpPixelBuffer, dwSizeImage, &dwResult, NULL);

のように行ってます。
BITMAPFILEHEADER、BITMAPINFOHEADERを先にReadFile関数で読み込んでいるので、
ファイルポインタ(hF)はピクセルデータの先頭をさしています(RGBQUADも無いので)。

最後に必要のなくなったメモリ領域を開放して終了しています。
結果としては、BMPFile構造体にピクセルデータ、ビットマップファイルの横幅と縦幅を取得できます。

取得したピクセルデータはglTexImage2D関数の最後の引数に与えます。
具体的には以下のように使います。

//テクスチャ情報とピクセルデータ配列の指定
glTexImage2D(
		GL_TEXTURE_2D,		//2次元テクスチャであることを示す
		0,			//複数の解像度のテクスチャマップを使う場合はここを指定する。1解像度の画像であれば0を指定。
		3, 			//テクスチャ画像に使われている成分(RGBA)。RGBの場合は3を指定する。
		tloData->materials[ tloData->TData[i]->useMtlIndex ]->bmpData->bmpWid,	//画像ファイルの横幅
		tloData->materials[ tloData->TData[i]->useMtlIndex ]->bmpData->bmpHei,	//画像ファイルの縦幅
		0,				//境界の幅を示す。0(幅なし)か1を指定する。
		GL_BGR_EXT,			//画像ファイルのピクセルデータのフォーマット(並び順)を指定。BGRの順に並んでいる。
		GL_UNSIGNED_BYTE, 		//画像ファイルのピクセルデータのデータ型を指定
		tloData->materials[ tloData->TData[i]->useMtlIndex ]->bmpData->bmpPixelBuffer	//画像ファイルのピクセルデータを指定
            );


inserted by FC2 system