Home | Lesson | Game | Tool | Link

14.2D基本図形

線や矩形・画像といった基本的な図形を描画します。
darw2DImageなどは引数の設定の仕方が大変なので、新しく簡単な関数にしています。


定義
三角関数を使用しているのでmath.hをインクルードしておきます。
矩形の塗りつぶしパターンとして、PS_SOLID〜PS_USERSTYLEを定義します。
最初のdefineは、関数の引数で使用します。
次のchara型は、画像のファイル名として使用します。
DrawImageを使用する際に、引数を全て入力しなくても済むように宣言しておきます。
#include <math.h>

#define PI 3.141592654
float rad = (float)(PI / 180);	//radian

#define PS_SOLID            0       // 全て塗りつぶし
#define PS_DASH             1       // -------  
#define PS_DOT              2       // .......  
#define PS_DASHDOT          3       // _._._._  
#define PS_DASHDOTDOT       4       // _.._.._  
#define PS_NULL             5       // 網目
#define PS_INSIDEFRAME      6       // 枠を描画
#define PS_USERSTYLE        7       // 特殊横縞 

char *names[] = {"PS_SOLID","PS_DASH","PS_DOT","PS_DASHDOT",
		"PS_DAHDOTDOT",	"PS_NULL", "PS_INSIDEFRAME", "PS_USERSTYLE"};

void DrawImage(
	IVideoDriver *driver, char* filename, int x, int y, int w, int h, int u, int v, 
	bool bColorKey = false, SColor colLU=0xFFFFFFFF, 
	SColor colLD=0, SColor colRD=0, SColor colRU=0, float sx=1.0f, float sy=1.0f);

ドットパターン作成
矩形の塗りつぶしようにドットパターンを作成します。
PS_SOLID〜PS_USERSTYLEに対応させた画像を作成していきます。
typesのbitが立っている場所が塗りつぶす場所となるように定義してあります。

作成方法としては、以下のようになります。
1.画像データを入れる配列を用意します。
2.全て0xFFで初期化します。(真っ白画像)
3.塗りつぶさない場所のアルファを0x00で書き換えます。
4.createImageFromDataでイメージデータを作成します。
 この時、画像サイズを指定して作成しているので注意してください。
5.addTextureでテクスチャとして登録します。
//ドットパターン作成(Rectの下地で使用)
void makePattern(IVideoDriver *driver)
{
	unsigned short types[] = {
		0xFFFF, //PS_SOLID       全て塗りつぶし サイズ:16*1
		0xFCFC, //PS_DASH        -------        サイズ:16*1
		0x6666, //PS_DOT         .......        サイズ:16*1
		0xFFF6, //PS_DASHDOT     _._._._        サイズ:16*1
		0xFFB6, //PAS_DASHDOTDOT _.._.._        サイズ:16*1
		0xFFFF, //PS_NULL        網目           サイズ: 2*2
		0xFFFF, //PS_INSIDEFRAME 枠を描画
		0xFFFF  //PS_USERSTYLE   特殊横縞       サイズ: 1*8 
	};

	IImage *image;
	char data[16*1*4];//RGBA
	int num;

	for(num=0;num<8;num++){
		memset(data,0xFF,sizeof(data));

		if((types[num] & 0x8000) == 0)data[ 3]=0;
		if((types[num] & 0x4000) == 0)data[ 7]=0;
		if((types[num] & 0x2000) == 0)data[11]=0;
		if((types[num] & 0x1000) == 0)data[15]=0;
		if((types[num] & 0x0800) == 0)data[19]=0;
		if((types[num] & 0x0400) == 0)data[23]=0;
		if((types[num] & 0x0200) == 0)data[27]=0;
		if((types[num] & 0x0100) == 0)data[31]=0;
		if((types[num] & 0x0080) == 0)data[35]=0;
		if((types[num] & 0x0040) == 0)data[39]=0;
		if((types[num] & 0x0020) == 0)data[43]=0;
		if((types[num] & 0x0010) == 0)data[47]=0;
		if((types[num] & 0x0008) == 0)data[51]=0;
		if((types[num] & 0x0004) == 0)data[55]=0;
		if((types[num] & 0x0002) == 0)data[59]=0;
		if((types[num] & 0x0001) == 0)data[63]=0;

		//PS_NULL:網目
		if(num == 5)
		{
			data[ 7]=(char)0x00;
			data[11]=(char)0x00;
			image = driver->createImageFromData(
				ECF_A8R8G8B8,dimension2d<s32>(2,2),data);
		}
		//PS_USERSTYLE:横縞
		else if(num == 7)
		{
			data[19]=(char)0x80;
			data[23]=(char)0x80;
			data[27]=(char)0x80;
			data[31]=(char)0x80;
			image = driver->createImageFromData(
				ECF_A8R8G8B8,dimension2d<s32>(1,8),data);
		}
		else image = driver->createImageFromData(
			driver->addTexture(names[num], image);
	}
}

画像描画
2D画像を描画します。
予め引数を指定して宣言しているので、呼び出し時はbColorKey以降は省略可能です。
カラーキー設定は、色を指定する方法と場所を指定する方法の2つがあるので
2つを記述してあります。実際は、色指定の方を使用しています。
ただし、描画の度にカラーキー指定を呼び出していると処理が重くなるので、
注意が必要です。(事前に読み込んでカラーキーを設定する関数を用意するとよいでしょう。)
最後に、各引数をもとにdraw2DImageを呼び出しています。

filename                画像ファイル名を指定します。
x,y                     画像の表示位置を指定します。左上を基準に描画します。
w,h                     画像の大きさを指定します。
u,v                     画像の読み込み位置を指定します。
bColorKey               カラーキーを使うかどうか。trueで使用します。
colLU,colLD,colRD,colRU 頂点カラーです。左から左上/左下/右下/右上になっています。
sx,sy                   画像の拡大縮小率を指定します。1.0が1倍です。
//画像描画
void DrawImage(
	IVideoDriver *driver, char* filename, int x, int y, int w, int h, int u, int v, 
	bool bColorKey, SColor colLU, SColor colLD, SColor colRD, SColor colRU,
	float sx, float sy)
{
	ITexture* texture = driver->getTexture(filename);
	SColor colors[] = {colLU,colLD,colRD,colRU};//左上:左下:右下:右上
	if(colLD == 0)
		colors[1] = colors[2] = colors[3] = colLU;
	if(bColorKey)
	{
		//カラーキー(色指定)
		driver->makeColorKeyTexture(texture, 0x00000000);
		//カラーキー(場所指定)
		//driver->makeColorKeyTexture(texture, position2d<s32>(0,0));
	}
	driver->draw2DImage(texture, 
		rect<s32>(x,y,x+w*sx,y+h*sy), 
		rect<s32>(u,v,u+w,v+h), 
		0, colors, true);
}

線描画
draw2DLineを呼び出して2点間を結ぶ線を描画します。
x1,y1 頂点1の座標を指定します。
x2,y2 頂点2の座標を指定します。
col   線の色を指定します。
//線描画
void DrawLine(IVideoDriver *driver, int x1, int y1, int x2, int y2, SColor col)
{
	driver->draw2DLine(position2d<s32>(x1, y1), position2d<s32>(x2, y2), col);
}

矩形描画(枠描画)
DrawLineを使用して、矩形の枠を描画します。
Irrlichtにはdarw2DPolygon関数がありますが、以下の問題点があります。
・矩形を描画するとひし形になってしまう。
・縦/横の長さを個別に指定できない。
・描画開始位置と終了位置に微妙に隙間ができる場合がある。
よって、全てDrawLineで描画しています。
x,y 矩形の描画位置を指定します。左上を基準に描画します。
w,h 矩形の大きさを指定します。
col 矩形の線の色を指定します。
//矩形描画
void DrawRect(IVideoDriver *driver, int x, int y, int w, int h, SColor col)
{
	DrawLine(driver,x,  y,  x+w,y    ,col);//上
	DrawLine(driver,x,  y+h,x+w,y+h  ,col);//下
	DrawLine(driver,x,  y,  x,  y+h  ,col);//左
	DrawLine(driver,x+w,y,  x+w,y+h+1,col);//右
}

矩形描画(単色塗りつぶし)
矩形を指定した1色で塗りつぶします。
また、塗りつぶしパターンを指定することもできます。
Irrlichtにはdarw2DRectangleという関数がありますが、
こちらは内部でテクスチャを使用した方法になっています。
typeにはPS_SOLIDなどを指定します。
x,y  矩形の描画位置を指定します。左上を基準に描画します。
w,h  矩形の大きさを指定します。
col  矩形を塗りつぶす色を指定します。
type 矩形を塗りつぶすパターンを指定します。パターンには以下を指定可能です。
     PS_SOLID       全て塗りつぶし
     PS_DASH        -------
     PS_DOT         .......
     PS_DASHDOT     _._._._ 
     PS_DASHDOTDOT  _.._.._
     PS_NULL        網目
     PS_INSIDEFRAME 枠を描画(DrawRectを呼び出し)
     PS_USERSTYLE   特殊横縞
各パターンで塗りつぶした結果がページ上の画像になります。
//矩形描画(単色塗りつぶし)
void FillRect(IVideoDriver *driver, int x, int y, int w, int h, SColor col, int type)
{
	if(type > 7)
		type = 0;
	if(type == PS_INSIDEFRAME)
		DrawRect(driver,x,y,w,h,col);
	else DrawImage(driver, names[type], x,y,w,h,0,0,false,col);
}

矩形描画(複数色塗りつぶし)
矩形を指定した4色で塗りつぶします。
また、塗りつぶしパターンを指定することもできます。
Irrlichtにはdarw2DRectangleという関数がありますが、
こちらは内部でテクスチャを使用した方法になっています。
typeにはPS_SOLIDなどを指定します。
x,y  矩形の描画位置を指定します。左上を基準に描画します。
w,h  矩形の大きさを指定します。
col  矩形を塗りつぶす色を指定します。
type 矩形を塗りつぶすパターンを指定します。パターンには以下を指定可能です。
     PS_SOLID       全て塗りつぶし
     PS_DASH        -------
     PS_DOT         .......
     PS_DASHDOT     _._._._ 
     PS_DASHDOTDOT  _.._.._
     PS_NULL        網目
     PS_INSIDEFRAME 枠を描画(DrawRectを呼び出し)
     PS_USERSTYLE   特殊横縞
各パターンで塗りつぶした結果がページ上の画像になります。
※PS_INSIDEFRAMEを指定した場合、colLU単色での塗りつぶしになります。
//矩形描画(複数色塗りつぶし)
void FillRect2(IVideoDriver *driver, int x, int y, int w, int h, 
	SColor colLU, SColor colLD, SColor colRD, SColor colRU, int type)
{
	if(type > 7)
		type = 0;
	if(type == PS_INSIDEFRAME)
		DrawRect(driver,x,y,w,h,colLD);
	else DrawImage(driver, names[type], x,y,w,h,0,0,false,colLU,colLD,colRD,colRU);
}

円描画(枠描画)
円(ポリゴン)の枠を描画します。
※開始点終了点を結ぶ線がないようなので、後で改造予定です。
cx,cy  中心座標を指定します。中心座標を基準に描画します。
radius 半径を指定します。
col    枠の色を指定します。
count  角の数を指定します。(3以上で指定してください。)
//円描画
void DrawOval(IVideoDriver *driver, int cx, int cy, float radius, SColor col, int count)
{
	if(count < 3)
		count = 3;
	driver->draw2DPolygon(position2d<s32>(cx,cy), radius, col, count);
}

円描画(塗りつぶし)
円(ポリゴン)を指定した色で塗りつぶします。
三角形ポリゴンを作成し、円になるように配置していきます。
内部では、円の縦半径と横半径を別々に扱っているので、楕円も描画可能です。
cx,cy  中心座標を指定します。中心を基準に描画します。
radius 半径を指定します。
col    塗りつぶす色を指定します。
count  角の数を指定します。(3以上72以下で指定してください。)
//円描画(塗りつぶし)
void FillOval(IVideoDriver *driver, int cx, int cy, float radius, SColor col, int count)
{
	matrix4 world, pos, Ortho;

	dimension2d<s32> screen = driver->getScreenSize();

	//属性設定
	SMaterial Material;
	Material.setFlag(EMF_LIGHTING,false);	//ライト設定
	Material.MaterialType = EMT_TRANSPARENT_ALPHA_CHANNEL;
	driver->setMaterial(Material);

	//三角形作成
	u16 point    = count;	//インデックス数(角の数)
	if(point < 3)point = 3;
	if(point > 72)point = 72;
	u16 angle    = 360/point;	//角度の最低は5(配列72個までにしたので)
	u16 radiusx  = radius;	//半径X
	u16 radiusy  = radius;	//半径Y
	SColor color = col;	//色(アルファは0x81以上のみ有効、それ以外は透明)

	u16 i;
	float x,y;
	u16 triList[72*3];
	S3DVertex triVer[72+1];

	memset(triList,0x00,72*3*sizeof(u16));
	triVer[0] = S3DVertex(0,0,0, 0,0,0, color, 0,0);//中心
	
	//頂点を作成する
	for(i=1;i<=point;i++)
	{
		x = (cos(i*angle*rad) * radiusx);
		y = (sin(i*angle*rad) * radiusy);
		triVer[i] = S3DVertex(x,y,0,  0,0,0, color, 0,0);

		triList[i*3-2] = i+1;
		triList[i*3-1] = i;
	}
	//最後だけリスト調整
	triList[i*3-5] = 1;
	triList[i*3-4] = i-1;


	//位置設定(中心を基準に設定)
	float px = ((cx+0/2))/((double)screen.Width/2) - 1.0;
	float py = 1.0 - ((cy+0/2))/((double)screen.Height/2);
	pos.setTranslation(vector3df(px,py,0));//Z座標での位置指定はダメ

	//スクリーン設定
	//初期値以外を書き換え(W,0,0,0,0,H,0,0,0,0,1,0,0,0,0,1)
	Ortho(0,0) = (double)2/(double)screen.Width;
	Ortho(1,1) = (double)2/(double)screen.Height;

	//ワールドに反映
	driver->setTransform(ETS_VIEW, world);
	driver->setTransform(ETS_PROJECTION, world);
	world = pos*Ortho;
	driver->setTransform(ETS_WORLD, world);

	//描画
	driver->drawIndexedTriangleList(triVer, point+1, triList, point);
}

描画
上で説明した関数を利用して基本図形を描画します。
void makeScene(IVideoDriver *driver)
{
	DrawImage(driver,"icon.bmp",4,120,32,32,64,0,true,0x80FFFFFF,0,0,0,2,2);
	DrawLine(driver,   10, 10,310,180,0xFFFF0000);
	DrawRect(driver,   10,100, 50, 50,0xFFFFFF00);
	FillRect(driver,   20, 20,100,100,0xA00000FF, PS_SOLID);
	FillRect2(driver, 200, 20,100,100,
		0xFFFF0000, 0xFF00FF00, 0xFF0000FF, 0xFFFFFF00, PS_SOLID);
	FillRect2(driver, 110,100,100,100,
		0xFFFFFFFF, 0xFF000000, 0xFF00FFFF, 0xFFFF00FF, PS_USERSTYLE);
	DrawOval(driver,  160, 80, 50,0xFFFFFFFF,30);
	FillOval(driver,  250,120, 50,0x90FF0000,30);

	FillRect(driver,  0,200,40,40,0xFF000000,PS_SOLID);
	FillRect(driver, 40,200,40,40,0xFF222222,PS_DASH);
	FillRect(driver, 80,200,40,40,0xFF444444,PS_DOT);
	FillRect(driver,120,200,40,40,0xFF666666,PS_DASHDOT);
	FillRect(driver,160,200,40,40,0xFF888888,PS_DASHDOTDOT);
	FillRect(driver,200,200,40,40,0xFFAAAAAA,PS_NULL);
	FillRect(driver,240,200,40,40,0xFFCCCCCC,PS_INSIDEFRAME);
	FillRect(driver,280,200,40,40,0xFFEEEEEE,PS_USERSTYLE);
}

パターン作成関数の呼び出し
メインループの前に、矩形で使用するパターンを作成しておきます。
	device->setWindowCaption(L"Irrlicht");//ウインドウタイトル設定

	makePattern(driver);

	while(device->run())
	{

ダウンロード
今回作成したファイル一式です。

宿題
1.FillOvalを改造して楕円を描画できるようにしてみましょう。
2.DrawImageを改造して画像を回転できるようにしてみましょう。
3.星(☆★)を描画する関数(DrawStar/FillStar)を作って見ましょう。