A09.お絵かきツール
ユーザーが好きなように画面にポリゴンを描画します。
ポリゴンで描くお絵かきツールを作成します。
指定した3点(または2点)を結んでポリゴンを描画します。
また、右クリックを押しながらマウスを動かすことで、回転させることもできます。

画面設定
画面サイズを320*240に設定しておきます。(変更可能)
また、様々な場所でこの値を使用するので、defineで定義しておきます。
#define SCREENW 320
#define SCREENH 240
:
:
#ifdef WIN32
int WINAPI WinMain(HINSTANCE hInst,
HINSTANCE hPrevInst, LPSTR strCmdLine, int nShowCmd)
#else
int main()
#endif
{
MyEventReceiver Receiver;
IrrlichtDevice *device = createDevice(EDT_OPENGL,
dimension2d(SCREENW,SCREENH),16,false,false,false,&Receiver);
driver = device->getVideoDriver();
ISceneManager* smgr = device->getSceneManager();
device->setWindowCaption(L"Irrlicht");//ウインドウタイトル設定
//ライト
smgr->addLightSceneNode(0, vector3df(0,0,-1000), SColorf(0xFFFFFFFF), 1000.0f);
//カメラ用ターゲット
ISceneNode *TargetNode = smgr->addEmptySceneNode(0);
TargetNode->setPosition(vector3df(0,0,0));
//カメラ設定
ICameraSceneNode* camera;
camera = smgr->addCameraSceneNode(TargetNode,
vector3df(0,0,-SCREENW/2), vector3df(0,0,0));
//並行投影設定
{
matrix4 matrix;
matrix.buildProjectionMatrixOrthoLH(
SCREENW,SCREENH,-SCREENW/2,SCREENW);
camera->setProjectionMatrix(matrix);
}
//draw系初期化
drawInit(device, driver);
//リストスタート
listStart();
while(device->run())
{
driver->beginScene(true,true,0xFF6060FF);
TargetNode->setRotation(vector3df(rx,ry,0));
//シーン作成
makeScene(driver);
FillRect(driver,SCREENW/2-40,0,80,10, gColor[gNum]);
//シーンの描画
smgr->drawAll();
driver->endScene();
}
//リスト全消去
listDeleteAll();
driver->drop();
return 0;
}
|
変数/構造体定義
ポリゴンを作成するための変数などを定義しておきます。
構造体はリストで作成しておきます。
s32 gCNT = 0; //何個目の頂点か
s32 gID = 1; //ポリゴンのID
S3DVertex vertex[3]; //頂点情報
position2d mouse; //マウス位置
s32 gNum = 0; //カラー番号
bool gRect; //矩形かどうか
SColor gColor[8] = //カラー配列
{
0xFF000000, 0xFF0000FF,
0xFF00FF00, 0xFF00FFFF,
0xFFFF0000, 0xFFFF00FF,
0xFFFFFF00, 0xFFFFFFFF
};
bool cameraMove; //カメラを動かすかどうか
f32 rx = 0, ry = 0; //カメラの回転角度
//リスト用構造体
typedef struct _POLYGON
{
s32 id;
S3DVertex vertex[3];
struct _POLYGON *prev;
struct _POLYGON *next;
}POLYGON;
POLYGON *poly;
|
LIST操作用関数
ポリゴン情報を操作するLISTの関数を用意します。
LISTの初期化/追加/削除などの基本的なものを用意しておきます。
使い方は、以下の通りです。
1.listStartを呼び出して、初期化します。
2.頂点情報が確定したらlistAddを呼び出して追加します。
その際、listGetLastを呼び出して、必ず最後に追加するようにします。
3.追加した情報が間違っていたり、削除したい場合は、listDeleteLastを呼び出します。
4.プログラムを終了させる場合は、listDeleteAllを呼び出して全て削除します。
これは、mallocでメモリを割り当てているため、最後に必ずfreeを呼び出すためです。
//項目の初期化
POLYGON* listInit()
{
POLYGON* polygon;
polygon = (POLYGON*)malloc(sizeof(POLYGON));
memset(polygon, 0x00, sizeof(POLYGON));
return polygon;
}
//リスト開始
void listStart()
{
poly = listInit();
}
//リストの最後を取得
POLYGON* listGetLast()
{
POLYGON *p = poly;
while(p->next != NULL)
{
p = p->next;
}
return p;
}
//リストの最後に追加
void listAdd(s32 id, S3DVertex vertex[3])
{
POLYGON *last = listGetLast();
//項目作成/追加
POLYGON* newPoly = listInit();
newPoly->id = id;
newPoly->vertex[0] = vertex[0];
newPoly->vertex[1] = vertex[1];
newPoly->vertex[2] = vertex[2];
newPoly->prev = last;
newPoly->next = NULL;
last->next = newPoly;
}
//指定したリスト項目を削除
void listDelete(POLYGON *polygon)
{
POLYGON *prev = polygon->prev;
POLYGON *next = polygon->next;
//前後関係を調整
if(prev)prev->next = polygon->next;
if(next)next->prev = polygon->prev;
//解放
if(polygon != poly)
free(polygon);
}
//リストを全て削除
void listDeleteAll()
{
POLYGON *p = poly, *next;
while(p != NULL)
{
next = p->next;
free(p);
p = next;
}
poly = NULL;
}
//リストの最後を削除
void listDeleteLast()
{
POLYGON *last = listGetLast();
listDelete(last);
}
|
シーン作成
LISTに追加されている頂点情報の数だけ、三角形ポリゴンを描画します。
また、現在作成しようとしている図形に応じて、頂点を結ぶヘルプを表示しています。
四角形の場合
最初の頂点とマウスカーソルの位置を結ぶ矩形。
三角形の場合
頂点が1つなら、頂点とマウスカーソルの位置を結ぶ線。
頂点が2つなら、頂点同士を結ぶ線と、頂点からマウスカーソル位置を結ぶ線。
※座標を操作する際、画面の中央が(0,0)となってるので注意してください。
//シーン作成
void makeScene(IVideoDriver *driver)
{
//属性設定
SMaterial Material;
Material.Lighting = false; //ライト設定
Material.BackfaceCulling = false;
driver->setMaterial(Material);
//三角形作成
u16 triList[] = {0,1,2};
POLYGON *p = poly;
while(p != NULL)
{
p = p->next;
if(p)driver->drawIndexedTriangleList(p->vertex, 3, triList, 1);
}
//作成しようとしているのが矩形の場合
if(gRect)
{
if(gCNT == 1)
{
DrawRect(driver, vertex[0].Pos.X+SCREENW/2, SCREENH/2-vertex[0].Pos.Y,
mouse.X-vertex[0].Pos.X-SCREENW/2, mouse.Y-SCREENH/2+vertex[0].Pos.Y,
0xFFFFFFFF);
}
}
//作成しようとしているのが三角形の場合
else
{
//選択した点とカーソルを線でつなぐ
if(gCNT == 1)
{
DrawLine(driver, vertex[0].Pos.X+SCREENW/2, SCREENH/2-vertex[0].Pos.Y,
mouse.X, mouse.Y, 0xFFFFFFFF);
}
if(gCNT == 2)
{
DrawLine(driver, vertex[0].Pos.X+SCREENW/2, SCREENH/2-vertex[0].Pos.Y,
mouse.X, mouse.Y, 0xFFFFFFFF);
DrawLine(driver, vertex[1].Pos.X+SCREENW/2, SCREENH/2-vertex[1].Pos.Y,
mouse.X, mouse.Y, 0xFFFFFFFF);
DrawLine(driver, vertex[0].Pos.X+SCREENW/2, SCREENH/2-vertex[0].Pos.Y,
vertex[1].Pos.X+SCREENW/2, SCREENH/2-vertex[1].Pos.Y, 0xFFFFFFFF);
}
}
}
|
頂点作成/補間
クリックした座標を頂点情報にセットします。
四角形を作成する場合は、2つの頂点が決まったらLISTに追加します。
(自動的に、残り2つの頂点を作成し、2回追加を行います。)
三角形を作成する場合は、3つの頂点が決まったらLISTに追加します。
checkVertexは、近くに以前作成した図形の頂点があれば、その頂点を取得します。
//頂点を作成する
void makeVertex(S3DVertex &vertex, f32 x, f32 y, f32 z, SColor col)
{
//頂点情報セット
vertex.Pos.X = x;
vertex.Pos.Y = y;
vertex.Pos.Z = z;
vertex.Normal.X = 0.0f;
vertex.Normal.Y = 0.0f;
vertex.Normal.Z = -1.0f;
vertex.Color = col;
}
//頂点を設定する
void setVertex(f32 x, f32 y, f32 z, SColor col)
{
makeVertex(vertex[gCNT],x,y,z,col);
gCNT++;
//矩形の頂点をセットして追加
if(gRect && gCNT == 2)
{
makeVertex(vertex[2],vertex[1].Pos.X,vertex[0].Pos.Y,z,col);
listAdd(++gID, vertex);
makeVertex(vertex[2],vertex[0].Pos.X,vertex[1].Pos.Y,z,col);
listAdd(++gID, vertex);
gCNT = 0;
}
//3頂点が決まったらリストに追加
if(gCNT == 3)
{
listAdd(++gID, vertex);
gCNT = 0;
}
}
//近くに頂点があるかチェック
bool checkVertex(s32 mx, s32 my, S3DVertex &ver)
{
s32 i,x,y;
s32 range = 10;
POLYGON *p = poly;
while(p->next != NULL)
{
p = p->next;
for(i=0;i<3;i++)
{
x = p->vertex[i].Pos.X;
if(x-range < mx && mx < x+range)
{
y = p->vertex[i].Pos.Y;
if(y-range < my && my < y+range)
{
ver = p->vertex[i];
return true;
}
}
}
}
return false;
}
|
レシーバーの設定
キーボード操作
SHIFTキーを押している時は、矩形を作成するようにします。
ESC/DELETEキーが押されたら、現在作成中の頂点を削除します。
作成中の頂点がない場合は、最後に作成した図形を削除します。
マウス操作
左クリックした場所を頂点として扱います。
もし、近くに以前作成した図形の頂点が存在するなら、その頂点を使用するようにします。
右クリックを押しながらマウスを動かすと、カメラが動くようにします。
ホイールを上下に回すと、設定する頂点の色を変更できます。
//レシーバー設定
class MyEventReceiver : public IEventReceiver
{
public:
virtual bool OnEvent(const SEvent& event)
{
if(event.EventType == EET_KEY_INPUT_EVENT)
{
if(event.KeyInput.Shift)
{
gRect = true;
}
else gRect = false;
if(event.KeyInput.PressedDown)
{
switch(event.KeyInput.Key)
{
case KEY_ESCAPE:
case KEY_DELETE:
//描画途中ならラインを削除
if(gCNT > 0)
gCNT--;
//描画後ならポリゴンを削除
else
listDeleteLast();
//device->closeDevice();
return true;
default:
return false;
}
}
return true;
}
if(event.EventType == EET_MOUSE_INPUT_EVENT)
{
switch(event.MouseInput.Event)
{
case EMIE_LMOUSE_PRESSED_DOWN:
//近くに頂点があれば使用
if(bCheck)
setVertex(ver.Pos.X, ver.Pos.Y, 0, gColor[gNum]);
//なければ自由に設定
else
setVertex(event.MouseInput.X-SCREENW/2,
SCREENH/2-event.MouseInput.Y, 0, gColor[gNum]);
return true;
case EMIE_LMOUSE_LEFT_UP:
return true;
case EMIE_RMOUSE_PRESSED_DOWN:
cameraMove = true;
return true;
case EMIE_RMOUSE_LEFT_UP:
rx = 0;
ry = 0;
cameraMove = false;
return true;
case EMIE_MOUSE_WHEEL:
//上回転
if(event.MouseInput.Wheel == 1)
{
gNum++;
if(gNum == 8)
gNum = 0;
}
//下回転
else if(event.MouseInput.Wheel == -1)
{
gNum--;
if(gNum == -1)
gNum = 7;
}
return true;
case EMIE_MOUSE_MOVED:
mouse.X = event.MouseInput.X;
mouse.Y = event.MouseInput.Y;
//カメラの回転角度を設定
if(cameraMove)
{
rx = SCREENH/2-mouse.Y;
if(rx < -90.0f)rx = -90.0f;
if(rx > 90.0f)rx = 90.0f;
ry = -(mouse.X-SCREENW/2);
if(ry < -90.0f)ry = -90.0f;
if(ry > 90.0f)ry = 90.0f;
}
//頂点補完
bCheck = checkVertex(mouse.X-SCREENW/2, SCREENH/2-mouse.Y, ver);
return true;
default:
return true;
}
}
return false;
}
private:
S3DVertex ver;
bool bCheck;
};
|

メモ
現在は、XY座標を元に平面に図形を作成しています。
これにZ座標も組み込めば、立体的なポリゴンを作成可能です。
また、LIST構造で作成してあるので、LISTの数だけ頂点情報をファイルに出力すれば
Xファイルを作成することもできるでしょう。
その際の注意点として、隣り合ったポリゴン同士はなるべく順番で
出力するようにすると良いでしょう。
もし順番がバラバラだと、ポリゴンを1個ずつ描画しなければなりません。
しかし、順番になっていたら、drawIndexedTriangleListやdrawIndexedTriangleFanに
順番になっているデータを一気に代入して、一度に描画できる場合もあります。
結果として処理速度の向上にもつながります。
|
ダウンロード
今回作成したファイル一式です。
宿題
1.キーボードで特定のポリゴンを選択できるようにしましょう。
2.マウスで特定のポリゴンを選択できるようにしましょう。
3.指定したポリゴンを削除できるようにしましょう。
|
|