nakayama.

平成 21 年度卒業論文
ルービックキューブ・アプリケーションにおける
マウス操作機能の追加
中山 孝昭
奈良産業大学情報学部
0
1.はじめに
福知・若野(2008)は、平成 20 年度卒業研究において、ルービックキューブを3DC
Gで描画し、それをキーボードで操作するアプリケーションを開発した。このアプリケー
ションでは、ルービックキューブの回転対象の選択およびその回転方向の指定をすべて、
ユーザーによるキー操作で実現している。図1のように、回転対象の選択はテンキーによ
って行なう。このとき、選択されたキューブはハイライトによって明るく表示され、どの
キューブを回転させることができるのかを視覚的にとらえやすい工夫がなされている。ま
た、回転方向の指定は矢印キーで行なう(図2)
。ここでは、矢印キーの矢印の向きと回転
方向を一致させることで、ユーザーに違和感を感じさせないようにしている。しかし、こ
れらのキー操作は、実際のルービックキューブを手で動かすような直観的な操作からはほ
ど遠い。そこで、より直観的な操作に近づけるため、マウスを使ってルービックキューブ
CGを操作する機能を実装した。
【全体】
5
【縦の列】
6
6
4
6
4
4
4
6
【横の列】
8
8
2
8
2
2
2
8
図1 ルービックキューブCGのキー操作:回転対象の選択
1
【全体】
↑
←
→
↓
【縦の列】
↓
↑
←
→
【横の列】
図2 ルービックキューブCGのキー操作:回転方向の指定
2
マウスを使ってルービックキューブCGを操作する場合、(1)ルービックキューブ全体を
回転させる場合と(2)ルービックキューブの一部を回転させる場合で処理すべき事柄が異な
ってくる。本研究で開発したアプリケーションでは、マウスの右ボタンによる操作で前者(1)
を、左ボタンによる操作で後者(2)を実行するようにした。(1)および(2)に関するプログラミ
ングを行なう際に処理すべき事柄について、ぞれぞれ、第二章および第三章でまとめる。
本研究では、アプリケーション開発を行なう環境として Visual Studio 2008 のプログラミ
ング言語 Visual C++を使用し、3DCGの描画ツールとして DirectX SDK (August 2006)
を使用した。また、ルービックキューブの3DCG制作に O.Mizuno 氏が開発した3Dポ
リゴンモデラーMetasequoia LE(http://www.metaseq.net/metaseq/index.html)を使用し
た。本論文末尾には、本研究で作成したアプリケーションのソースコードを掲載した。
2.ルービックキューブ全体の回転
ルービックキューブ全体を回転させる場合、マウスの右ボタンを押しながら、画面上を
ドラッグする。このとき、回転軸と回転方向をマウスの動きから読み取る必要がある。
ルービックキューブの3DCGを描く仮想的な三次元空間に、図3(a)のような座標系を
設定する。この座標系では、ルービックキューブの中心に原点を置き、右側面に直交する
方向にX軸、上側面に直交する方向にY軸、正面に直交する方向にZ軸を配置する。また、
3DCGが描かれる二次元空間の直交座標系を図3(b)のように設定する。
Y
h
(b)
(a)
X
Z
v
図3 3DCGを描く座標系
(a) 仮想空間における三次元座標系。原点はルービックキューブの中心に設定する。
(b) ウィンドウ上の二次元座標系。原点は描画領域左上角に設定する。
3
「ルービックキューブを転がす」イメージでルービックキューブを回転させるためには、
まず、ルービックキューブの面を指先で押さえ(マウスの右ボタンを押し)
、回転させる方
向へ指を動かす(マウスをドラッグする)ことができればよい。この機能をアプリケーシ
ョンに実装する場合、(1)マウスの右ボタンを押したとき、マウスポインタがルービックキ
ューブのどの面上に位置するのか、(2)マウスのドラッグから、回転方向をどのように決定
するのか、が問題となる。以下、これら2点についての解決方法を述べる。
2-1.ルービックキューブの各面上におけるマウスポインタの存在判定
3DCGで描かれたルービックキューブは、正面、右側面および上面の3面がユーザー
側に見えている(図3)
。ウィンドウ内の描画領域では、これらの面は四角形で近似できる。
各面の四角形の内部にマウスポインタが存在するのかを面ごとに判定すれば、いずれの面
上にマウスポインタが位置しているのか、または、ルービックキューブCGの外側にマウ
スポインタが存在するのかを調べることができる。
描画領域に描かれた任意の四角形を考える。四角形の各頂点の座標は、左上角から時計
回りに点 P1(h1, v1),点 P2(h2, v2),点 P3(h3, v3)および点 P4(h4, v4)とする。また、
マウスポインタの位置を点M(hM, vM)とする(図4)
。点Mから四角形の各頂点 P1,P2,
P3 および P4 へのベクトルは次式で表される。
①
隣接するベクトル
と
のなす角をθk とする(k=1, 2, 3, 4;ただし、k=4 の場合、
k+1 は「5」ではなく「1」とみなす)。この角θk は、①式のベクトルを用いて次式で求め
ることができる。
②
4つの角の和を求めたとき、マウスポインタが四角形の内部にある場合(図4(a))と外
部にある場合(図4(b))で計算結果が異なり、前者での角の和は2π[radian](360°)と
なり、後者での角の和は0[radian](0°)となる。そのため、②式で表された4つの角の
和を求め、その値が2πとなる場合、マウスポインタが四角形の内部に存在することが分
かる。
最終的に、描画領域に描かれたルービックキューブの3つの面に対して、順次、その面
(四角形)の内部にマウスポインタが存在するか調べることで、マウスポインタが位置す
る面を特定することができる。
4
P1
(a)
(b)
P2
P1
P2
θ1
P4
θ1
θ2
θ4
M
θ3
M
θ4
P4
P3
θ3
θ2
P3
図4 四角形内部におけるマウスポインタの存在判定
点Mはマウスポインタ、点 P1,P2,P3 および P4 は四角形の頂点を示す。隣接するベ
クトル
と
のなす角θk は、時計回りと正値とする。このとき、(a)マウスポイ
ンタが四角形の内部に存在する場合は、4つの角の和は2π[radian]となる。(b)マウ
スポインタが四角形の外部に存在する場合は、4つ角の和は0[radian]となる。
2-2.ルービックキューブの回転方向の決定
ルービックキューブの回転方向は、マウスのドラッグによって指定する。ドラッグ操作
では、ボタンを押した地点(hM1, vM1)とボタンを離した地点(hM2, vM2)の2つの座標が
得られるため、ドラッグの方向はベクトル (hM2-hM1, vM2- vM1)として求められる。
ルービックキューブCGは斜め上からの視点で描かれており、正面、右側面および上面
の各面はゆがんだ四角形となっている。そこで、次の方法で、ベクトル の方向と回転方向
を感覚的に一致させることにする。
まず、各面の四角形を対角線で区切り、4領域に区分けする(図5(a))。次に、対角線
の交点をドラッグの開始地点(hM1, vM1)に一致させるように、4領域の区分を平行移動さ
せる。最終的に、ドラッグの終了地点(hM2, vM2)が4領域のいずれに位置するのかを調べ
ることによって、回転方向を決定する(図5(b))。実際には、ベクトル とh軸のなす角、
および、4領域を区分けする対角線とh軸のなす角を求め、それらの角をもとに、いずれ
の領域にドラッグの終端が位置するのかを調べることになる。
以上の処理によって、ルービックキューブの回転軸および回転方向を決めることができ
る。その結果を用いて3DCGの回転軸と回転角を設定することで、ルービックキューブ
CG全体の回転を描く。
5
(a)
(b)
X 軸,時計回り
Y 軸,
時計回り
Y 軸,
反時計回り
【正面】
X 軸,反時計回り
Z 軸,反時計回り
Y 軸,
反時計回り
【右側面】
Y 軸,
時計回り
Z 軸,時計回り
X 軸,時計回り
Z 軸,
反時計回り
X 軸,反時計回り
Z 軸,
時計回り
【上面】
図5 回転方向の決定
(a) 回転方向を決定するため、対角線を用いて各面を4領域に区分する。
(b) 上記の4領域の区分を平行移動させ、ドラッグ始点(青丸)を中心とする領域分
布を考える。このとき、ドラッグ終端がどの領域内に位置するかによって、回転軸お
よび回転方向を図中の文字のように決定する。
6
3.ルービックキューブのキューブ列の回転
ルービックキューブの一部の列のみを回転させる場合、マウスの左ボタンを押しながら、
画面上をドラッグする。このとき、マウスの動きから読み取るべき情報は、回転軸と回転
角のほかに、回転させるキューブの組み合わせ(キューブ列)である。
3-1.キューブ各面の指定方法
ルービックキューブは計26個のキューブから構成されており、外部から見ることがで
きるキューブの側面は計54面である。そのうち、今回開発したアプリケーションで表示
される面は、正面の9面、右側面の9面、上面の9面となる。それらの面をプログラム内
で区別するため、図6のように表示されている各面に0から26までの番号を割り当てた。
各面の4つの頂点にも0~47の番号を用意した。
図6 各面と各頂点に割り当てた番号
3-2.キューブ列に含まれるキューブの特定
キューブは正面の9つ、右側面の9つ、上面の9つと計27面がユーザー側に見えてい
る。各面の内部にマウスポインタが存在するか否かをすべての面に対して判定すれば、い
ずれの面上にマウスポインタが位置しているのかを調べることができる。
判定方法は2-1章に記述した求め方とほぼ同じだが、ここでは0~26番の計27面
に対して個々にマウスポインタが内部に存在するか否かを調べる。この手順によって、面
の内部にマウスポインタが存在することがわかれば、その面を特定することができる。ま
た、いずれの面にもマウスポインタが存在しない場合、ルービックキューブCGの外側に
マウスポインタが存在していることが分かる。
図7 2番の面にマウスポインタが存在している状態
7
3-3.キューブ列の決定
ルービックキューブ全体を回転させる場合とは異なり、回転対象はドラックの開始地点
および終了地点の位置情報を用いて決定しなければならない。回転方向は、全体の回転と
同様にドラック方向で決定できる。ドラッグ開始地点が同じであっても、終了地点によっ
て回転対象が異なることに注意しなければならない。回転対象の決定手順は次の通りであ
る。まず、各面の四角形を対角線で区切り、4領域に区分けする。次に、対角線の交点を
ドラッグの開始地点に一致させるように、4領域の区分を平行移動させる。最終的に、ド
ラッグの終了地点が4領域のいずれに位置するのかを調べることによって、回転対象を決
定する。例えば、図8でドラックの終了地点が緑か青の領域ならば、回転対象は図8(a)の
ように横のキューブ列となる。また、終了地点が赤・黄色の領域ならば、回転対象は図8(b)
のように縦のキューブ列となる。
緑・青の領域ならば
赤・黄の領域ならば
回転対象と回転方向は(a)
回転対象と回転方向は(b)
(a)
(b)
図8 回転対象と回転方向の決定の例
上図○
○をドラッグ開始地点とするとき、ドラッグ終了地点によって(a)と(b)に分岐
(a) 横のキューブ列をY軸回転: 緑の領域なら時計周り,青の領域なら反時計周り
(b) 縦のキューブ列をX軸回転: 赤の領域なら時計周り,黄の領域なら反時計回り
8
以上の処理によって、ルービックキューブの回転軸および回転方向、回転対象を決める
ことができる。ドラッグ開始地点がルービックキューブの正面、右側面、上面のいずれに
位置するかによって、回転させるキューブ列とその回転方向は図9で示した例のように異
なってくる。
図9 ドラッグ開始地点によるキューブ列の回転方向の違い
9
4.付記
本卒業研究で開発したルービックキューブ・アプリケーションのソースコードを以下に
記す。本アプリケーションの実行形式は http://www.***からダウンロードできる。
//----------------------------------------------------------------------------// ルービックキューブ・アプリケーションの開発
// 情報学演習Ⅲ(2008 年度卒業研究): 福知 寛史 ・ 若野 吉治
// 情報学演習Ⅲ(2009 年度卒業研究): 中山 孝昭
//----------------------------------------------------------------------------#include <windows.h>
#include <mmsystem.h>
#include <d3dx9.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "resource2.h"
//----------------------------------------------------------------------------// 表示モード
//----------------------------------------------------------------------------#define WIDTH
800
// 横幅
#define HEIGHT
600
// 高さ
#define FULLSCREEN FALSE
// TRUE = フルスクリーン / FALSE = ウィンドウ
//----------------------------------------------------------------------------// 変数宣言
//----------------------------------------------------------------------------LPDIRECT3D9
g_pD3D
= NULL;
// Direct3D のインスタンス
LPDIRECT3DDEVICE9
g_pd3dDevice
= NULL;
// Device のインスタンス
LPD3DXFONT
g_pFont
= NULL;
// フォント
DWORD
preTime
= NULL;
// 直前の描画時刻
DWORD
nowTime
= NULL;
// 現在の描画時刻
float
fps
= NULL;
// FPS
// Xファイル
LPD3DXMESH
g_pMesh
= NULL;
// メッシュ
D3DMATERIAL9*
g_pMaterial
= NULL;
// 材質
LPDIRECT3DTEXTURE9* g_pTexture
= NULL;
// テクスチャ
DWORD
g_NumMaterial
= 0;
// メッシュ数
int
n_Cube
= 52;
// キューブ数
LPD3DXMESH
g_Cube[60];
// 各キューブのメッシュ
DWORD
g_Numb[60];
// 各キューブのメッシュ数
// カレント・インスタンス
HINSTANCE
hCurInst;
// 回転
int
rCode
= -1;
// 動作状況: -1=停止,+1=回転中
float
rTime
= 0.0f;
// 回転開始時刻
float
xCube[60];
// 各キューブのX軸回転角(新規)
float
yCube[60];
// 各キューブのY軸回転角(新規)
float
zCube[60];
// 各キューブのZ軸回転角(新規)
float
x_Old[60];
// 各キューブのX軸回転角(現在)
float
y_Old[60];
// 各キューブのY軸回転角(現在)
float
z_Old[60];
// 各キューブのZ軸回転角(現在)
// 各キューブの回転行列(新規)
D3DXMATRIXA16
matCube[60];
D3DXMATRIXA16
matPrev[60];
// 各キューブの回転行列(現在)
float
rSpeed
= 0.5f;
// 回転速度(回転に要する時間[s])
// セル配置
int
LocCube[3][3][3];
// 各キューブの配置
int
RotationKey
= 0;
// 回転対象
// 文字表示
int
ShowText
= 0;
// 選択セル
int
SelCube[60];
// マウスポインタのX座標,Y座標
10
int
X1, Y1;
int
X2, Y2;
// 表示キューブの各面の頂点番号(時計回り)
int
R1[27][4]={ 0, 1, 5,
1, 2, 6,
2, 3, 7,
4, 5, 9,
5, 6, 10,
6, 7, 11,
8, 9, 13,
9, 10, 14,
10, 11, 15,
16, 17, 21,
17, 18, 22,
18, 19, 23,
20, 21, 25,
21, 22, 26,
22, 23, 27,
24, 25, 1,
25, 26, 2,
26, 27, 3,
3, 27, 37,
27, 23, 38,
23, 19, 39,
7, 37, 41,
37, 38, 42,
38, 39, 43,
11, 41, 45,
41, 42, 46,
42, 43, 47,
// 表示キューブの各面の頂点座標(X,Y)
int
R2[48][2]={ 53, 92,
103, 105,
166, 121,
242, 136,
64, 167,
107, 181,
168, 202,
240, 226,
71, 233,
115, 250,
170, 274,
236, 300,
79, 281,
120, 305,
172, 330,
232, 361,
173, 52,
210, 59,
263, 65,
321, 74,
145, 62,
184, 67,
240, 77,
302, 89,
105, 76,
146, 85,
209, 97,
278, 113,
53, 92,
103, 105,
166, 121,
242, 136,
242, 136,
278, 113,
302, 89,
321, 74,
// ボタンを押した地点
// ボタンを離した地点
4,
5,
6,
8,
9,
10,
12,
13,
14,
20,
21,
22,
24,
25,
26,
0,
1,
2,
7,
37,
38,
11,
41,
42,
15,
45,
46 };
11
240,
273,
299,
314,
236,
268,
291,
307,
232,
264,
286,
303,
// キューブ番号
int
int
// 作業用変数
float
// マウスボタン
bool
bool
226,
194,
159,
140,
300,
258,
223,
197,
361,
310,
273,
242 };
targetCube
targetLock
= -1;
= -1;
// マウスポインタ下のキューブ
// 回転作業対象のキューブ
degDraw
= 0.0;
// ドラッグ方向
Lbtn
Rbtn
= FALSE;
= FALSE;
// 左ボタンのオン/オフ
// 右ボタンのオン/オフ
//----------------------------------------------------------------------------// 初期設定
//----------------------------------------------------------------------------HRESULT InitD3D( HWND hWnd )
{
// Direct3D インスタンスの作成
if( (g_pD3D = Direct3DCreate9(D3D_SDK_VERSION)) == NULL )
return E_FAIL;
// Device の設定
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory( &d3dpp, sizeof(d3dpp) );
d3dpp.BackBufferWidth
= WIDTH;
d3dpp.BackBufferHeight
= HEIGHT;
d3dpp.BackBufferFormat
= FULLSCREEN ? D3DFMT_R5G6B5 : D3DFMT_UNKNOWN;
d3dpp.SwapEffect
= D3DSWAPEFFECT_DISCARD;
d3dpp.Windowed
= FULLSCREEN ? FALSE : TRUE;
d3dpp.EnableAutoDepthStencil
= TRUE;
d3dpp.AutoDepthStencilFormat
= D3DFMT_D16;
// Device インスタンスの作成
if( FAILED(g_pD3D->CreateDevice( D3DADAPTER_DEFAULT,
D3DDEVTYPE_HAL,
hWnd,
D3DCREATE_SOFTWARE_VERTEXPROCESSING,
&d3dpp,
&g_pd3dDevice)) )
return E_FAIL;
// Z-Buffer
g_pd3dDevice->SetRenderState( D3DRS_ZENABLE, TRUE );
// 環境光
g_pd3dDevice->SetRenderState( D3DRS_AMBIENT, 0xffffffff );
// フォントの作成
if( FAILED( D3DXCreateFont( g_pd3dDevice,
28,
0,
0,
0,
FALSE,
SHIFTJIS_CHARSET,
OUT_DEFAULT_PRECIS,
ANTIALIASED_QUALITY,
FIXED_PITCH | FF_SCRIPT,
"MS Pゴシック",
&g_pFont) ) )
return E_FAIL;
// 時刻
preTime = timeGetTime();
return S_OK;
12
}
//----------------------------------------------------------------------------// 3Dモデル
//----------------------------------------------------------------------------HRESULT InitGeometry()
{
LPD3DXBUFFER
pMB;
int
n;
DWORD
i, k;
// 材質およびテクスチャ
D3DXMATERIAL* dxMaterial;
g_pMaterial
= new D3DMATERIAL9[600];
g_pTexture
= new LPDIRECT3DTEXTURE9[600];
// Xファイルのリソース名
int xResource[53] = { IDR_XFILE2, IDR_XFILE6, IDR_XFILE1, IDR_XFILE3, IDR_XFILE4,
IDR_XFILE5, IDR_XFILE7, IDR_XFILE8, IDR_XFILE9, IDR_XFILE10,
IDR_XFILE11, IDR_XFILE12, IDR_XFILE13, IDR_XFILE14, IDR_XFILE15,
IDR_XFILE16, IDR_XFILE17, IDR_XFILE18, IDR_XFILE19, IDR_XFILE20,
IDR_XFILE21, IDR_XFILE22, IDR_XFILE23, IDR_XFILE24, IDR_XFILE25,
IDR_XFILE26,
IDR_XFILE28, IDR_XFILE32, IDR_XFILE27, IDR_XFILE29, IDR_XFILE30,
IDR_XFILE31, IDR_XFILE33, IDR_XFILE34, IDR_XFILE35, IDR_XFILE36,
IDR_XFILE37, IDR_XFILE38, IDR_XFILE39, IDR_XFILE40, IDR_XFILE41,
IDR_XFILE42, IDR_XFILE43, IDR_XFILE44, IDR_XFILE45, IDR_XFILE46,
IDR_XFILE47, IDR_XFILE48, IDR_XFILE49, IDR_XFILE50, IDR_XFILE51,
IDR_XFILE54, IDR_XFILE53 };
// セル配置
LocCube[0][0][0] = 13;
LocCube[1][0][0] = 4;
LocCube[2][0][0] = 22;
LocCube[0][1][0] = 10;
LocCube[1][1][0] = 1;
LocCube[2][1][0] = 19;
LocCube[0][2][0] = 16;
LocCube[1][2][0] = 7;
LocCube[2][2][0] = 25;
LocCube[0][0][1] = 12;
LocCube[1][0][1] = 3;
LocCube[2][0][1] = 21;
LocCube[0][1][1] = 9;
LocCube[1][1][1] = -1;
LocCube[2][1][1] = 18;
LocCube[0][2][1] = 15;
LocCube[1][2][1] = 6;
LocCube[2][2][1] = 24;
LocCube[0][0][2] = 14;
LocCube[1][0][2] = 5;
LocCube[2][0][2] = 23;
LocCube[0][1][2] = 11;
LocCube[1][1][2] = 2;
LocCube[2][1][2] = 20;
LocCube[0][2][2] = 17;
LocCube[1][2][2] = 8;
LocCube[2][2][2] = 26;
// 頂点宣言
D3DVERTEXELEMENT9 decl[] = {
{0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},//位置
{0, 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL,
0},//法線
{0, 24, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0},//テクスチャ座標
D3DDECL_END()
};
// 3Dモデル: キューブ
g_Numb[0] = 0;
for( n=0; n<n_Cube; n++ ) {
// Xファイルの読み込み
if( FAILED( D3DXLoadMeshFromXResource( hCurInst,
MAKEINTRESOURCE(xResource[n]),
"XFILE",
D3DXMESH_SYSTEMMEM,
g_pd3dDevice,
NULL,
&pMB,
NULL,
&g_NumMaterial,
&g_pMesh ) ) )
return E_FAIL;
// メッシュ情報のコピー
g_pMesh->CloneMesh( D3DXMESH_SYSTEMMEM, decl, g_pd3dDevice, &g_Cube[n+1]);
// 材質番号のコピー
g_Numb[n+1] = g_Numb[n] + g_NumMaterial;
// 材質およびテクスチャ
dxMaterial = (D3DXMATERIAL*)pMB->GetBufferPointer();
for( i=0; i<g_NumMaterial; i++ ) {
// 材質番号
13
k = g_Numb[n] + i;
// 材質情報のコピー
g_pMaterial[k] = dxMaterial[i].MatD3D;
// 環境光の設定
g_pMaterial[k].Ambient = g_pMaterial[k].Diffuse;
// テクスチャ
g_pTexture[k] = NULL;
}
}
// 材質バッファの解放
pMB->Release();
// メッシュ情報の解放
if( g_pMesh != NULL ) g_pMesh->Release();
n_Cube = n_Cube / 2;
// 回転行列の初期化
for( n=0; n<30; n++ ) {
xCube[n] = yCube[n] = zCube[n] = 0.0f;
D3DXMatrixIdentity( &matCube[n] );
D3DXMatrixIdentity( &matPrev[n] );
}
// 選択セル
for( n=0; n<=n_Cube; n++ )
SelCube[n] = 1;
return S_OK;
}
//----------------------------------------------------------------------------// 終了作業
//----------------------------------------------------------------------------VOID Cleanup()
{
// テクスチャ情報の解放: キューブ
if( g_pTexture != NULL ) {
for( DWORD i = 0; i < g_Numb[n_Cube]; i++ ) {
if( g_pTexture[i] )
g_pTexture[i]->Release();
}
}
// Xファイル情報の解放: キューブ
for( int n=29; n>=0; n-- ) {
if( g_Cube[n] != NULL ) g_Cube[n]->Release();
}
// フォント情報の解放
if( g_pFont != NULL )
g_pFont->Release();
// デバイス情報の解放
if( g_pd3dDevice != NULL )
g_pd3dDevice->Release();
// DirectX の解放
if( g_pD3D != NULL )
g_pD3D->Release();
}
//----------------------------------------------------------------------------// 行列の設定
//----------------------------------------------------------------------------VOID SetupMatrices()
{
int n;
// カメラの位置および視線方向
D3DXVECTOR3
vEyePt( 3.0f, 3.0f,-5.0f );
D3DXVECTOR3
vLookatPt( 0.0f, 0.0f, 0.0f );
D3DXVECTOR3
vUpVec( 0.0f, 1.0f, 0.0f );
D3DXMATRIXA16 matView;
D3DXMatrixLookAtLH( &matView, &vEyePt, &vLookatPt, &vUpVec );
g_pd3dDevice->SetTransform( D3DTS_VIEW, &matView );
// Projection 行列(表示範囲)
D3DXMATRIXA16 matProj;
D3DXMatrixPerspectiveFovLH( &matProj, D3DX_PI/4, 1.2f, 1.0f, 100.0f );
g_pd3dDevice->SetTransform( D3DTS_PROJECTION, &matProj );
// World 行列
D3DXMATRIXA16 matX;
14
D3DXMATRIXA16 matY;
D3DXMATRIXA16 matZ;
D3DXMATRIXA16 matW;
float deg = 2.0f * D3DX_PI * ( timeGetTime()/2000.0f ) / 5.0f;
float rad = D3DX_PI / 180.0f;
float x_deg = 0.0f;
float y_deg = 0.0f;
float z_deg = 0.0f;
float dTime = 0.0f;
// 回転中
if( rCode > 0 ) {
// 回転開始からの経過時間[秒]
dTime = timeGetTime() / 1000.0f - rTime;
// 速度補正
dTime = dTime / rSpeed;
// dTime = 1 で回転完了
if( dTime > 1.0f ) {
dTime = 1.0f;
rCode = -1;
}
// 各3Dモデルの回転行列
for( n=1; n<=n_Cube; n++ ) {
x_deg = ( xCube[n] - x_Old[n] ) * dTime;
y_deg = ( yCube[n] - y_Old[n] ) * dTime;
z_deg = ( zCube[n] - z_Old[n] ) * dTime;
D3DXMatrixRotationX( &matX, x_deg*rad );
D3DXMatrixRotationY( &matY, y_deg*rad );
D3DXMatrixRotationZ( &matZ, z_deg*rad );
D3DXMatrixMultiply( &matW, &matX, &matY );
D3DXMatrixMultiply( &matW, &matW, &matZ );
D3DXMatrixMultiply( &matCube[n], &matPrev[n], &matW );
}
// 回転完了時に回転行列をバックアップ
if( rCode < 0 ) {
for( n=1; n<=n_Cube; n++ ) {
matPrev[n] = matCube[n];
}
}
}
}
//----------------------------------------------------------------------------// レンダリング
//----------------------------------------------------------------------------VOID Render()
{
DWORD
i, k;
int
n, nn;
// 画面クリア
g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0,0,255), 1.0f, 0 );
// 描画開始
if( SUCCEEDED( g_pd3dDevice->BeginScene() ) )
{
// 行列の設定
SetupMatrices();
// 3Dモデルの描画
for( n=0; n<n_Cube; n++ ) {
if( SelCube[n+1] < 0 ) nn = n + 26;
// 陰
else
nn = n;
// 通常
for( k=g_Numb[nn]; k<g_Numb[nn+1]; k++ ) {
i = k - g_Numb[nn];
// 材質およびテクスチャの設定
g_pd3dDevice->SetMaterial( &g_pMaterial[k] );
g_pd3dDevice->SetTexture( 0, g_pTexture[k] );
// メッシュの描画
g_pd3dDevice->SetTransform( D3DTS_WORLD, &matCube[n+1] );
g_Cube[nn+1]->DrawSubset( i );
15
}
}
// 文字列の描画
RECT Rc1 = { 10, 10, 500, 40 };
RECT Rc2 = { 660, 450, 800, 800 };
char Strng[100];
int ret;
if( ShowText == 0 || ShowText == 1 ) {
ret = sprintf_s(Strng, 100, "全体: [←] [→] [↑] [↓]" );
if( RotationKey == 1 ) ret = sprintf_s(Strng, 50, "左側: [↑] [↓]");
if( RotationKey == 2 ) ret = sprintf_s(Strng, 50, "右側: [↑] [↓]");
if( RotationKey == -1 ) ret = sprintf_s(Strng, 50, "下側: [←] [→]");
if( RotationKey == -2 ) ret = sprintf_s(Strng, 50, "上側: [←] [→]");
g_pFont->DrawText( NULL,
Strng,
-1,
&Rc1,
DT_LEFT,
D3DCOLOR_XRGB(255, 255, 255));
}
if( ShowText == 0 || ShowText == 2 ) {
ret = sprintf_s(Strng, 100,
"全:[5]/[S]\n 左:[4]/[A]\n 右:[6]/[D]\n 上:[8]/[W]\n 下:[2]/[Z]");
g_pFont->DrawText( NULL,
Strng,
-1,
&Rc2,
DT_LEFT,
D3DCOLOR_XRGB(255, 255, 255));
}
// 描画の終了
g_pd3dDevice->EndScene();
}
// バックバッファの画面表示
g_pd3dDevice->Present( NULL, NULL, NULL, NULL );
}
//----------------------------------------------------------------------------// キューブ回転(右方向)
//----------------------------------------------------------------------------void RotationRight( int n1, int n2 )
{
int n;
int k1, k2;
int ix, iy, iz;
// 回転準備
rCode = 1;
// 回転中
rTime = timeGetTime() / 1000.0f;
// 回転開始時刻
// 現在の回転角の保存
for( n=1; n<=n_Cube; n++ ) {
x_Old[n] = xCube[n];
y_Old[n] = yCube[n];
z_Old[n] = zCube[n];
}
// 回転前後の角度
for( iy=n1; iy<n2; iy++ ) {
for( ix=0; ix<3; ix++ ) {
for( iz=0; iz<3; iz++ ) {
n = LocCube[ix][iy][iz];
yCube[n] -= 90.0f;
if( yCube[n] < -80.0f ) {
yCube[n] = 270.0f;
y_Old[n] = 360.0f;
}
}
}
}
16
// 新しい配置
for( iy=n1; iy<n2; iy++ ) {
k1 = LocCube[0][iy][1];
k2 = LocCube[0][iy][0];
LocCube[0][iy][1] = LocCube[1][iy][0];
LocCube[0][iy][0] = LocCube[2][iy][0];
LocCube[1][iy][0] = LocCube[2][iy][1];
LocCube[2][iy][0] = LocCube[2][iy][2];
LocCube[2][iy][1] = LocCube[1][iy][2];
LocCube[2][iy][2] = LocCube[0][iy][2];
LocCube[1][iy][2] = k1;
LocCube[0][iy][2] = k2;
}
}
//----------------------------------------------------------------------------// キューブ回転(左方向)
//----------------------------------------------------------------------------void RotationLeft( int n1, int n2 )
{
int n;
int k1, k2;
int ix, iy, iz;
// 回転準備
rCode = 1;
rTime = timeGetTime() / 1000.0f;
for( n=1; n<=n_Cube; n++ ) {
x_Old[n] = xCube[n];
y_Old[n] = yCube[n];
z_Old[n] = zCube[n];
}
// 回転前後の角度
for( iy=n1; iy<n2; iy++ ) {
for( ix=0; ix<3; ix++ ) {
for( iz=0; iz<3; iz++ ) {
n = LocCube[ix][iy][iz];
yCube[n] += 90.0f;
if( yCube[n] > 350.0f ) {
yCube[n] =
0.0f;
y_Old[n] = -90.0f;
}
}
}
}
// 新しい配置
for( iy=n1; iy<n2; iy++ ) {
k1 = LocCube[0][iy][2];
k2 = LocCube[1][iy][2];
LocCube[0][iy][2] = LocCube[2][iy][2];
LocCube[1][iy][2] = LocCube[2][iy][1];
LocCube[2][iy][2] = LocCube[2][iy][0];
LocCube[2][iy][1] = LocCube[1][iy][0];
LocCube[2][iy][0] = LocCube[0][iy][0];
LocCube[1][iy][0] = LocCube[0][iy][1];
LocCube[0][iy][0] = k1;
LocCube[0][iy][1] = k2;
}
}
//----------------------------------------------------------------------------// キューブ回転(下方向)
//----------------------------------------------------------------------------void RotationDown( int n1, int n2 )
{
int n;
int k1, k2;
int ix, iy, iz;
17
// 回転準備
rCode = 1;
rTime = timeGetTime() / 1000.0f;
for( n=1; n<=n_Cube; n++ ) {
x_Old[n] = xCube[n];
y_Old[n] = yCube[n];
z_Old[n] = zCube[n];
}
// 回転前後の角度
for( ix=n1; ix<n2; ix++ ) {
for( iy=0; iy<3; iy++ ) {
for( iz=0; iz<3; iz++ ) {
n = LocCube[ix][iy][iz];
xCube[n] -= 90.0f;
if( xCube[n] < -80.0f ) {
xCube[n] = 270.0f;
x_Old[n] = 360.0f;
}
}
}
}
// 新しい配置
for( ix=n1; ix<n2; ix++ ) {
k1 = LocCube[ix][0][2];
k2 = LocCube[ix][1][2];
LocCube[ix][0][2] = LocCube[ix][2][2];
LocCube[ix][1][2] = LocCube[ix][2][1];
LocCube[ix][2][2] = LocCube[ix][2][0];
LocCube[ix][2][1] = LocCube[ix][1][0];
LocCube[ix][2][0] = LocCube[ix][0][0];
LocCube[ix][1][0] = LocCube[ix][0][1];
LocCube[ix][0][0] = k1;
LocCube[ix][0][1] = k2;
}
}
//----------------------------------------------------------------------------// キューブ回転(上方向)
//----------------------------------------------------------------------------void RotationUp( int n1, int n2 )
{
int n;
int k1, k2;
int ix, iy, iz;
// 回転準備
rCode = 1;
rTime = timeGetTime() / 1000.0f;
for( n=1; n<=n_Cube; n++ ) {
x_Old[n] = xCube[n];
y_Old[n] = yCube[n];
z_Old[n] = zCube[n];
}
// 回転前後の角度
for( ix=n1; ix<n2; ix++ ) {
for( iy=0; iy<3; iy++ ) {
for( iz=0; iz<3; iz++ ) {
n = LocCube[ix][iy][iz];
xCube[n] += 90.0f;
if( xCube[n] > 350.0f ) {
xCube[n] =
0.0f;
x_Old[n] = -90.0f;
}
}
}
}
// 新しい配置
for( ix=n1; ix<n2; ix++ ) {
18
k1 = LocCube[ix][0][1];
k2 = LocCube[ix][0][0];
LocCube[ix][0][1] = LocCube[ix][1][0];
LocCube[ix][0][0] = LocCube[ix][2][0];
LocCube[ix][1][0] = LocCube[ix][2][1];
LocCube[ix][2][0] = LocCube[ix][2][2];
LocCube[ix][2][1] = LocCube[ix][1][2];
LocCube[ix][2][2] = LocCube[ix][0][2];
LocCube[ix][1][2] = k1;
LocCube[ix][0][2] = k2;
}
}
//----------------------------------------------------------------------------// キューブ回転(反時計回り)
//----------------------------------------------------------------------------void RotationAntiClock( int n1, int n2 )
{
int n;
int k1, k2;
int ix, iy, iz;
// 回転準備
rCode = 1;
rTime = timeGetTime() / 1000.0f;
for( n=1; n<=n_Cube; n++ ) {
x_Old[n] = xCube[n];
y_Old[n] = yCube[n];
z_Old[n] = zCube[n];
}
// 回転前後の角度
for( iz=n1; iz<n2; iz++ ) {
for( ix=0; ix<3; ix++ ) {
for( iy=0; iy<3; iy++ ) {
n = LocCube[ix][iy][iz];
zCube[n] += 90.0f;
if( zCube[n] > 350.0f ) {
zCube[n] =
0.0f;
z_Old[n] = -90.0f;
}
}
}
}
// 新しい配置
for( iz=n1; iz<n2; iz++ ) {
k1 = LocCube[0][1][iz];
k2 = LocCube[0][0][iz];
LocCube[0][0][iz] = LocCube[0][2][iz];
LocCube[0][1][iz] = LocCube[1][2][iz];
LocCube[0][2][iz] = LocCube[2][2][iz];
LocCube[1][2][iz] = LocCube[2][1][iz];
LocCube[2][1][iz] = LocCube[1][0][iz];
LocCube[2][2][iz] = LocCube[2][0][iz];
LocCube[1][0][iz] = k1;
LocCube[2][0][iz] = k2;
}
}
//----------------------------------------------------------------------------// キューブ回転(時計回り)
//----------------------------------------------------------------------------void RotationClock( int n1, int n2 )
{
int n;
int k1, k2;
int ix, iy, iz;
// 回転準備
rCode = 1;
19
rTime = timeGetTime() / 1000.0f;
for( n=1; n<=n_Cube; n++ ) {
x_Old[n] = xCube[n];
y_Old[n] = yCube[n];
z_Old[n] = zCube[n];
}
// 回転前後の角度
for( iz=n1; iz<n2; iz++ ) {
for( ix=0; ix<3; ix++ ) {
for( iy=0; iy<3; iy++ ) {
n = LocCube[ix][iy][iz];
zCube[n] -= 90.0f;
if( zCube[n] < -80.0f ) {
zCube[n] = 270.0f;
z_Old[n] = 360.0f;
}
}
}
}
// 新しい配置
for( iz=n1; iz<n2; iz++ ) {
k1 = LocCube[1][0][iz];
k2 = LocCube[0][0][iz];
LocCube[0][0][iz] = LocCube[2][0][iz];
LocCube[1][0][iz] = LocCube[2][1][iz];
LocCube[2][0][iz] = LocCube[2][2][iz];
LocCube[2][1][iz] = LocCube[1][2][iz];
LocCube[2][2][iz] = LocCube[0][2][iz];
LocCube[1][2][iz] = LocCube[0][1][iz];
LocCube[0][1][iz] = k1;
LocCube[0][2][iz] = k2;
}
}
//----------------------------------------------------------------------------// キューブ回転
//----------------------------------------------------------------------------void RotationCube( int Target, float dX, float dY )
{
float deg;
// ドラッグ方向
if( fabs(dX) > 0.01 ) {
deg = atan( dY / dX ) * 180.0 / 3.14159;
if( dX < 0.0 ) {
if( dY > 0.0 ) deg += 180.0;
else
deg -= 180.0;
}
} else {
if( dY > 0.0 ) deg = 90.0;
else
deg = -90.0;
}
degDraw = deg;
// 正面
if( Target > -1 && Target < 9 ) {
// 右方向
if( deg > -52.0 && deg < 38.0 ){
// 回転対象
switch( Target ) {
// 上段
case 0:
case 1:
case 2:
RotationRight( 2, 3 );
break;
// 中段
case 3:
case 4:
20
case 5:
RotationRight( 1, 2 );
break;
// 下段
case 6:
case 7:
case 8:
RotationRight( 0, 1 );
break;
}
// 上方向
} else if( deg > 38.0 && deg <
// 回転対象
switch( Target ) {
// 左段
case 0:
case 3:
case 6:
RotationUp( 0, 1
break;
// 中段
case 1:
case 4:
case 7:
RotationUp( 1, 2
break;
// 右段
case 2:
case 5:
case 8:
RotationUp( 2, 3
break;
}
// 下方向
} else if( deg > -142.0 && deg
// 回転対象
switch( Target ) {
// 左段
case 0:
case 3:
case 6:
RotationDown( 0,
break;
// 中段
case 1:
case 4:
case 7:
RotationDown( 1,
break;
// 右段
case 2:
case 5:
case 8:
RotationDown( 2,
break;
}
// 左方向
} else {
// 回転対象
switch( Target ) {
// 上段
case 0:
case 1:
case 2:
RotationLeft( 2,
break;
// 中段
128.0 ){
);
);
);
< -52.0 ){
1 );
2 );
3 );
3 );
21
case 3:
case 4:
case 5:
RotationLeft( 1, 2 );
break;
// 下段
case 6:
case 7:
case 8:
RotationLeft( 0, 1 );
break;
}
}
}
// 上面
if( Target > 8 && Target < 18 ) {
// 時計回り
if( deg > -83.0 && deg < 7.0 ){
// 回転対象
switch( Target ) {
// 奥
case 9:
case 10:
case 11:
RotationClock( 0, 1 );
break;
// 中
case 12:
case 13:
case 14:
RotationClock( 1, 2 );
break;
// 前
case 15:
case 16:
case 17:
RotationClock( 2, 3 );
break;
}
// 上方向
} else if( deg > 7.0 && deg < 97.0 ){
// 回転対象
switch( Target ) {
// 左段
case 9:
case 12:
case 15:
RotationUp( 0, 1 );
break;
// 中段
case 10:
case 13:
case 16:
RotationUp( 1, 2 );
break;
// 右段
case 11:
case 14:
case 17:
RotationUp( 2, 3 );
break;
}
// 下方向
} else if( deg > -173.0 && deg < -83.0 ){
// 回転対象
switch( Target ) {
// 左段
22
case 9:
case 12:
case 15:
RotationDown( 0, 1 );
break;
// 中段
case 10:
case 13:
case 16:
RotationDown( 1, 2 );
break;
// 右段
case 11:
case 14:
case 17:
RotationDown( 2, 3 );
break;
}
// 反時計回り
} else {
// 回転対象
switch( Target ) {
// 奥
case 9:
case 10:
case 11:
RotationAntiClock( 0, 1 );
break;
// 中
case 12:
case 13:
case 14:
RotationAntiClock( 1, 2 );
break;
// 前
case 15:
case 16:
case 17:
RotationAntiClock( 2, 3 );
break;
}
}
}
// 右面
if( Target > 17 && Target < 27 ) {
// 右方向
if( deg > -22.0 && deg < 68.0 ){
// 回転対象
switch( Target ) {
// 上段
case 18:
case 19:
case 20:
RotationRight( 2, 3 );
break;
// 中段
case 21:
case 22:
case 23:
RotationRight( 1, 2 );
break;
// 下段
case 24:
case 25:
case 26:
RotationRight( 0, 1 );
break;
23
}
// 時計回り
} else if( deg > -112.0 && deg < -22.0 ){
// 回転対象
switch( Target ) {
// 奥
case 20:
case 23:
case 26:
RotationClock( 0, 1 );
break;
// 中
case 19:
case 22:
case 25:
RotationClock( 1, 2 );
break;
// 前
case 18:
case 21:
case 24:
RotationClock( 2, 3 );
break;
}
// 反時計回り
} else if( deg > 68.0 && deg < 158.0 ){
// 回転対象
switch( Target ) {
// 奥
case 20:
case 23:
case 26:
RotationAntiClock( 0, 1 );
break;
// 中
case 19:
case 22:
case 25:
RotationAntiClock( 1, 2 );
break;
// 前
case 18:
case 21:
case 24:
RotationAntiClock( 2, 3 );
break;
}
// 左方向
} else {
// 回転対象
switch( Target ) {
// 上段
case 18:
case 19:
case 20:
RotationLeft( 2, 3 );
break;
// 中段
case 21:
case 22:
case 23:
RotationLeft( 1, 2 );
break;
// 下段
case 24:
case 25:
case 26:
24
RotationLeft( 0, 1 );
break;
}
}
}
}
//----------------------------------------------------------------------------// キューブ回転(全体)
//----------------------------------------------------------------------------void RotationAll( int Target, float dX, float dY )
{
float deg;
// ドラッグ方向
if( fabs(dX) > 0.01 ) {
deg = atan( dY / dX ) * 180.0 / 3.14159;
if( dX < 0.0 ) {
if( dY > 0.0 ) deg += 180.0;
else
deg -= 180.0;
}
} else {
if( dY > 0.0 ) deg = 90.0;
else
deg = -90.0;
}
degDraw = deg;
// 正面
if( Target > -1 && Target < 9 ) {
// 右方向
if( deg > -52.0 && deg < 38.0 )
RotationRight( 0, 3 );
// 上方向
else if( deg > 38.0 && deg < 128.0 )
RotationUp( 0, 3 );
// 下方向
else if( deg > -142.0 && deg < -52.0 )
RotationDown( 0, 3 );
// 左方向
else
RotationLeft( 0, 3 );
}
// 上面
if( Target > 8 && Target < 18 ) {
// 時計回り
if( deg > -83.0 && deg < 7.0 )
RotationClock( 0, 3 );
// 上方向
else if( deg > 7.0 && deg < 97.0 )
RotationUp( 0, 3 );
// 下方向
else if( deg > -173.0 && deg < -83.0 )
RotationDown( 0, 3 );
// 反時計回り
else
RotationAntiClock( 0, 3 );
}
// 右面
if( Target > 17 && Target < 27 ) {
// 右方向
if( deg > -22.0 && deg < 68.0 )
RotationRight( 0, 3 );
// 時計回り
else if( deg > -112.0 && deg < -22.0 )
RotationClock( 0, 3 );
// 反時計回り
else if( deg > 68.0 && deg < 158.0 )
RotationAntiClock( 0, 3 );
// 左方向
else
RotationLeft( 0, 3 );
}
}
//----------------------------------------------------------------------------// イベント検出
//----------------------------------------------------------------------------LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
int
i, j, m, n;
int
ix, iy, iz;
25
int
n1, n2;
float
ax, ay, bx, by;
float
rad, radSum;
float
x, y;
char s[100];
HCURSOR hCursor;
// マウスポインタの形状
switch( msg )
{
// ウィンドウを閉じたとき
case WM_DESTROY:
Cleanup();
PostQuitMessage( 0 );
return 0;
// 左ボタンを押す
case WM_LBUTTONDOWN:
// 移動中はキー入力を受け付けない
if( rCode > 0 ) return 0;
// マウスボタン・オンの場合は受け付けない
if( Lbtn == TRUE || Rbtn == TRUE ) return 0;
Lbtn = TRUE;
// マウスポインタの座標
X1 = LOWORD(lParam );
Y1 = HIWORD(lParam );
// 操作対象のキューブ
targetLock = targetCube;
// マウスカーソルの変更
hCursor = LoadCursor(NULL, IDC_HAND);
SetClassLong( hWnd, GCL_HCURSOR, (long)hCursor );
break ;
// 左ボタンを離す
case WM_LBUTTONUP:
// 移動中はキー入力を受け付けない
if( rCode > 0 ) return 0;
// 左ボタン・オフの場合は受け付けない
if( Lbtn == FALSE ) return 0;
// マウスポインタの座標
X2 = LOWORD(lParam);
Y2 = HIWORD(lParam);
// マウスカーソルの変更
hCursor = LoadCursor(NULL, IDC_ARROW);
SetClassLong( hWnd, GCL_HCURSOR, (long)hCursor );
// 一部回転
RotationCube( targetLock, X2-X1, Y1-Y2 );
// 左ボタン・オフ
Lbtn = FALSE;
break ;
// 右ボタンを押す
case WM_RBUTTONDOWN:
// 移動中はキー入力を受け付けない
if( rCode > 0 ) return 0;
// マウスボタン・オンの場合は受け付けない
if( Lbtn == TRUE || Rbtn == TRUE ) return 0;
Rbtn = TRUE;
// マウスポインタの座標
X1 = LOWORD(lParam );
Y1 = HIWORD(lParam );
// 操作対象のキューブ
targetLock = targetCube;
// マウスカーソルの変更
hCursor = LoadCursor(NULL, IDC_HAND);
SetClassLong( hWnd, GCL_HCURSOR, (long)hCursor );
break ;
// 右ボタンを離す
case WM_RBUTTONUP:
// 移動中はキー入力を受け付けない
if( rCode > 0 ) return 0;
// 右ボタン・オフの場合は受け付けない
26
if( Rbtn == FALSE ) return 0;
// マウスポインタの座標
X2 = LOWORD(lParam);
Y2 = HIWORD(lParam);
// マウスカーソルの変更
hCursor = LoadCursor(NULL, IDC_ARROW);
SetClassLong( hWnd, GCL_HCURSOR, (long)hCursor );
// 全体回転
RotationAll( targetLock, X2-X1, Y1-Y2 );
// 右ボタン・オフ
Rbtn = FALSE;
break ;
// マウスを動かす
case WM_MOUSEMOVE:
// マウスポインタの座標
X2 = LOWORD(lParam);
Y2 = HIWORD(lParam);
// マウスポインタ下のキューブ面
for( m=0; m<27; m++ ) {
radSum = 0.0f;
for( i=0; i<4; i++ ) {
// ベクトルA
n = R1[m][i];
ax = R2[n][0] - X2;
ay = R2[n][1] - Y2;
// ベクトルB
j = i + 1;
if( j > 3 ) j = 0;
n = R1[m][j];
bx = R2[n][0] - X2;
by = R2[n][1] - Y2;
// ベクトル間の角
y = ax * by - bx * ay; // 外積
x = ax * bx + ay * by; // 内積
if( fabs(x) > 0.01 ) {
rad = atan( y / x );
if( x < 0.0 ) {
if( y > 0.0 ) rad += D3DX_PI;
else
rad -= D3DX_PI;
}
} else {
if( y > 0.0 ) rad = D3DX_PI / 2.0f;
else
rad = -D3DX_PI / 2.0f;
}
radSum += rad;
}
// 判定
targetCube = -1;
if( fabs(radSum) > 5.0 ) {
targetCube = m;
// キューブ面の番号
break;
}
}
break ;
// キーが押されたとき
case WM_KEYDOWN:
// 移動中はキー入力を受け付けない
if( rCode > 0 ) return 0;
// キーの種類に応じた命令分岐
switch( wParam )
{
// 左矢印キー(←)
case VK_LEFT:
// 回転対象
if( RotationKey == -6 ) {
// 下側
n1 = 0;
n2 = 1;
27
} else if( RotationKey ==
n1 = 1;
n2 = 2;
} else if( RotationKey ==
n1 = 2;
n2 = 3;
} else if( RotationKey ==
n1 = 0;
n2 = 3;
} else {
return 0;
}
// 回転
RotationLeft( n1, n2 );
return 0;
// 右矢印キー(→)
case VK_RIGHT:
// 回転対象
if( RotationKey == -6 ) {
n1 = 0;
n2 = 1;
} else if( RotationKey ==
n1 = 1;
n2 = 2;
} else if( RotationKey ==
n1 = 2;
n2 = 3;
} else if( RotationKey ==
n1 = 0;
n2 = 3;
} else {
return 0;
}
// 回転
RotationRight( n1, n2 );
return 0;
// 上矢印キー(↑)
case VK_UP:
// 回転対象
if( RotationKey == 2 ) {
n1 = 0;
n2 = 1;
} else if( RotationKey ==
n1 = 1;
n2 = 2;
} else if( RotationKey ==
n1 = 2;
n2 = 3;
} else if( RotationKey ==
n1 = 0;
n2 = 3;
} else {
return 0;
}
// 回転
RotationUp( n1, n2 );
return 0;
// 下矢印キー(↓)
case VK_DOWN:
// 回転対象
if( RotationKey == 2 ) {
n1 = 0;
n2 = 1;
} else if( RotationKey ==
n1 = 1;
n2 = 2;
} else if( RotationKey ==
28
-4 ) {
// 中
-2 ) {
// 上側
0 ) {
// 全体
// その他
// 下側
-4 ) {
// 中
-2 ) {
// 上側
0 ) {
// 全体
// その他
// 左側
4 ) {
// 中
6 ) {
// 右側
0 ) {
// 全体
// その他
// 左側
4 ) {
// 右側
6 ) {
// 右側
n1 = 2;
n2 = 3;
} else if( RotationKey == 0 ) { // 全体
n1 = 0;
n2 = 3;
} else {
// その他
return 0;
}
// 回転
RotationDown( n1, n2 );
return 0;
// 全体(5/S)
case VK_NUMPAD5:
case 83:
RotationKey = 0;
// 選択セル
for( n=0; n<=n_Cube; n++ ) SelCube[n] = 1;
return 0;
// 左側(4/A)
case VK_NUMPAD4:
case 65:
if( RotationKey < 1 ) RotationKey = 4;
else
RotationKey -= 2;
if( RotationKey < 1 ) RotationKey = 6;
// 回転対象
if( RotationKey == 2 ) {
// 左側
n1 = 0;
n2 = 1;
} else if( RotationKey == 4 ) { // 中
n1 = 1;
n2 = 2;
} else if( RotationKey == 6 ) { // 右側
n1 = 2;
n2 = 3;
} else if( RotationKey == 0 ) { // 全体
n1 = 0;
n2 = 3;
} else {
// その他
return 0;
}
// 選択セル
for( n=0; n<=n_Cube; n++ ) SelCube[n] = -1;
for( iz=0; iz<3; iz++ ) {
for( iy=0; iy<3; iy++ ) {
for( ix=n1; ix<n2; ix++ ) {
SelCube[ LocCube[ix][iy][iz] ] = 1;
}
}
}
return 0;
// 右側(6/D)
case VK_NUMPAD6:
case 68:
if( RotationKey < 1 ) RotationKey = 4;
else
RotationKey += 2;
if( RotationKey > 7 ) RotationKey = 2;
// 回転対象
if( RotationKey == 2 ) {
// 左側
n1 = 0;
n2 = 1;
} else if( RotationKey == 4 ) { // 中
n1 = 1;
n2 = 2;
} else if( RotationKey == 6 ) { // 右側
n1 = 2;
n2 = 3;
} else if( RotationKey == 0 ) { // 全体
29
n1 = 0;
n2 = 3;
} else {
// その他
return 0;
}
// 選択セル
for( n=0; n<=n_Cube; n++ ) SelCube[n] = -1;
for( iz=0; iz<3; iz++ ) {
for( iy=0; iy<3; iy++ ) {
for( ix=n1; ix<n2; ix++ ) {
SelCube[ LocCube[ix][iy][iz] ] = 1;
}
}
}
return 0;
// 上側(8/W)
case VK_NUMPAD8:
case 87:
if( RotationKey > -1 ) RotationKey = -4;
else
RotationKey += 2;
if( RotationKey > -1 ) RotationKey = -6;
// 回転対象
if( RotationKey == -6 ) {
// 下側
n1 = 0;
n2 = 1;
} else if( RotationKey == -4 ) { // 中
n1 = 1;
n2 = 2;
} else if( RotationKey == -2 ) { // 上側
n1 = 2;
n2 = 3;
} else if( RotationKey == 0 ) { // 全体
n1 = 0;
n2 = 3;
} else {
// その他
return 0;
}
// 選択セル
for( n=0; n<=n_Cube; n++ ) SelCube[n] = -1;
for( iz=0; iz<3; iz++ ) {
for( iy=n1; iy<n2; iy++ ) {
for( ix=0; ix<3; ix++ ) {
SelCube[ LocCube[ix][iy][iz] ] = 1;
}
}
}
return 0;
// 下側(2/Z)
case VK_NUMPAD2:
case 90:
if( RotationKey > -1 ) RotationKey = -4;
else
RotationKey -= 2;
if( RotationKey < -7 ) RotationKey = -2;
// 回転対象
if( RotationKey == -6 ) {
// 下側
n1 = 0;
n2 = 1;
} else if( RotationKey == -4 ) { // 中
n1 = 1;
n2 = 2;
} else if( RotationKey == -2 ) { // 上側
n1 = 2;
n2 = 3;
} else if( RotationKey == 0 ) { // 全体
n1 = 0;
n2 = 3;
} else {
// その他
30
return 0;
}
// 選択セル
for( n=0; n<=n_Cube; n++ ) SelCube[n] = -1;
for( iz=0; iz<3; iz++ ) {
for( iy=n1; iy<n2; iy++ ) {
for( ix=0; ix<3; ix++ ) {
SelCube[ LocCube[ix][iy][iz] ] = 1;
}
}
}
return 0;
// 文字表示
case VK_F1:
ShowText += 1;
if( ShowText > 3 ) ShowText = 0;
}
break;
// キーを離したとき
case WM_KEYUP:
switch( wParam )
{
// ESC キー → 終了
case VK_ESCAPE:
Cleanup();
PostQuitMessage( 0 );
return 0;
}
break;
}
// 標準イベント
return DefWindowProc( hWnd, msg, wParam, lParam );
}
//----------------------------------------------------------------------------// 初期配置
//----------------------------------------------------------------------------void InitCube()
{
int n, n1, n2;
int k, k1, k2, k3;
int ix, iy, iz;
int j1, j2;
// 各3Dモデルの回転行列
D3DXMATRIXA16 matX;
D3DXMATRIXA16 matY;
D3DXMATRIXA16 matW;
float rad = D3DX_PI / 180.0f;
float x_deg = 0.0f;
float y_deg = 0.0f;
float z_deg = 0.0f;
for( n=1; n<=n_Cube; n++ ) {
x_Old[n] = xCube[n];
y_Old[n] = yCube[n];
z_Old[n] = zCube[n];
}
// 乱数の種
srand( (int)( (double)timeGetTime() / 1000.0f ) );
// 初期操作
for( k=0; k<100; k++ ) {
k1 = (int)( 2.0f * ( (double)rand() / 32767.0f ) );
if( k1 > 1 ) k1 = 1;
k2 = (int)( 3.0f * ( (double)rand() / 32767.0f ) );
if( k2 > 2 ) k2 = 2;
k3 = (int)( 2.0f * ( (double)rand() / 32767.0f ) ) - 1;
if( k3 < 0 ) k3 = -1;
else
k3 = 1;
31
// 操作領域
switch( k1 ) {
case 0: // 左右側
// 左側
if( k2 == 0 ) {
n1 = 0;
n2 = 1;
} else if( k2 == 1 ) { // 右側
n1 = 2;
n2 = 3;
} else {
// 全体
n1 = 0;
n2 = 3;
}
// 回転前後の角度
for( ix=n1; ix<n2; ix++ ) {
for( iy=0; iy<3; iy++ ) {
for( iz=0; iz<3; iz++ ) {
n = LocCube[ix][iy][iz];
x_Old[n] = xCube[n];
xCube[n] = xCube[n] + 90.0f * (float)k3;
if( xCube[n] < -80.0f ) xCube[n] = 270.0f;
if( xCube[n] > 350.0f ) xCube[n] =
0.0f;
}
}
}
// 新しい配置
if( k3 < 0 ) {
// 下向き
for( ix=n1; ix<n2; ix++ ) {
j1 = LocCube[ix][0][2];
j2 = LocCube[ix][1][2];
LocCube[ix][0][2] = LocCube[ix][2][2];
LocCube[ix][1][2] = LocCube[ix][2][1];
LocCube[ix][2][2] = LocCube[ix][2][0];
LocCube[ix][2][1] = LocCube[ix][1][0];
LocCube[ix][2][0] = LocCube[ix][0][0];
LocCube[ix][1][0] = LocCube[ix][0][1];
LocCube[ix][0][0] = j1;
LocCube[ix][0][1] = j2;
}
} else {
// 上向き
for( ix=n1; ix<n2; ix++ ) {
j1 = LocCube[ix][0][1];
j2 = LocCube[ix][0][0];
LocCube[ix][0][1] = LocCube[ix][1][0];
LocCube[ix][0][0] = LocCube[ix][2][0];
LocCube[ix][1][0] = LocCube[ix][2][1];
LocCube[ix][2][0] = LocCube[ix][2][2];
LocCube[ix][2][1] = LocCube[ix][1][2];
LocCube[ix][2][2] = LocCube[ix][0][2];
LocCube[ix][1][2] = j1;
LocCube[ix][0][2] = j2;
}
}
break;
default: // 上下側
if( k2 == 0 ) {
// 下側
n1 = 0;
n2 = 1;
} else if( k2 == 1 ) {
// 上側
n1 = 2;
n2 = 3;
} else {
// 全体
n1 = 0;
n2 = 3;
}
// 回転前後の角度
for( iy=n1; iy<n2; iy++ ) {
32
for( ix=0; ix<3; ix++ ) {
for( iz=0; iz<3; iz++ ) {
n = LocCube[ix][iy][iz];
y_Old[n] = yCube[n];
yCube[n] = yCube[n] + 90.0f * (float)k3;
if( yCube[n] < -80.0f ) yCube[n] = 270.0f;
if( yCube[n] > 350.0f ) yCube[n] =
0.0f;
}
}
}
// 新しい配置
if( k3 < 0 ) {
// 右向き
for( iy=n1; iy<n2; iy++ ) {
j1 = LocCube[0][iy][1];
j2 = LocCube[0][iy][0];
LocCube[0][iy][1] = LocCube[1][iy][0];
LocCube[0][iy][0] = LocCube[2][iy][0];
LocCube[1][iy][0] = LocCube[2][iy][1];
LocCube[2][iy][0] = LocCube[2][iy][2];
LocCube[2][iy][1] = LocCube[1][iy][2];
LocCube[2][iy][2] = LocCube[0][iy][2];
LocCube[1][iy][2] = j1;
LocCube[0][iy][2] = j2;
}
} else {
// 左向き
for( iy=n1; iy<n2; iy++ ) {
j1 = LocCube[0][iy][2];
j2 = LocCube[1][iy][2];
LocCube[0][iy][2] = LocCube[2][iy][2];
LocCube[1][iy][2] = LocCube[2][iy][1];
LocCube[2][iy][2] = LocCube[2][iy][0];
LocCube[2][iy][1] = LocCube[1][iy][0];
LocCube[2][iy][0] = LocCube[0][iy][0];
LocCube[1][iy][0] = LocCube[0][iy][1];
LocCube[0][iy][0] = j1;
LocCube[0][iy][1] = j2;
}
}
break;
}
// 各3Dモデルの回転行列
for( n=1; n<=n_Cube; n++ ) {
x_deg = xCube[n] - x_Old[n];
y_deg = yCube[n] - y_Old[n];
D3DXMatrixRotationX( &matX, x_deg*rad );
D3DXMatrixRotationY( &matY, y_deg*rad );
D3DXMatrixMultiply( &matW, &matX, &matY );
D3DXMatrixMultiply( &matCube[n], &matPrev[n], &matW );
}
// 回転完了時に回転行列をバックアップ
for( n=1; n<=n_Cube; n++ ) {
x_Old[n] = xCube[n];
y_Old[n] = yCube[n];
z_Old[n] = zCube[n];
matPrev[n] = matCube[n];
}
}
}
//----------------------------------------------------------------------------// 起動直後
//----------------------------------------------------------------------------INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR, INT )
{
// カレント・インスタンス
hCurInst = hInst;
// ウィンドウ・クラスの設定
33
WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L, 0L,
GetModuleHandle(NULL), NULL, NULL, NULL, NULL,
"DirectX Programming", NULL };
wc.hIcon = LoadIcon( hInst, MAKEINTRESOURCE(IDI_ICON1) );
wc.hIconSm = NULL;
RegisterClassEx( &wc );
// ウィンドウの横幅と高さ
int g_Width, g_Height;
if( FULLSCREEN ) {
g_Width = WIDTH;
g_Height = HEIGHT;
} else {
g_Width = 400;
g_Height = 400;
}
// ウィンドウの作成
HWND hWnd = CreateWindow( "DirectX Programming",
"Rubik's Cube",
WS_OVERLAPPED | WS_SYSMENU | WS_MINIMIZEBOX | WS_VISIBLE,
CW_USEDEFAULT,
CW_USEDEFAULT,
g_Width,
g_Height,
GetDesktopWindow(),
NULL,
wc.hInstance,
NULL );
// 初期設定
if( SUCCEEDED( InitD3D( hWnd ) ) ) {
// 3Dモデル
if( SUCCEEDED( InitGeometry() ) ) {
// キューブ初期配置
InitCube();
// ウィンドウの表示
ShowWindow( hWnd, SW_SHOWDEFAULT );
UpdateWindow( hWnd );
// メッセージ・ループ
MSG msg;
ZeroMemory( &msg, sizeof(msg) );
while( msg.message!=WM_QUIT ) {
// メッセージを受け取ったとき → イベント処理
if( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) ) {
TranslateMessage( &msg );
DispatchMessage( &msg );
// メッセージがないとき → 画面表示
} else {
Render();
}
}
}
}
UnregisterClass( "DirectX Programming", wc.hInstance );
return 0;
}
34