Fine Kernel ToolKit System (C++版)
ユーザーズマニュアル
Version 4.2.13(Manual Revision 1)

FineKernel Project
1999 - 2024⃝c

OpenGL ARB Working Group,⃝c

Microsoft, Inc.,⃝c

Apple Computer, Inc.,⃝c

The FLTK Team,⃝c

The FreeType Project,⃝c

Autodesk Corp.,⃝cInternational Business Machines Corp.,⃝cXiph.org,⃝c

EXTGL project.

目次
はじめに ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

1

第1章

さあはじめよう・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

3

1.1

直方体回転のサンプルプログラム ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

3

1.2

4 つの “レイヤー”・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

4

1.3

プログラムの概要・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

4

1.4

作成できる “形状” の種類 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

5

1.5

モデルの制御 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

6

1.6

カメラと光源 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

7

1.7

シーン・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

7

1.8

ウィンドウと GUI ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

7

1.9

デバイス状態取得・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

8

1.10

次の段階は.....・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

8

第2章

ベクトル、行列、四元数 (クォータニオン) ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

9

2.1

3 次元ベクトル ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

9

2.1.1

ベクトルの生成・設定 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

9

2.1.2

比較演算子 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

10

2.1.3

単項演算子 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

10

2.1.4

二項演算子 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

11

2.1.5

代入演算子 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

11

2.1.6

ノルム (長さ) の算出 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

12

2.1.7

ベクトルの正規化 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

12

2.1.8

ベクトルの射影 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

12

行列 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

13

2.2.1

FK における行列系 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

13

2.2.2

行列の生成 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

13

2.2.3

比較演算子 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

14

2.2.4

単行演算子 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

14

2.2.5

二項演算子 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

14

2.2.6

代入演算子 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

15

2.2.7

各成分へのアクセス・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

15

2.2.8

その他のメンバ関数・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

15

2.2.9

4 次元ベクトル・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

16

四元数 (クォータニオン) ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

17

2.3.1

四元数クラスのメンバ構成 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

17

2.3.2

四元数の生成と設定・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

17

2.3.3

比較演算子 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

18

2.3.4

単項演算子 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

18

2.3.5

二項演算子 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

18

2.3.6

代入演算子 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

19

2.2

2.3

i

2.3.7オイラー角との相互変換・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

19

2.3.8

各種メンバ関数 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

20

2.3.9

補間関数 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

20

2.4

乱数 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

21

第3章

色とマテリアル・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

22

3.1

色の基本 (fk Color)・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

22

3.2

物質のリアルな表現 (fk Material)・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

22

第4章

形状表現・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

25

4.1

ポリライン (fk Polyline)・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

25

4.2

閉じたポリライン (fk Closedline) ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

26

4.3

点 (fk Point)・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

26

4.4

線分 (fk Line) ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

27

4.5

多角形平面 (fk Polygon)・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

28

4.6

円 (fk Circle) ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

28

4.7

直方体 (fk Block)・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

29

4.8

球 (fk Sphere) ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

31

4.9

正多角柱・円柱 (fk Prism) ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

31

4.10

正多角錐・円錐 (fk Cone)・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

32

4.11

インデックスフェースセット (fk IndexFaceSet) ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

32

4.11.1

STL ファイルの取り込み ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

32

4.11.2

SMF ファイルの取り込み ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

33

4.11.3

DXF ファイルの取り込み ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

33

4.11.4

MQO ファイルの取り込み・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

33

4.11.5

MQO データの取り込み ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

34

4.11.6

形状情報の取得と、頂点座標の移動 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

34

4.11.7

形状データの各種ファイルへの出力 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

35

4.12

光源 (fk Light) ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

36

4.13

パーティクル用クラス ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

37

4.13.1

fk Particle クラス ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

37

4.13.2

fk ParticleSet クラス ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

38

4.13.2.1

fk ParticleSet クラスの仮想関数・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

38

4.13.2.2

fk ParticleSet クラスの通常のメンバ関数 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

38

4.14

D3DX 形式中のアニメーション動作・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

40

4.15

BVH 形式のモーション再生 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

40

第5章

動的な形状生成と形状変形・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

42

5.1

立体の作成方法 (1)・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

42

5.2

立体の作成方法 (2)・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

43

5.3

頂点の移動・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

45

第6章

テクスチャマッピングと画像処理・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

47

6.1

テクスチャマッピング ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

47

矩形テクスチャ ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

47

6.1.1.1

基本的な利用方法・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

47

6.1.1.2

画像中の一部分の切り出し ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

48

6.1.1.3

リピートモード ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

48

6.1.1

ii

6.1.2三角形テクスチャ ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

48

6.1.3

IFS テクスチャ ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

49

6.1.4

メッシュテクスチャ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

50

6.1.5

テクスチャのレンダリング品質設定 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

51

6.2

画像処理用クラス・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

52

第7章

文字列表示 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

56

7.1

スプライトモデル・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

56

7.1.1

変数の準備とフォントの読み込み ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

56

7.1.2

各種設定 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

57

7.1.3

シーンやウィンドウへの登録 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

57

7.1.4

文字列設定 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

58

高度な文字列表示・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

58

7.2.1

文字列テクスチャの生成・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

59

7.2.2

フォント情報の読み込み・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

59

7.2.3

文字列テクスチャの各種設定 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

59

7.2.3.1

フォントに関する設定・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

59

7.2.3.2

文字列配置に関する設定 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

61

7.2.3.3

文字送りに関する設定・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

61

7.2.4

文字列の設定・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

62

7.2.5

文字列情報の読み込み ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

62

7.2.6

文字列読み込み後の情報取得 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

63

7.2.7

文字列テクスチャ表示のサンプル ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

63

7.2.8

文字送り ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

64

第8章

モデルの制御 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

66

8.1

形状の代入・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

66

8.2

色の設定 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

67

8.3

描画モードと描画状態の制御・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

68

8.4

線や点の色付け (マテリアル)・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

69

8.5

線の太さや点の大きさの制御・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

69

8.6

スムースシェーディング・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

70

8.7

モデルの位置と姿勢 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

70

8.8

グローバル座標系とローカル座標系・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

71

8.9

モデルの位置と姿勢の参照 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

72

8.10

平行移動による制御 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

72

8.10.1glTranslate ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

73

8.10.2

loTranslate ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

73

8.10.3glMoveTo ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

74

方向ベクトルとアップベクトルの制御 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

74

8.11.1

glFocus ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

75

8.11.2

loFocus ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

75

8.11.3

glVec・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

75

8.11.4

glUpvec ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

76

8.11.5

loUpvec ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

76

オイラー角による姿勢の制御・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

76

7.2

8.11

8.12

iii

8.12.1

glAngle ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

76

8.12.2

loAngle ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

77

回転による制御 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

78

8.13.1

glRotate と glRotateWithVec・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

78

8.13.2loRotate と loRotateWithVec・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

79

8.14

モデルの拡大縮小・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

79

8.15

モデルの親子関係と継承・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

80

8.15.1モデル親子関係の概要 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

80

8.15.2

親子関係とモデル情報取得 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

81

親子関係とグローバル座標系・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

82

親子関係に関する関数 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

82

第9章

モデルや光線の干渉・衝突判定 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

84

9.1

境界ボリューム ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

84

9.1.1

境界球・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

84

9.1.2

軸平行境界ボックス (AABB) ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

84

9.1.3

有向境界ボックス (OBB) ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

85

9.1.4

カプセル型 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

85

モデル同士の干渉判定 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

86

9.2.1

干渉判定の基本 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

86

9.2.2

干渉継続モード ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

87

9.2.3

干渉自動停止モード・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

87

9.3

モデル同士の衝突判定 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

88

9.4

光線とモデルの干渉判定・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

88

9.4.1

fk Ray による光線設定・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

89

9.4.2

光線とモデルの干渉判定・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

89

第 10 章シーン ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

91

10.1

モデルの登録 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

91

10.2

カメラ (視点) の設定 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

92

10.3

背景色の設定 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

92

10.4

透過処理の設定 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

92

10.5

霧の効果 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

93

10.5.1

霧効果の典型的な利用方法 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

93

10.5.2

霧効果の詳細な利用方法・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

93

10.6

オーバーレイモデルの登録 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

94

第 11 章ウインドウとデバイス・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

95

11.1

ウィンドウの生成・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

95

11.2

ウィンドウの描画・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

95

11.3

座標軸やグリッドの表示・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

96

11.4

デバイス情報の取得 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

97

11.4.1getKeyStatus() メンバ関数 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

97

11.4.2

特殊キーの状態取得・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

97

11.4.3

getMousePosition() 関数・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

98

ウィンドウ座標と 3 次元座標の相互変換 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

99

3 次元座標からウィンドウ座標への変換・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

99

8.13

8.16
8.16.1

9.2

11.5
11.5.1iv

11.5.2
11.6

ウィンドウ座標から 3 次元座標への変換 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・

99

高度なウィンドウ制御 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ 101

11.6.1

ウィンドウの生成 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ 101

11.6.2

シーンの設定・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ 102

11.6.3ウィンドウの描画 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ 102

11.6.4

デバイス情報取得と座標系変換・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ 103

11.6.5ウィンドウ表示状態の画像情報取り込み ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ 103

11.6.6ウィンドウのサイズ変更・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ 104

11.6.7

テクスチャメモリの解放・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ 105

11.6.8

メッセージ出力 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ 105

第 12 章簡易形状表示システム・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ 107

12.1

形状表示 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ 107

12.2

標準機能 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ 108

12.3

fk ShapeViewer のメンバ関数 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ 108

第 13 章サンプルプログラム ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ 112

13.1

基本的形状の生成と親子関係・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ 112

13.2

LOD 処理とカメラ切り替え ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ 114

13.3

FLTK を用いた GUI 構築・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ 119

13.4

マルチウィンドウ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ 126

13.5

形状の簡易表示とアニメーション ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ 132

13.6

パーティクルアニメーション・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ 133

付録 A. マテリアル一覧・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ 137

v

はじめに

この文章で述べられている FK (Fine Kernel) System は、容易にインタラクティブな 3D 空間を表現するための Tool
Kit である。
ここでいう Tool Kit とは、システムを構築する際に用いられる簡易インターフェースをプログラミング言語から呼び出す形で実現されたもののことをいう。平易な言葉で述べるなら、この FK System を用いれば簡単にインタラクティブな
3D の世界を創造することが可能であるということである。普通、なんらかのシステム構築の際には本質的でない部分に労力をさかねばならないことは周知のとおりである。それは、ときには学習であったり、ときには作業であったり、ときには試行錯誤であったりする。ツールキットは、それらをユーザに代わって肩代りをし、より本質的な部分にのみユーザが没頭することを助ける役割を持つ。
FK System が、3D 空間の作成をサポートすることは前述したが、大きな理念としての柱が幾つかある。それを列挙す
ると、• オブジェクト指向概念の採用。
• モデルに対する制御の柔軟性。
• 形状の容易な定義や変形。
• 複雑な座標系処理の簡便化。
• ディスプレイリストの概念。
• インターフェースの柔軟な構築。
• 汎用性と高速描画の両立。
• 環境との非依存。
といった事柄を特に重要視して設計が行われている。
これらの概念を、我々はまず C++ 言語を用いたクラスライブラリとして実現した。さらに Ver.3 では、この C++ 版をベースとして CLI による実装も追加した。CLI 版を用いることで、C++ 版とほぼ同一の機能を C# や F# といった.NET
対応言語で開発することが可能となった。C++ と C# の両方に共通な 3D フレームワークはあまり多くはなく、両方の言語をシームレスに扱うことができるという点が、FK System のユニークな点である。また、F# のような関数型言語においては 3D プログラミングフレームワークはあまり普及しておらず、関数型プログラミングを用いた 3D プログラミングを行いたい場合で、FK は大変有用なライブラリとなるであろう。
本書は、全 13 章で構成されている。
第 1 章では、FK System の基本的な考え方を理解するため、簡単なサンプルを用いて機能を紹介していく。
第 2 章では、FK システムで準備された三次元座標値や三次元ベクトルに関しての扱い方を述べる。座標やベクトルは、
特に第 4、8 章の内容と著しく関わる。形状は、もちろん三次元座標で表現されるし、モデルの挙動の制御にはベクトルや座標を多用するからである。
第 3 章では、マテリアルと呼ばれるカラー属性に関して述べる。これは、形状や光源に対して色を含む質感を設定する際に用いられるパラメータのことである。非常に細かな設定が可能であるが、簡易な使用法もあることをここでは述べている。
第 4 章では、基本形状の生成法を中心に述べる。さらに、第 5 章では任意形状の動的な生成や変形方法に関して解説する。
第 6 章では、FK の中で画像表示およびテクスチャマッピングを行うための方法について述べる。さらに、特別なテクス
チャマッピングとして「文字列テクスチャ」を第 7 章で解説する。画面上に文字を表示したい場合には、この 2 つの章を参1

考にしてほしい。
第 8 章では、FK のプログラミングで中心的な役割を果たす「モデル」についての解説を行う。モデルに対してマテリア
ルや形状の設定、移動や描画の有無などを行うことになる。
第 9 章では、モデル同士の干渉・衝突判定の方法を解説する。また、光線とモデルの干渉についても解説する。
第 10、11 章は、シーンとウィンドウについて解説する。描画対象とするモデルの登録や解除を行うものである。また、複数のシーンを使い分ける方法についても解説する。
第 12 章では、簡易な形状描画手段に関しての解説を述べる。
最終章は、全体を通しての様々なトピックやエッセンス、そして簡単な例題を掲載する。

2

第1章

さあはじめよう

一般的に、3 次元コンピュータグラフィックス (以下 3DCG) のために書かれたソースコードは、かなり長くなることが多い。ただ単に直方体が回転しているプログラムを書くために、500 行以上必要な場合もある。むしろ、そうでないプラットフォームの方が珍しい。何故か？
何故なら、3DCG アプリケーションには考えなければならない要素がとても多いからである。先ほど例に出した直方体の回転に関しても、次のような要素を考慮する必要がある。
•「直方体」をどうやって生成するか？
• 回転をどうやって実現するか？
• 立体の色はどうするのか？
• 背景色はどうするのか？
• ウィンドウをどうやって作成するのか？
• 作成したウィンドウにどうやって表示するのか？
• アニメーションをどうやって実現するのか？
• アニメーションしている間、マウスやキーボードをどう扱うのか？
これらを全てプログラムソースとして書いていくと、すぐに 500 行にも 1000 行にも簡単に達してしまう。
FK ツールキット (以下 FK) は、このような状況を打破するために産み出された。複雑に絡んでいる各要素を整理し、オブジェクト指向の概念を利用して極力簡略化できるように設計されている。実際に、直方体を回転させるプログラムを記述してみる。

1.1 直方体回転のサンプルプログラム
ソースコード 1.1 直方体回転サンプル
1# include < FK / FK .h >2
3using namespace FK ;4
5

int main ( int , char **)

6

{

7

fk_Blockblock (10.0 , 20.0 , 15.0);

8

fk_Modelmodel ;

9

fk_AppWindow

window ;1011// 色 パ レ ッ ト の 初 期 化

12

fk_Material :: initDefault ();13
14// モ デ ル に 直 方 体 を 設 定

15model . setShape (& block );16
17// モ デ ル の 色 を 黄 色 に 設 定

18model . setMaterial ( Material :: Yellow );1920// カ メ ラ の 位 置 と 方 向 を 設 定

3

21

window . setCameraPos (0.0 , 0.0 , 100.0);

22

window . s etCame raFocu s (0.0 , 0.0 , 0.0);

23
24// ウ ィ ン ド ウ に モ デ ル を 登 録

25

window . entry (& model );26
27// ウ ィ ン ド ウ の サ イ ズ を 設 定

28

window . setSize (800 , 800);

29
30// ウ ィ ン ド ウ を 開 く

31

window . open ();32
33

while ( window . update () == true ) {34
35// 直 方 体 を Y 軸 を 中 心 に 回 転 さ せ る 。

36model . g lR o ta te W it hV e c (0.0 , 0.0 , 0.0 , fk_Axis :: Y , 0.01);

37

}

38
39

return 0;}

各処理の詳細な解説は次章以降に譲るとして、ここでは大きな流れを見ていく。

1.2 4 つの “レイヤー”
プログラムの実際の中身を分析する前に、FK の根幹をなす 4 種類の “レイヤー” である「形状」、「モデル」、「シーン」、「ウィンドウ」を簡単に解説する。
「形状」は、文字通り立体形状を表す。FK では、形状として直方体、球、平面、円盤、線分、点など様々なものを、変数を 1 つ定義するだけで作成することができる。また、様々な 3 次元データファイル形式を入力することもできる。もちろん、その場合も「形状」を表すには変数を 1 つ準備するだけでよい。
「モデル」は、形状に対して位置や方向などを持たせた存在である。「形状」と「モデル」の概念が分離しているのには理由がある。例えば、同じ形状を持つ 100 台の車のカーチェイスゲームを想定してみよう。このとき、100 台分全てのデータをメモリ上に確保するのは大変無駄である。しかし、100 台の車は位置も方向も速度も、おそらく色も違うことであろう。
したがって、これらは別々に存在していなければならない。こんなときに「モデル」の概念が役立つ。まず「形状」として1 個の車体を準備し、100 個の「モデル」を準備する。各モデルは形状として先ほどの車体を設定し、それぞれ固有の位置や方向や色を持てば良い。これで、データ量の節約と 100 台の車の存在を両立することができる。また、モデルは瞬時に設定する形状を変更することができるので、形状を入れ替えることで変形アニメーションを簡単に実現することもできる。
「シーン」は、複数のモデルと 1 つのカメラから成り立っており、全体で 1 つの “空間” を表現する。ここには、実際に描画するモデルを全て登録しておく。最後に紹介する「ウィンドウ」はキャンバスのようなもので、ここに「シーン」を設定することで “空間” が実際に描写される。「シーン」と「ウィンドウ」は完全に独立した存在なので、ウィンドウに描画されるシーンを簡単に切り替えたり、逆に複数のウィンドウに同じシーンを描画することも簡単にできる。

1.3 プログラムの概要
次に、実際にサンプルプログラムの解説を述べる。
FK でプログラムを記述する場合、まず 1 行目にあるように「#include <FK/FK.h>」を追加しておく。その後の 3 行目にある「using namespace FK;」は「FK 名前空間の就職を省略する」という意味である。FK のクラスは全て「FK 名前空間」で定義されており、本来であれば例えば fk Model クラスであれば「FK::fk Model」と記述する必要があるが、using 構
文で「FK::」を省略することが可能となる。名前空間に関する詳細は C++ での解説書を参照してほしい。

4

以降、本書では全て「using namespace FK;」を冒頭で用いていると仮定して解説を進める。この宣言を行いたくない場
合は、FK 関係のクラスや定数はすべて頭に「FK::」を付けて読み替えてほしい。
7 〜 9 行目で用意している変数は、それぞれ次のような意味を持っている。
表 1.1 変数の意味

変数名

解説

block

直方体形状を表す変数。

model

直方体の「モデル」を表す変数。

window ウィンドウを表す変数。
変数を準備することは、その時点でそのクラスが表現する「もの (オブジェクト)」を作成することだと考えてくれればよい。例えば、5 行目の直方体変数の宣言は、この記述によって直方体を生成したということになる。他の変数、例えばモデルやウィンドウも全て変数の定義の時点で生成される。あとは、これらオブジェクトに対して適切な設定を行っていけばよい。
12 行目の記述は、様々な色 (マテリアル) を初期化するための関数で、これは何か色設定を行う前に記述しておく必要がある。
15 行目はモデル「model」に対して形状を設定している部分である。ここでは形状として「block」を設定している。
18 行目では、モデルの色として「Yellow」を採用している。ちなみに、デフォルトでは灰色が設定されている。
21,22 行目は、カメラに対して位置と方向を設定している。21 行目の「setCameraPos」関数は、カメラの位置を指定する関数である。また、22 行目の「setCameraFocus」はカメラの被写体の位置 — これは CG 用語で「注視点」とか「注目点」などと呼ばれている — を指定する関数である。従って、ここではカメラ位置を (0, 0, 100) に置き、原点の方向を向いていることになる。
25 行目は、準備したモデルをウィンドウに登録している部分である。FK では、形状やモデルは単に準備しただけでは表示対象とはならず、このようにウィンドウに登録して初めて実際に描画されるようになる。
28 行目はウィンドウのサイズをピクセル単位で指定するものである。今回のプログラムではモデルやカメラの設定後に行ったが、変数を作成した時点でいつでも設定が可能である。
31 行目は window を実際に描画する関数である。この関数を呼んだ時点ではじめてウィンドウが実際に画面に現れる。
33 〜 37 行目は while ループとなっており、これがプログラムの「メインループ」となる。33 行目の while 文の中にある「window.update()」は現在の各モデル等に設定された情報に従い、3D シーンを再描画するものである。while 文中で各モデルの変化を記述していくことで、アニメーションプログラムが実現されている。なお、「update()」関数は正常に表示されている場合は true を返し、表示されていない場合に false を返す仕様となっている。従って、この while ループはウィ
ンドウが閉じられた場合に終了する仕組みとなる。
36 行目では、直方体を持つモデル「model」を y 軸を中心に回転させている。「glRotateWithVec」関数は、モデルを回転∼ 0.57◦ ずつ回転させている。
させる関数である。ここでは、原点を中心に 0.01 ラジアン =1.4 作成できる “形状” の種類
FK 中で作成できる基本的な「形状」には、現在次のようなものが用意されている。

5

表 1.2 形状を表すクラス群
形状

クラス名

必要な引数

点

fk Point

位置ベクトル線分

fk Line

両端点の位置ベクトル

ポリライン

fk Polyline

各頂点の位置ベクトル

閉じたポリライン

fk Closedline

各頂点の位置ベクトル

多角形平面

fk Polygon

各頂点の位置ベクトル円

fk Circle

分割数、半径直方体

fk Block

縦、横、高さ球

fk Sphere

分割数、半径角柱 (円柱)fk Prism

角数、上面と底面の内接円半径、高さ
角数、底面の内接円半径、高さ

角錐 (円錐)fk Coneインデックスフェースセット

fk IndexFaceSet

ファイル名等矩形テクスチャfk RectTexture

画像ファイル名

三角形テクスチャ

fk TriTexture

画像ファイル名

メッシュテクスチャ

fk MeshTexture

画像ファイル名

IFS テクスチャ

fk IFSTexture

画像ファイル名

文字列板

fk TextImage

文字列またはテキストファイル

パーティクル

fk ParticleSet

様々な設定

光源

fk Light

タイプ

これらの変数を定義するときは、最初に初期値として様々な設定を行うことになる。例えば fk Point 型、つまり空間上の「点」を表す変数を定義するとき、その直方体の各辺長を次のようにして設定することができる。
fk_Blockblock (10.0 , 5.0 , 20.0);

このように、各形状クラスにはそれぞれ初期設定の方法が用意されている。具体的な設定項目については第 4 章で詳しく述べている。
例えば、サンプルプログラムで回転する形状を直方体ではなく円盤にしたいのであれば、7 行目の直方体の部分を
fk_Circle

circle (4 , 20.0);と変更し、15 行目の block を circle に変更するだけでよい。

1.5 モデルの制御
FK では、モデルに対して非常に豊富な機能を提供している。FK に限らず、一般的な 3DCG のプログラム中で最も多くの作業を必要とするのがこのモデルの制御である。大抵の 3D プログラミング環境では、座標軸回りの回転、平行移動、拡大縮小といった限られた命令セットしか準備されていないことが多い。プログラマはこれらを巧みに利用してモデルを制御することになるが、この部分の実現が思いの外難しい。というのも、実現には非常に難解な数式処理を必要とするからである。FK は、プログラマがそのような数学をあまり意識することなくモデルを扱う方法を何種類も提供し、サポートしている。詳細は第 8 章に全て網羅してあるので、ここではダイジェストとして一部機能を紹介する。

fk_Modelmodel ;// (50 , 10 , -20) へ 移 動
model . glMoveTo (50.0 , 10.0 , -20.0);// (10 , 20 , 0) だ け 平 行 移 動
model . glTranslate (10.0 , 20.0 , 0.0);// (0 , 0 , 100) の 方 を 向 か せ る

6model . glFocus (0.0 , 0.0 , 100.0);// モ デ ル の 向 き を (1 , 1 , 1) に す るmodel . glVec (1.0 , 1.0 , 1.0);// モ デ ル の 位 置 を (0 , 10 , 0) を 中 心 に x 軸 方 向 に// 0.1 ラ ジ ア ン 回 転 し た 位 置 に 移 動 す る ( 向 き は そ の ま ま )model . glRotate (0.0 , 10.0 , 0.0 , fk_Axis :: X , 0.1);// GlRotate の 機 能 に 加 え 、 さ ら に 向 き も 回 転 さ せ る
model . g lRota te W it hV e c (0.0 , 10.0 , 0.0 , fk_Axis :: X , 0.1);

また、モデルには「継承関係」というモデル同士の関係を形成することができる。これは、複数のモデルをある 1 つのモデルに属した関係にするもので、FK の中では前者を「子モデル」、後者を「親モデル」と呼んでいる。親モデルを動かすと、それに従って子モデルも動いていく。従って、この機能は複数のモデルを 1 つのモデルのように扱いたい場合に効果を発揮する。具体的な応用としては第 13 章の各サンプルが例として挙げられる。

1.6 カメラと光源
fk AppWindow クラスは、初期状態でカメラと光源が設定されている。カメラの制御方法としては、前述したプログラ
ムのような方法の他に、fk Model 型変数をカメラとして扱うこともできる。詳細は第 11 章で述べる。
光源については、デフォルトでは (0, 0, −1) 方向の平行光源が設定されている。これに対し、別の方向からの平行光源を設定したい場合や、点光源などを設定したい場合は f k Light というクラスを用いて光源を作成し、それを形状としてモデルに設定し、fk AppWindow にモデルを登録するという手順を取る。詳細は第 4 章の光源に関する節で説明する。

1.7 シーン「シーン」とは、一般的には描画すべき要素の集合のことを指す。FK における「シーン」とは、モデルのデータベースとなっており、意味的には空間全体を成すものである。従って、あるモデルを描画するかどうかはシーンに対して対象モデルを登録したり抹消すればよい。
fk AppWindow では、最初から一つのシーンが内部に登録されており、そこへの登録は fk AppWindow の「entry()」関数で行うことができる。また、抹消は「remove()」によって行う。
一方、アプリケーションによっては複数のシーンを使い分けたい場合がある。異なるシーンをそれぞれ保持しておき、状況によってウィンドウに表示するシーンを切り替えるような場合である。そのような機能を実現する手段として、FKでは「fk Scene」というクラスが用意されている。このクラスはシーン中に表示するモデルの登録や抹消、そしてカメラを管理するものであり、このクラスの変数を複数個用意することで、複数のシーンを容易に切り替えることができる。また、マルチウィンドウアプリケーションを作成する場合にも利用することになる。さらに、シーンには霧効果の設定など、
fk AppWindow よりも高度なシーン管理機能を利用することができる。これらについては、第 10 章で詳しく解説する。

1.8 ウィンドウと GUI
FK は、元々 OpenGL と FLTK というシステムを基盤に構築されている。このうち、OpenGL は 3D 描画のための機能であり、FLTK はグラフィカルユーザインターフェース (GUI) を作成するためのツールである。FK は、FLTK との強い親和性を意識して設計されており、FLTK の GUI 機能をそのまま利用することができる。その一例が第 13 章の「FLTKを用いた GUI 構築」というサンプルに示されている。

7

1.9 デバイス状態取得
多くのリアルタイムアプリケーションでは、マウスやキーボードなどによるリアルタイムな操作を必要とすることが多い。FK でも、現時点でマウスの位置やボタン状態、キーボードの情報などをウィンドウオブジェクトから取得することができる。また、(やや高度なトピックになるが) どの形状、どの頂点、どの面がピックされたかを取得する機能も提供されている。これらの機能は、モデラーなどを作成する際には必須の機能である。これらに関する事項は、第 11 章を中心に記述されている。

1.10 次の段階は.....
以上が、FK の大体の概要説明である。FK は、もともとコンテンツ作成支援と CG 研究支援の両方を目的としているため、ここでは紹介できないかなり専門的な機能もある。
もし、読者が CG、数学、プログラムの全てに初心者意識があるのならば、次の順番に読み進めることをお勧めする。
13 → 4 → 3 → 5 → 8 → 9 → 10 → 11 → 12 → 2 → 13
ある程度の CG プログラミングの経験があるのならば、次の順番で読み進めるのが効率がよいだろう。
2 → 3 → 4 → 5 → 8 → 9 → 10 → 11 → 12 → 13
なんにしろ、読み方は自由である。各自で効果的な学習を試みてほしい。

8

第2章

ベクトル、行列、四元数 (クォータニオン)

この章では、ベクトル、行列、四元数、乱数といった数学的基本クラスの利用方法を紹介する。

2.1 3 次元ベクトル
この節では、fk Vector と呼ばれるベクトルや座標を司るクラスに関して述べる。ベクトルは、単なる浮動小数点数が 3
つならんでいるという以上に多くの意味を持つ。例えば、ベクトルは加減法、実数との積や内積、外積といった多くの演算をほどこす必要が度々現われる。そのため、fk Vector 型は大きく 2 つの役割を持っている。ひとつは、そのようなベクト
ルの演算をプログラムの中で比較的容易に実現することをサポートすることであり、もうひとつは形状やモデルの挙動を制御するための引数として扱うことである。この節では、特に前者について述べている。後者に関しては 4 章 〜 8 章で順次説明していく。この節での真価は、13 章のサンプルで様々な形で現われている。

2.1.1 ベクトルの生成・設定
ベクトルの生成はいたって単純に行われる。fk Vector 型の変数を定義すればよい。

fk_Vector vec ;このとき、vec 変数は初期成分として (0, 0, 0) が代入される。また、初期値を最初から代入することも可能である。

fk_Vector vec (1.0 , 1.0 , -3.0);

この記述は、vec に (1, 1, −3) を代入する。もちろん、生成後の代入も可能である。次のように記述すればよい。

fk_Vector vec1 , vec2 ;

vec1 . set (1.0 , 1.0 , -3.0);
vec2 . set (5.0 , -2.5);

set メンバ関数が、vec1 や vec2 に値を代入してくれる。vec2 のように引数が 2 個しか無い場合は、z 成分には自動的に0 が代入されるため、結果的に vec2 に (5, −2.5, 0) が代入されることになる。また、単にvec . init ();と記述した場合、vec はゼロベクトル (0, 0, 0) となる。

9

ベクトル中の x, y, z の各成分はすべて public メンバとなっているので、直接参照や代入を行うことが可能である。たとえば、z 要素が正か負かを調べたいときは、

if ( vec . z < 0.0) {// 後 処 理}と書けばよい。代入に関しても、以下のような記述が可能である。

vec . x = tmpX ;vec . y = tmpY ;vec . z = tmpZ ;ベクトルを配列として定義した場合も、もちろん各成分を直接扱うことが可能である。以下に例を示す。

fk_Vector vec [10];for ( int i = 0; i < 10; i ++) {vec [ i ]. x = double ( i );vec [ i ]. y = double ( - i );vec [ i ]. z = 1.0;}

2.1.2 比較演算子
fk Vector 型は比較演算子として等しいかどうかを判定する ‘==’ と、その否定 ‘!=’ を利用することができる。これらは、
通常の int 型の変数の場合と同様に if 文中で利用でき、演算結果が bool 型として返る点でも通常の比較演算と同様である。このとき注意しなければならないのは、この比較演算はある程度の数値誤差を許していることである。具体的に述べると、もし vec1.y と vec2.y、vec1.z と vec2.z の値がともに等しいとして、vec1.x と vec2.x が 10−14 程度の差が存在しても、(vec1 == vec2) は真 (true) を返すのである。現在、この許容誤差は 10−12 に設定されている。ちなみに、偽の場合は falseを返す。

2.1.3 単項演算子
fk Vector 型は、単項演算子 ‘-’ を持っている。これは、ベクトルを反転したものを返すもので、例えば、

vec2 = - vec1 ;という記述は vec2 に vec1 を反転したものを代入する。この際、vec1 の値そのものは変化しない。これは、通常の int 型
や double 型の変数の場合と同じ挙動であるといえる。

10

2.1.4 二項演算子
fk Vector の二項演算子は多様であり、どれも実践的なものである。表 2.1 にそれらを羅列する。
表 2.1 fk Vector の二項演算子

演算子

形式

機能

返り値の型

+

V ector + V ector

ベクトルの和

fk Vector-

V ector − V ector

ベクトルの差fk Vector*

double ∗ V ector

ベクトルの実数倍

fk Vector*

V ector ∗ double

ベクトルの実数倍

fk Vector/

V ector / double

ベクトルの実数商

fk Vector*

V ector ∗ V ector

ベクトルの内積

doubleˆV ectorˆV ector

ベクトルの外積

fk Vector

2.1.5 代入演算子
fk Vector 型は代入演算子 ‘ = ’ を使用することができる。このとき、実際は中の値のコピーが行われるのであって、ポインタが代入されるわけではない。したがって、

fk_Vector vec1 ;fk_Vector vec2 (1.0 , 1.0 , 1.0);

vec1 = vec2 ;
vec2 . x = 3.0;という記述は vec1.x の値を変更しない。もちろん、

fk_Vector * pointer ;fk_Vector vec2 (1.0 , 1.0 , 1.0);

pointer = & vec2 ;pointer - > x = 3.0;

といった記述は可能であり、この場合の vec2.x の値は 3.0 に変更される。
その他の代入演算子として、次のようなものが使用できる。効果は、表 2.2 の右に掲載された式と同様の働きである。なお、効果で left は左辺、right は右辺を指す。

11

表 2.2 fk Vector の代入演算子

演算子

形式

効果

+=

V ector += V ector

left = left + right-=

V ector -= V ector

left = left - right*=

V ector *= double

left = left * right/=

V ector /= double

left = left / right2.1.6 ノルム (長さ) の算出
ベクトルのノルム (長さ) を出力する関数として dist() が、ノルムの 2 乗を指す dist2() があり、それぞれ double 型を返す。使用法は次のようなものである。

double l = vec1 . dist ();double l2 = vec1 . dist2 ();printf ( " length ␣ = ␣ % lf \ n " , l );printf ( " length ˆ2 ␣ = ␣ % lf \ n " , l2 );

処理速度は、dist2() の方が dist() よりも高速である。従って、ただ単にベクトルの長さを比較したいだけならば dist2()を利用した方が効率的である。

2.1.7 ベクトルの正規化
正規化とは、零ベクトルでない任意のベクトル V に対し、
V′ =

V
|V|

を求めることである。V′ は、結果的には V と方向が同一で長さが 1 のベクトルとなる。3 次元中の様々な幾何計算や、コンピュータグラフィックスの理論では、しばしば正規化されたベクトルを用いることが多い。
fk Vector 型の変数に対し、自身を正規化 (Normalize) する関数として normalize() がある。

fk_Vector vec1 (5.0 , 4.0 , 3.0);

vec1 . normalize ();という記述は、vec1 を正規化する。ちなみに、normalize() 関数は bool 型を返し、true ならば正規化の成功、false ならば失敗を意味する。失敗するのは、ベクトルが零ベクトル (つまり長さが 0) の場合に限られる。

2.1.8 ベクトルの射影
ベクトルを用いた理論では、射影と呼ばれる概念がよく用いられる。射影とは、図 2.1 にあるようにあるベクトルに対して別のベクトルを投影することである。ここで、図 2.1 の Pproj を「P の Q に対する射影ベクトル」と呼ぶ。

12

P
Pperp
Pproj
Q
図 2.1 ベクトルの射影

この射影ベクトルを求める方法として、fk Vector では proj() メンバ関数を利用することができる。

fk_Vector

P , Q , P_proj ;P_proj = P . proj ( Q );また、投影の際の垂直成分 (図 2.1 中での Pperp ) を求めるときは、perp() というメンバ関数を利用する。
P_perp = P . perp ( Q );2.2 行列
この節は、FK System の持つ行列演算に関して紹介する。行列を用いることによってよりプログラムの記述が洗練されたり、あるいは高速な実行を可能にする。なお、この節の解説は線形代数、特に 1 次変換の知識があることを前提としている。初学者は、必要となるまで読み飛ばしてもらっても差し支えない。

2.2.1

FK における行列系

FK システムでは「MV 行列系」という行列系を採用している。これは、行列とベクトルの演算を以下のように規定するものである。
• ベクトルは、通常列ベクトルとして扱う。
• 行列とベクトルの積は、通常左側に行列、右側にベクトルを置くものとする。
• 行列の積が生成された場合に、ベクトルに先に作用するのは右項の行列である。
一般に、行列を扱う数学書においては、上記の MV 行列系を前提として記述されているものがほとんどであり、それらの理論を参考にするのに都合が良いと言える。一方で、3DCG の開発システムにおいて、MV 行列系とは逆の「VM 行列系」
を採用しているものもあり1）、それらの理論や実装を参考にする際には注意が必要である。

2.2.2 行列の生成
一般的に、3DCG の世界で頻繁に用いられる行列は 4 行 4 列の正方行列である。これは、4 行 4 列であることによって、3 次元座標系の回転移動、平行移動、線形拡大縮小を全て表現できるからである。FK System において、4 行 4 列の正方行
1） 例えば、マイクロソフト社の「Direct3D」は行列系として VM 系を採用している。

13

列は fk Matrix という型で提供されている。
fk Matrix 型は、定義時には単位行列 (零行列ではない) が初期値として設定される。fk Matrix は、fk Vector のように初期値設定の手段を持たない。そのかわり、多様な設定方法が存在する。表 2.3 はそれをまとめたものである。
表 2.3 fk Matrix の初期値設定用メンバ関数

メンバ関数名と引数

引数の意味

効果

makeRot(double, fk Axis)

角度数と軸回転行列の生成makeTrans(double, double, double)

x, y, z に対応する実数値

平行移動行列の生成

makeTrans(fk Vector)

ベクトル

上記に同じ

makeScale(double, double, double)

x, y, z に対応する実数値

拡大縮小行列の生成

makeEuler(double, double, double)

オイラー角に相当する実数値

オイラー回転行列の生成

makeEuler(fk Angle)

オイラー角

上記に同じ

makeRot 関数は、2 つ目の引数に x 軸を表す fk X、y 軸を表す fk Y、z 軸を表す fk Z のいずれかをとる。最初の引数である実数値は弧度法 (rad ラジアン) として扱われる。180◦ = π rad である。FK システムでは π を表す定義値としてfk Math::PI を提供している。したがって、たとえば 90 度は fk_Math::PI/2.0 と表せばよい。
makeEuler 関数は引数にそれぞれ順に heading 角、pitch 角、bank 角を与える。単位は makeRot 関数と同様に弧度法である。fk Angle 型は、オイラー角を表わすクラスで、heading 角、pitch 角、bank 角にあたるメンバはそれぞれ h, p, b となっており、public アクセスが可能である。

2.2.3 比較演算子
fk Matrix 型も、fk Vector 型のような ‘==’ 演算子と ‘!=’ 演算子を持ち合わせている。これらは、やはり許容誤差を持って判定される。

2.2.4 単行演算子
fk Matrix 型は、単項演算子 ‘!’ を持っている。これは逆行列を返すものであり、自身に変化は起こさない。以下のプロ
グラムは、行列 A の逆行列を B に代入するものである。

fk_Matrix

A, B;

B = !A;2.2.5 二項演算子
fk Matrix 型の二項演算子は表 2.4 の通りのものが用意されている。
表 2.4 fk Matrix の二項演算子

演算子

形式

機能

返り値の型

+

M atrix + M atrix

行列の和

fk Matrix-

M atrix − M atrix

行列の差

fk Matrix*

M atrix ∗ M atrix

行列の積

fk Matrix*

M atrix ∗ V ector

ベクトルと行列の積

fk Vector

14

2.2.6 代入演算子
fk Matrix 型の持つ代入演算子は、単純代入演算子として ‘=’ を持っており、その挙動は fk Vector 型とまったく同様である。その他の代入演算子も用意されており、それは表 2.5 のようなものである。記述法は、表 2.2 と同様である。
表 2.5 fk Matrix の代入演算子

演算子

形式

効果

+=

M atrix += M atrix

left = left + right-=M atrix -= M atrix

left = left - right*=M atrix *= M atrix

left = left * right*=M atrix *= V ector

left = right * left

2.2.7 各成分へのアクセス
行列成分へのアクセスは、配列演算子を用いる。これは 2 次元で定義されており、1 次元目が行を、2 次元目が列を表している2）。

fk_Matrix mat ;mat . makeEuler ( fk_Math :: PI /2.0 , fk_Math :: PI /4.0 , fk_Math :: PI /6.0);
printf ( " mat [1][0] ␣ = ␣ % lf \ n " , mat [1][0]);2.2.8 その他のメンバ関数
以下に、fk Matrix で用いられる主要なメンバ関数を紹介する。
void init(void)
自身を単位行列にする。

void set(int r, int c, double a)
行番号が r、列番号が c に対応する成分の値を a に設定する。

void setRow(int r, fk Vector v)
void setRow(int r, fk HVector v)
行番号が r である行ベクトルを v の各成分値に設定する。

void setCol(int c, fk Vector v)
void setCol(int c, fk HVector v)
列番号が c である列ベクトルを v に設定する。

fk HVector getRow(int r)
行番号が r である行ベクトルを取得する。

2） 正確に述べると、配列の 1 次元目は行列の各行の先頭のアドレスを返す。

15

fk HVector getCol(int c)
列番号が c である列ベクトルを取得する。

bool isSingular(void)
自身が特異行列であるかどうかを判定する。特異行列とは、逆行列が存在しない行列のことである。特異行列である場
合は true を、そうでない場合は false を返す。

bool isRegular(void)
自身が正則行列であるかどうかを判定する。正則行列とは、逆行列が存在する行列のことである。(つまり、「非正則行列」と「特異行列」はまったく同じ意味になる。) 正則行列である場合は true を、そうでない場合は false を返す。

bool inverse(void)
自身が正則行列であった場合、自身を逆行列と入れ替えて true を返す。特異行列であった場合は、false を返し自身は変化しない。

void negate(void)
自身を転置する。転置とは、行列の行と列を入れ替える操作のことである。

2.2.9

4 次元ベクトル

fk Matrix は 4 行 4 列の正方行列であるため、本来であれば、fk Matrix 型に対応するベクトルは 4 次元でなければならない。しかし、fk Vector 型は 3 次元であるため、そのままでは積演算ができないことになる。そのため、FK では 4 次元ベ
クトル用クラスとして fk HVector クラスがあり、実際の行列演算は fk HVector を用いて行うという仕組みになっている。
fk HVector クラスは fk Vector クラスの派生クラスであり、4 次元目の成分「w」を持つ。この成分は double 型の public
メンバとして定義されており、自由にアクセスすることができる。座標変換においては、w 成分は同次座標を表わすことを想定している。同次座標は通常 1 で固定されるが、「射影幾何学」と呼ばれる数学分野においては、この同次座標を操作す
る理論もある。
fk Matrix 型と fk Vector 型の積演算は、実際には以下のような処理が FK 内部で行われる。
1. まず、fk Vector 型変数に対し fk HVector 型に暗黙の型変換が行われる。
2. 変換後の fk HVector オブジェクトと fk Matrix による積を算出し、その結果が fk HVector 型として返される。
3. 返り値である fk HVector オブジェクトから、暗黙の型変換によって fk Vector 型変数に代入される。
この仕組みにより、FK の利用者が通常の行列演算において fk HVector の存在を意識する必要はない。しかし、あえて
fk HVector を利用することによる利点もある。
4 行 4 列の行列は、回転等による姿勢変換と平行移動変換を、行列同士の積演算を行うことによって同時に内包することができる。物体の位置や形状頂点などの位置ベクトルに関しては、同次座標が 1 であることによってそのまま変換が可能であるが、モデルの姿勢等の方向ベクトルに関しては同次座標が 1 のまま変換を行うと間違った結果を生じてしまう。これは、方向ベクトルの変換に関しては姿勢変換のみが適用されるべきであるのに対し、平行移動も適用してしまうからである。
このようなとき、同次座標を 0 に設定したベクトルで処理を行うとよい。それにより、行列中の平行移動用成分 (4 列目)が結果に作用しなくなり、姿勢変換のみによる結果を得ることが可能となる。このような処理を実現するためには、単純にw メンバに 0 を代入するだけで可能であるが、一応専用のメンバ関数も準備されている。ispos() メンバ関数は同次座標を1 に設定し、平行移動変換を有効とする。isvec() メンバ関数は同次座標を 0 に設定し、平行移動変換を無効とする。
上記のような処理をプログラム中で実現したい場合は、fk HVector クラスを利用するとよいだろう。

16

2.3 四元数 (クォータニオン)
四元数 (クォータニオン) は、3 種類の虚数単位 i, j, k と 4 個の実数 s, x, y, z を用いて
q = s + xi + yj + zkという形式で表現される数のことであり、発見者のハミルトンにちなんで「ハミルトン数」と呼ばれることもある。この節では、FK 中で四元数を表わすクラス「fk Quaternion」の利用方法を述べる。ただし、四元数の数学的定義や、オイラー
角、行列と比較した長所と短所に関してはここでは扱わない。適宜参考書を参照されたい。
なお、FK における四元数と行列、オイラー角に関する関係は• MV 行列系。
• 右手座標系。
を満たすことを前提に構築されている。

2.3.1 四元数クラスのメンバ構成
四元数の 3 種類の虚数単位をそれぞれ i, j, k とし、4 個の実数によって q = s + xi + yj + zk で表わされるとする。このとき、実数部である s をスカラー部、虚数部である xi + yj + zk をベクトル部と呼ぶ。これは、四元数ではしばしば虚数部
係数をベクトル (x, y, z) として扱うと、都合の良いことが多々あるためである。
これにならい、四元数を表わすクラス fk Quaternion では、四元数を 1 個の double 型実数 s と、fk Vector 型のメンバ v
によって表わしている。つまり、fk Quaternion 型の変数を q とした場合、
q.s,

q.v.x,

q.v.y,

q.v.z

として各成分にアクセスできることになる。これらは全て public メンバとなっている。

2.3.2 四元数の生成と設定
fk Quaternion クラスは、デフォルトコンストラクタとして何も引数をとらないコンストラクタがある。この場合、スカラー部である s が 1、ベクトル部に零ベクトルが設定される。
その他にも、fk Quaternion には 2 種類のコンストラクタがある。まず、4 個の実数を引数にとるもので、これはそれぞ
れスカラー部、そしてベクトル部の x, y, z 成分に対応する。もう 1 つのコンストラクタは実数 1 個と fk Vector 1 個をとる
もので、やはり同様にスカラー部とベクトル部に対応する。
それぞれの書式例は、以下のようなものになる。

fk_Vector

v (0.2 , 0.5 , 1.0);

fk_Quaternion

q1 (0.3 , 0.4 , 2.0 , -2.0);

fk_Quaternion

q2 (0.5 , v );設定用のメンバ関数としては、以下のようなものがある。
void init(void)
初期化のためのメンバ関数で、デフォルトコンストラクタと同様にスカラー部が 1、ベクトル部に零ベクトルが設定される。

17

void set(double t, const fk Vector &v)
設定のためのメンバ関数で、t がスカラー部、v がベクトル部に対応している。

void set(double s, double x, double y, double z)
設定のためのメンバ関数で、四元数の各成分が順に対応している。

void setRotate(double theta, const fk Vector &v)
角度を theta、回転軸を v とするような回転変換を表わす四元数を生成する。実際に四元数に代入される値は、thetaVを θ、v を V としたとき、スカラー部が cos θ2 、ベクトル部が |V|sin θ2 となる。

void setRotate(double theta, double x, double y, double z)
角度を theta, 回転軸を (x, y, z) とするような回転変換を表わす四元数を生成する。ベクトル部の引数が実数となる以外は、前述の setRotate と同様である。

2.3.3 比較演算子
fk Quaternion 型は、比較演算子として ‘==’ と ‘!=’ を利用できる。fk Vector と同様に、ある程度の数値誤差を許している。

2.3.4 単項演算子
fk Quaternion 型では、以下の表 2.6 に挙げる 3 種類の単項演算子をサポートする。
表 2.6 fk Quaternion の単項演算子

演算子

効果

-

符合転換を返す。

˜

共役を返す。

!

逆元を返す。

元となる四元数を q = s + xi + yj + zk とすると、符合転換 −q、共役 q、逆元 q−1 はそれぞれ以下の式によって求められる。
−q= −s − xi − yj − zkq

= s − xi − yj − zk
q
=
|q|2

q−1

なお、全ての単項演算子は自身には変化を及ぼさない。

2.3.5 二項演算子
fk Quaternion 型がサポートする二項演算子を、表 2.7 に羅列する。

18

表 2.7 fk Quaternion の二項演算子

演算子

形式

機能

返り値の型

+

Quaternion + Quaternion

四元数の和fk Quaternion-

Quaternion − Quaternion

四元数の差

fk Quaternion*

Quaternion ∗ Quaternion

四元数の積

fk Quaternion*

double ∗ Quaternion

四元数の実数倍

fk Quaternion*

Quaternion ∗ double

四元数の実数倍

fk Quaternion/

Quaternion / double

四元数の実数商

fk Quaternionˆ

Quaternion ˆ Quaternion

四元数の内積double*Quaternion ∗ V ector

四元数によるベクトル変換

fk Vector

この中で、内積値は q1 = s1 + x1 i + y1 j + z1 k, q2 = s2 + x2 i + y2 j + z2 k としたとき、
q1 · q2 = s1 s2 + x1 x2 + y1 y2 + z1 z2という式によって求められる実数値のことである。また、ベクトル変換というのは四元数 q と 3 次元ベクトル V に対し、
V′ = qVq−1という演算を施すことである。このとき、q が回転変換を表わす場合に、V′ は V を回転したベクトルとなる。この演算は、行列による回転演算と比較して若干高速であることが知られている。

2.3.6 代入演算子
fk Quaternion 型の代入演算子 ‘ = ’ は、fk Vector の場合と同様に値のコピーが行われ、実体は別物となる。その他の代入演算子を表 2.8 に列挙する。
表 2.8 fk Quaternion の代入演算子

演算子

形式

効果

+=

Quaternion += Quaternion

left = left + right-=

Quaternion -= Quaternion

left = left - right*=

Quaternion *= double

left = left * right/=

Quaternion /= double

left = left / right*=

Quaternion *= Quaternion

left = left * right2.3.7 オイラー角との相互変換
四 元 数 は 任 意 軸 に よ る 回 転 変 換 を 表 わ す が、こ れ は す な わ ち オ イ ラ ー 角 と 同 義 の 情 報 を 持 つ こ と を 意 味 す る。
fk Quaternion には、fk Angle と相互変換を行うためのメンバ関数が用意されている。以下にその仕様を述べる。
void makeEuler(const fk Angle angle)
angle が示すオイラー角と同義の四元数を設定する。

void makeEuler(double h, double p, double b)h をヘディング角、p をピッチ角、b をバンク角とするオイラー角と同義の四元数を設定する。

fk Angle getEuler(void)
同義となるオイラー角を返す。
19

2.3.8 各種メンバ関数
fk Quaternion に は、こ れ ま で 挙 げ た 他 に も 以 下 の よ う な メ ン バ 関 数 が あ る。な お、文 中 で は 四 元 数 自 身 を
q = s + xi + yj + zk と想定する。
double norm(void)
ノルム |q|2 = s2 + x2 + y 2 + z 2 を返す。

double abs(void)
絶対値 |q| =√s2 + x2 + y 2 + z 2 を返す。

bool normalize(void)q
を求め、自身に上書きし true を返す。ただし、成分が全て 0 であった場合は値を変更せずに自身の正規化四元数 |q|false を返す。

bool inverse(void)
自身の逆元 q−1 を求め、自身に上書きし true を返す。ただし、成分が全て 0 であった場合は値を変更せずに false を返す。

void conj(void)
自身の共役 q を求め、自身に上書きする。

fk Matrix conv(void)
自身が表わす回転変換と同義の行列を返す。

2.3.9 補間関数
四元数の最大の特徴は姿勢の補間である。あるオイラー角から別のオイラー角への変化をスムーズに実現することは、オイラー角や行列のみを用いる場合は難解であるが、四元数はこういった問題を解決するのに適している。
補間四元数を求めるために、FK では以下の 2 種類の関数が用意されている。
fk Quaternion fk Math::quatInterLinear( fk Quaternion q1, fk Quaternion q2, double t)
q1 と q2 に対し、単純線形補間を行った結果を返す。t は 0 から 1 までのパラメータで、0 の場合 q1、1 の場合 q2 と完全に一致する。補間処理は以下の式に基づく。
q(t) = (1 − t)q1 + tq2
fk Quaternion fk Math::quatInterSphere( fk Quaternion q1, fk Quaternion q2, double t)
q1 と q2 に対し、球面線形補間を行った結果を返す。t は 0 から 1 までのパラメータで、0 の場合 q1、1 の場合 q2 と完全に一致する。補間処理は以下の式に基づく。
q(t) =

sin(tθ)sin((1 − t)θ)
q1 +
q2
sin θ
sin θ

(θ = cos−1 (q1 · q2 ))演算そのものは単純線形補間の方が高速である。しかし単純線形補間では、状況によっては不安定な結果を示す場合がある。このような現象が許容できない場合には、球面線形補間による処理が有効である。

20

2.4 乱数
乱数の取得は様々なプログラムで必要となるものであるが、FK では以下のような簡便な一様乱数取得用関数を提供している。
unsigned int fk Math::rand()
0 以上の一様乱数整数値を取得する。

int fk Math::rand(int m, int M)m ≦ r < M を満たす一様乱数整数値 r を取得する。

double fk Math::drand()0 以上 1 未満の一様乱数実数を取得する。

double fk Math::drand(double m, double M)m ≦ r < M を満たす一様乱数実数値 r を取得する。

21

第3章

色とマテリアル

この章では fk Material と呼ばれる立体の色属性を司るクラスの使用法を述べる。この章に書かれていることは、のちに立体の色属性を設定するために必要なものとなる。

3.1 色の基本 (fk Color)
まず、色の構成に関しての記述から始めよう。光による色の 3 元色は、赤、緑、青である。これらの色の組合せによって、ディスプレイで映し出されるあらゆる色の表現が可能である。
これらの色の組合せを表現するのが fk Color クラスの役目である。fk Color クラスは次のように使用する。

fk_Color col ;col . init (0.5 , 0.6 , 0.7);

3 つの引数はそれぞれ Red, Green, Blue の値を表し、0 から 1 の値を代入することができる。つまり、すべてに 1 を代入したときに白色、すべてに黒を代入したときに黒色を表現することになる。これは、次のように初期設定によっての代入も可能である。

fk_Color col (0.5 , 0.6 , 0.7);

また、次のように個別に代入することも可能である。

fk_Color col ;col . setR (0.5);
col . setG (0.6);
col . setB (0.7);

3.2 物質のリアルな表現 (fk Material)
ここでは、マテリアルと呼ばれる物質色の表現法と、その設定法を記述する。前節で述べたような表現では、まだ物質を表現するには不十分なのである。たとえば蛍光色のような表現、光沢、透明度といった、非常に細かな設定がなされて初めて物質感を出すことができる。ここでは、それらの設定法を述べる。しかし、実際に自分の思い通りにマテリアルの設定が行えるようになるには、ある程度の試行錯誤が必要となるだろう。
fk Material クラスは表 3.1 のようなステータスを持っている。

22

表 3.1 fk Material の持つステータス

ステータス名

値の型

意味

alpha

ﬂoat

透明度

ambient

fk Color

環境反射係数

diﬀusefk Color

拡散反射係数emissionfk Color

放射光係数

specularfk Color

鏡面反射係数

shininess

ﬂoat

鏡面反射のハイライト

それぞれに対しての簡単な説明を付随する。
透明度は、文字通り物質の透明度を指し、0.0 のとき完全な透明、1.0 のときに完全な不透明を指す。注意しなければな
らないのは、例えばガラスのような透明な物質感を表現したいときは、他のステータスを黒に近い色に設定しないと、曇り
ガラスのような表現になってしまうことである。従って、立体が持つ透明感はこの値だけではなく、他の色属性も考慮に入れる必要がある。なお、立体のシーンへの登録の順序によって透過処理の有無が変わってしまうので、透過処理を行いたい
モデルはできるだけ後に登録する必要がある。これに関する詳細は第 10 章で再び述べる。また、透過処理を行う場合は描
画そのものが非常に低速になるため、シーンにおいて透過処理を実際に行うための設定を行う必要もある。これに関しても第 10 章で述べる。
環境反射係数は、環境光に対しての反射の度合を示すものである。環境光は、どのような状態にある面にも同様に照らされる (と仮定された) 光である。したがってこの値が高いと、光の当たってない面も光が当たっている面と同様な色合いを写し出すので、蛍光色に似たような雰囲気になる。逆に、この値が低いと露骨に光源の効果が出る。したがって、暗い部屋の中に光源があるような雰囲気が出る。
拡散反射係数は、普通一般にものの「色」と呼ばれているものを指す。具体的には、光源に当たることによって反映される色のことである。この色は、光源に対して垂直な角度になったときに最も明るく反映されるが、一旦面に照射されればすべての方向に均等に散乱するため、どの視点から見ても同じ明るさを示す。この値が高いと、物質の色が素直に現れる。この値が低く、ambient や diﬀuse や emission の値も低い場合は、その物体は墨のように黒いものとなる。diﬀuse の値が低
く、その他の値のうちの幾つかが高い値を持つとき、変化に富んだ物質感が醸し出される。
放射光係数は、文字通り自身が放射する光の係数を示す。つまり、あたかも自身が発光しているかのような効果を出す。
しかし、この物体自身は光源ではないので、他の物体の色に影響することはない。この値の働きは、あくまで自身が発光しているような効果を出すことだけである。光源の設定に関しては、8 章と 10 章で詳しく述べている。
鏡面反射係数は、文字通り反射の色合いを示すものである。この値は、ある特定の角度範囲からしか反映されない反射の強さを示すものである。この値が高いと、鏡のように反射が強くなる1）。この値が高いと、金属やプラスチックのように表面が滑らかな印象を受ける。逆に低い場合には、紙や石炭のように表面が粗い印象を受ける。
鏡面反射のハイライトは、鏡面反射の反射角度範囲を設定するものである。この値は、0 から 128 までの値をとり、この値が大きいほどハイライトは小さくなり、より金属の質感が増す。逆に値を小さくした場合、質感はプラスチックのようになる。
それぞれの設定の仕方は次のようになっている。

fk_Materialmat ;fk_Coloramb (0.3 , 0.5 , 0.8);

fk_Colordif (0.2 , 0.4 , 0.9);

fk_Coloremi (0.0 , 0.5 , 0.3);

fk_Colorspe (1.0 , 0.5 , 1.0);

1） この値を高くしても、実際の鏡のような効果 (他のオブジェクトが反射して映される) があるわけではない。

23

mat . setAlpha (0.5)// 透 明 度 の 設 定

mat . setAmbient ( amb );// 環 境 反 射 係 数 の 設 定

mat . setDiffuse ( dif );// 拡 散 反 射 係 数 の 設 定

mat . setEmission ( emi );// 放 射 光 係 数 の 設 定

mat . setSpecular ( spe );// 鏡 面 反 射 係 数 の 設 定

mat . setShininess (64.0);// 鏡 面 反 射 の ハ イ ラ イ ト の 設 定

また、fk Color を引数にとる関数は次のように直接代入することもできる。

fk_Material

mat ;mat . setAlpha (0.5);
mat . setAmbient (0.3 , 0.5 , 0.8);// 環 境 反 射 係 数 の 設 定

mat . setDiffuse (0.2 , 0.4 , 0.9);// 拡 散 反 射 係 数 の 設 定

mat . setEmission (0.0 , 0.5 , 0.3);// 放 射 光 係 数 の 設 定// 鏡 面 反 射 係 数 の 設 定

mat . setSpecular (1.0 , 0.5 , 1.0);
mat . setShininess (64.0);// 鏡 面 反 射 の ハ イ ラ イ ト の 設 定

この作業は、ディティールを凝る分には非常にいいのであるが、ときには色つけは簡単に済ませたいという場面もあるだろう。そのようなとき、逐一値を設定するのは不便である。このとき、非常に簡易に済ませることのできる手段が 2 種類用
意されている。
最初の手段は、setAmbDiﬀ というメンバ関数を用いることである。これは、setAmbient メンバ関数や setDiﬀuse 関数と同様の使用法なのだが、関数に代入された値を同時に ambient と diﬀuse の両方の値に同じく設定する。テストなど、あまり色の質感が関係ない状況ならば、通常はこれだけでも十分である。

fk_Material mat1 , mat2 ;mat1 . setAmbDiff (1.0 , 1.0 , 0.0);// 黄 色 い マ テ リ ア ル

mat2 . setAmbDiff (1.0 , 0.0 , 1.0);// マ ゼ ン タ の マ テ リ ア ル

もうひとつの手段は、あらかじめ準備されているマテリアルを使用してしまうことである。全部で 40 種類あるこれらのマテリアル群は、どれもグローバルな変数として利用できる。大抵の場合、これで事が足りるだろう。なお、このマテリア
ルを羅列した表を付録 A に掲載しておく。参照して、適宜使用してほしい。
これらの使い方は，以下のように FK::Material 名前空間を利用すればよい。

using namespace FK ;model . setMaterial ( Material :: Yellow );

24

第4章

形状表現

この章では fk Shape と言われる形状を司るクラスと、それから派生したクラスの使用法を述べる。これらのクラスは形状をなんらかの形で定義する手段を提供している。しかし、これらの形状はのちの fk Model に代入が行われない限り描画されない。つまり、FK システムでは形状とオブジェクトの存在は別々に定義されている必要がある。たとえば、車を 3 台表示したければ、まず車の形状を定義し、次にモデルを 3 つ作成し、それらに車の形状を代入すればよい。このようなケースで、モデル 1 つずつに対して形状を改めて作成するのは非効率といえる。こういったことは、8 章で詳しく述べる。
FK システムにおいて、形状は表 4.1 のようなものを定義することができる。
表 4.1 形状の種類

形状

クラス名

必要な引数

点

fk Point

位置ベクトル線分

fk Line

両端点の位置ベクトル

ポリライン

fk Polyline

各頂点の位置ベクトル

閉じたポリライン

fk Closedline

各頂点の位置ベクトル

多角形平面

fk Polygon

各頂点の位置ベクトル円

fk Circle

分割数、半径直方体

fk Block

縦、横、高さ球

fk Sphere

分割数、半径正多角柱・円柱

fk Prism

上面半径、底面半径、高さ

正多角錐・円錐

fk Cone

底面半径、高さ

インデックスフェースセット

fk IndexFaceSet

ファイル名等矩形テクスチャfk RectTexture

画像ファイル名

三角形テクスチャ

fk TriTexture

画像ファイル名

メッシュテクスチャ

fk MeshTexture

画像ファイル名

IFS テクスチャ

fk IFSTexture

画像ファイル名

文字列板

fk TextImage

文字列またはテキストファイル

パーティクル

fk ParticleSet

様々な設定

光源

fk Light

タイプ

次節から、これらの詳細な使用法をひとつずつ述べ、最後にそれらを統括的に扱う方法を述べる。

4.1 ポリライン (fk Polyline)
ポリラインとは、いわば折れ線のことである。線分が複数つながったものと考えてもよい。構成される線の数は任意でよい。
定義方法は、普通に変数を準備すればよい。

fk_Polyline poly ;25

pushVertex() 関数を使えば 1 個ずつ頂点を代入していくことができる。

fk_Vector pos ;fk_Polyline poly ;int i ;for ( i = 0; i < 10; i ++) {pos . set ( double ( i * i ) , double ( i ) , 0.0);
poly . pushVertex ( pos );}このように、順番に位置ベクトルを代入していけばよい。この場合は、9 本の線分によって構成されたポリラインが生成される。もし途中で頂点位置を変更したい場合は、setVertex 関数を用いるとよい。

fk_Vector pos ;fk_Polyline poly ;int i ;for ( int i = 0; i < 10; i ++) {pos . set ( double ( i * i ) , double ( i ) , 0.0);
poly . pushVertex ( pos );}pos . set (5.0 , 5.0 , 5.0);
poly . setVertex (5 , pos );上記のプログラムソースの最後の行で、ポリラインの 6 番目の頂点の位置を変えている。すべてを生成し直すよりも、この方が高速かつ手軽に処理できる。
なお、設定した全ての頂点情報を削除したい場合は、次のように allClear() メンバ関数を用いれば実現できる。

fk_Polyline Poly ;Poly . allClear ();4.2 閉じたポリライン (fk Closedline)
fk Closedline クラスは、基本的には使用法は fk Polyline クラスと変わりはない。唯一異なる点は、fk Closedline は閉じ
たポリライン — つまり、始点と終点の間にも線分が存在することを意味する。したがって、多角形を線分で表現したい場
合に適している。

4.3 点 (fk Point)「点」というのは、ここでは画面上に表示させる 1 ピクセル分の存在を指す。例えば、これらの集合を流動的に動かすことによって、空間中での流れを表現することができる1）。点は、それ自体が大きさを持たないことや、描画することが高速
1） 実際にこのような機能を実装する場合は、4.13 節にあるパーティクル用クラスの採用も検討するとよい。

26

なことから、とても扱いやすい対象である。
fk Point クラスの利用法は、fk Polyline クラスとまったく同一である。つまり、「pushVertex」関数で点を生成し、「setVertex」によって移動させることができる。fk Polyline と異なる点は、ポリラインが表示されるか、複数の点が表示されるかということのみである。
なお、設定された頂点の全削除は fk Polyline と同様に allClear() を用いれば実現可能である。

4.4 線分 (fk Line)「線分」は、画面上に線分を表示させる。fk Line は、もちろん 1 本の線分を表現することが可能だが、複数の線分を 1 つのオブジェクトで表現できる。
定義には特に特別な引数は必要としない。

fk_Line line ;ただし、この場合には両端点の位置がともに原点になってしまうので、位置ベクトルをなんらかの形で代入する必要がある。1 つの手段として、両端点の位置ベクトルが並んだ fk Vector 型の配列を用意しておき、それを setVertex() 関数を用いて代入することである。

fk_Vector vec [2];fk_Line line ;vec [0]. set (1.0 , 1.0 , 1.0);
vec [1]. set ( -1.0 , 1.0 , 1.0);
line . setVertex ( vec );このような記述で、line は (1, 1, 1) と (−1, 1, 1) を結ぶ線分を表現することになる。値の代入をしなおすことも可能である。それにはやはり setVertex メンバ関数を使用すればよい。

fk_Line line ;fk_Vector a , b ;a . set (1.0 , 1.0 , 1.0);b . set ( -1.0 , 1.0 , 1.0);
ln . setVertex (0 , a );ln . setVertex (1 , b );この場合での setVertex() 関数の最初の引数は、0 なら始点を、1 なら終点を代入することを意味する。2 つめの引数にはfk Vector 型の変数を代入すればよい。
また、fk Line クラスのオブジェクトは複数の線分を持つことが可能である。新たに線分を追加したい場合は、pushLine()
メンバ関数を使用する。次のようにすればよい。

fk_Lineline ;

27

fk_Vector

vec1 , vec2 , vecArray [2];vec1 . set (0.0 , 1.0 , 0.0);
vec2 . set (1.0 , 0.0 , 0.0);line . pushLine ( vec1 , vec2 );// 2 つ の fk_Vector を 使 う 方 法

vecArray [0]. set (0.0 , 0.0 , 1.0);
vecArray [1]. set (0.0 , 0.0 , -1.0);
line . pushLine ( vecArray );// fk_Vector の 配 列 を 使 う 方 法

これにより、fk Line 中の線分が次々と追加されていく。
fk Line における線分情報の全削除は、fk Polyline と同様に allClear() によって実現可能である。

4.5 多角形平面 (fk Polygon)
この fk Polygon クラスは、fk Polyline クラスや fk Closedline クラスと使用法は同様である。ただし、このクラスで定義されたオブジェクトは、平面として存在する。つまり、厚さのない 1 枚の板として存在することになる。
fk Polygon による多角形の生成は、以下のように std::vector を用いて行う。

fk_Polygon poly ;std :: vector < fk_Vector > pos ;pos . push_back ( fk_Vector (0.0 , 10.0 , 0.0));
pos . push_back ( fk_Vector ( -10.0 , 0.0 , 0.0));
pos . push_back ( fk_Vector (10.0 , 0.0 , 0.0));

poly . setVertex (& pos );4.6 円 (fk Circle)
fk Circle クラスは、ステータスとして半径と分割数を持つ。fk Circle クラスのオブジェクトは、実際には多角形の集合によって構成されている。具体的に述べると、中心から放射状に伸びた三角形によって構成される。したがって、円は実際には正多角形の形をしていることになる。ここで問題になるのは、いくつの三角形によって円を疑似するかということである。当然、円により近くするには多くの三角形に分割した方がよい。しかし、多くの三角形が存在するということは、処理そのものも時間がかかるということである。特に多くのオブジェクトを操作するときや、あまりパフォーマンスのよくないマシンで扱う場合にはこの問題は切実となる。そこで、fk Circle には分割数を指定するメンバ関数を持っている。ある条
件によって、分割数を変更することができるのである。
実際の使用法を述べる。まず、定義はやはり通常どおり行えばよい。

fk_Circle circ ;fk Circle クラスでは、初期値として分割数と半径を指定することができる。

28

fk_Circle circ (4 , 100.0);

これによって、分割数 4、半径 100 の円が生成される。2）。なお、この円は半径を r とすると (r cos θ, r sin θ, 0) 上に境界線が存在し、面の法線ベクトルは必ず (0, 0, −1) となっている。
また、setRadius メンバ関数で半径を動的に制御することが可能である。

fk_Circle circ (4 , 5.0);

circ . setRadius (10.0);

半径を変更する方法として、他にも setScale 関数がある。これは、半径を実数倍するものである。

fk_Circle circ (4 , 10.0);
double scale = 4.0;

circ . setScale ( scale );他に、動的に分割数を変更する方法として setDivide 関数がある。引数として分割数を与えることができる。

fk_Circle circ ;circ . setDivide (10);

4.7 直方体 (fk Block)
直方体は、x, y, z 軸にそれぞれ垂直な 6 つの面で構成された立体である。この立体は横幅、高さ、奥行きのステータスを持ち、それぞれ x 方向、y 方向、z 方向の大きさと対応している。
定義は、通常通り行えばよい。

fk_Block block ;このとき、初期値としてすべての辺の長さが 1 である立方体が与えられる。初期値を設定することも可能である。

fk_Block block (10.0 , 1.0 , 40.0);

2） ここでいう分割数とは、円の 14 を三角形に分割する数を指定するものである。したがって、分割数が 4 ならばその円は 16 個の三角形によって構成されることになる。

29

メンバ関数 setSize は、大きさを動的に制御できる。

fk_Block block ;block . setSize (10.0 , 40.0 , 50.0);

setSize は多重定義されており、次のようにひとつの要素だけを制御することもできる。

fk_Block block

block . setSize (10.0 , fk_X );block . setSize (40.0 , fk_Y );block . setSize (50.0 , fk_Z );ここで注意しなければならないのは、この直方体の中心が原点の設定されていることである。つまり、直方体の 8 つの頂点の位置ベクトルは、(x/2, y/2, z/2),(−x/2, y/2, z/2),(x/2, −y/2, z/2),(−x/2, −y/2, z/2),(x/2, y/2, −z/2),(−x/2, y/2, −z/2),(x/2, −y/2, −z/2),(−x/2, −y/2, −z/2).

ということになる。たとえば、yz 平面を地面にみたてて直方体を配置する場合、直方体を代入されたモデルの移動量は x
方向に x/2 移動すればよい。
setScale メンバ関数は、直方体を現状から実数倍するものである。この関数も 3 種類の多重定義がされている。大きさそのものを単純に実数倍する場合、次のように実数ひとつを引数に代入すればよい。

fk_Block block (10.0 , 10.0 , 10.0);

block . setScale (2.0);

また、ある軸方向だけ実数倍したい場合は、2 つ目の引数に軸要素を代入すればよい。

fk_Block block (10.0 , 10.0 , 10.0);

block . setScale (2.0 , fk_X );block . setScale (3.0 , fk_Y );block . setScale (4.0 , fk_X );x, y, z 軸の倍率を 1 度に指定することも可能である。次のプログラムは、上記のプログラムと同じ挙動をする。

30

fk_Block block (10.0 , 10.0 , 10.0);

block . setScale (2.0 , 3.0 , 4.0);

4.8 球 (fk Sphere)
球は、円の場合と同様に半径と分割数を要素の持つ。基本的には、円と使用法はほとんど変わりがない。例えば、分割数4、半径 10 の球を生成するには、以下のようにして fk Sphere 型の変数を宣言すればよい。

fk_Sphere sphere (4 , 10.0);

初期設定や setRadius()、setDivide()、setScale() といったメンバ関数の利用方法は全て fk Circle クラスと同様なので、そちらのマニュアルを参照してほしい。
円と異なる点をあげていくと、分割数によって生成される三角形個数が異なる3）ことと、(当然ながら) 法線ベクトルの値が一定ではないことなどがあげられる。

4.9 正多角柱・円柱 (fk Prism)
正多角柱、円柱を生成するには、fk Prism クラスを用いることによって容易に生成できる。fk Prism では、初期値として角数、上面半径、底面半径、高さをそれぞれ指定する。生成時は上面が −z 方向を、底面が +z 方向を向くように生成される。

fk_Prism prism (5 , 20.0 , 30.0 , 40.0 , false );ここで、上面と底面の「半径」とは、面を構成する多角形の外接円半径 (下図の r) のことを指す。

r

図 4.1 正多角形と外接円半径円柱を生成するには、多角形の角数をある程度大きくすればよい。大体正 20 角形くらいでかなり円柱らしくなる。あとは、リアリティとパフォーマンスによって各自で調整してほしい。
なお、コンストラクタの最後の引数は「スムースシェーディング」の有無の設定で、true の場合は側面が円柱らしい雰囲気となる。逆に false の場合は各面の境界が明瞭になり、角柱らしい雰囲気となる。
3） 分割数を d とおくと、円では 4d 個であったが球では 8d(d − 1) 個である。このことからもわかるように、球は大変多くの多角形から成り立つので扱いには注意が必要である。

31

次に述べる関数で、fk Prism クラスの形状をいつでも動的に変形できる。
void setTopRadius(double r)
上面半径を r に変更する。

void setBottomRadius(double r)
底面半径を r に変更する。

void setHeight(double h)
高さを h に変更する。

4.10 正多角錐・円錐 (fk Cone)fk Cone は正多角錐や円錐を生成するためのクラスである。このクラスでは、初期値として角数、底面半径、高さを指定する。なお、このクラスも fk Prism と同様に底面は +z 方向を向く。

fk_Cone cone (5 , 20.0 , 40.0 , false );「半径」に関しては前節の fk Prism 中の解説を参照してほしい。また、最後の引数は fk Prism と同様にスムースシェー
ディングの有無の設定である。
円錐を生成するには、やはり初期値の角数を大きくすればよいが、あまり大きな値を指定すると表示速度が遅くなるので注意が必要である。なお、以下のメンバ関数によって形状をいつでも動的に変形することが可能である。
void setRadius(double r)
底面半径を r に変更する。

void setHeight(double h)
高さを h に変更する。

4.11 インデックスフェースセット (fk IndexFaceSet)
インデックスフェースセットは、これまでに述べたような球や角錐のような典型的な形状ではない、一般的な形状を表現
したいときに用いる。利用方法として、別のモデリングソフトウェアによって出力したファイルを取り込む方法と、形状情報をプログラム中で生成して与える方法がある。ここでは主にファイル入力による形状生成について解説する。プログラム
による形状生成方法は 5 章にまとめて解説してあるので、そちらを参照してほしい。

4.11.1 STL ファイルの取り込み
STL は、様々な CAD や 3 次元関連のソフトウェアで多く使われているフォーマットである。FK では、STL ファイルを読み込む機能も提供されている。STL ファイルを読み込むには、次に示すように fk IndexFaceSet で readSTLFile 関数
を使用すればよい。ファイル読み込みに成功した場合 true を、失敗した場合 false を返す。もしファイル読み込みに失敗
した場合、エラーメッセージが出力される。

32

fk_IndexFaceSet

ifs ;if ( ifs . readSTLFile ( " sample . stl " ) == false ) {fl_alert ( " File ␣ Read ␣ Error ! " );}4.11.2 SMF ファイルの取り込み
SMF は、主に CG 関連で普及したフォーマットであり、プレーンなテキストファイルや簡単なデータ構造を特徴とする
ため、実際にエディタで記述するのが容易であるという利点も持っている。FK では、VRML や STL と同様に SMF を読み込む機能を持つ。使用法は次の通りである。ファイル読み込みに成功した場合 true を、失敗した場合 false を返す。もしファイル読み込みに失敗した場合、エラーメッセージが出力される。

fk_IndexFaceSet

ifs ;if ( ifs . readSMFFile ( " sample . smf " ) == false ) {fl_alert ( " File ␣ Read ␣ Error ! " );}4.11.3 DXF ファイルの取り込み
DXF は、Autodesk 社が提唱している形状データ変換用フォーマットで、ほどんどの 3D モデリングソフトで入出力機能が用意されている。このフォーマットのファイルを読み込むには、次のように記述する。ファイル読み込みに成功した場合
true を、失敗した場合 false を返す。もしファイル読み込みに失敗した場合、エラーメッセージが出力される。

fk_IndexFaceSet

ifs ;if ( ifs . readDXFFile ( " sample . dxf " ) == false ) {fl_alert ( " File ␣ Read ␣ Error ! " );}4.11.4 MQO ファイルの取り込み
MQO は、Metasequoia というフリーのモデラーの標準ファイルである。このフォーマットのファイルを読み込むには、
readMQOFile() というメンバ関数を利用する。この関数は多重定義されており、二種類の引数構成がある。構成は以下の通りである。

readMQOFile ( string fileName , string objName , bool solidFlg , bool contFlg , bool matFlg );readMQOFile ( string fileName , string objName , int matID , bool solidFlg , bool contFlg , bool matFlg );「ﬁleName」はファイル名文字列、「objName」はファイル中のオブジェクト名文字列を指定する。下段の定義中の「matID」は、特定のマテリアル ID 部分だけを抽出したい場合に、その ID を入力する。
これ以降の引数に関してはデフォルト値が設定されており、省略可能である。「solidFlg」は、false の場合全てのポリゴ
ンを独立ポリゴンとして読み込む。デフォルト引数では「true」となっている。「contFlg」は、テクスチャ断絶操作の有無を指定するためのもので、ここに関しては 6.1.3 節で詳しく説明する。デフォルトでは「true」となっている。最後の「matFlg」は、MQO ファイルからマテリアル情報を読み込むかどうかを設定するもので、デフォルトでは「false」になっている。
33

ファイル読み込みに成功した場合 true を、失敗した場合 false を返す。以下のプログラムは、ファイル名「sample.mqo」、
オブジェクト名「obj1」という指定でデータを読み込む例である。

fk_IndexFaceSet

ifs ;if ( ifs . readMQOFile ( " sample . mqo " , " obj1 " ) == false ) {fl_alert ( " File ␣ Read ␣ Error ! " );}また、MQO ファイル内で特定のマテリアル番号が指定されている面のみ入力したい場合には、以下のように記述すればよい。

fk_IndexFaceSet

ifs ;if ( ifs . readMQOFile ( " sample . mqo " , " obj1 " , 1) == false ) {fl_alert ( " File ␣ Read ␣ Error ! " );}3 番目の引数を「-1」にしたとき、3 番目の引数がない場合と同様に全ての面を入力する。
なお、Metasequoia 中でテクスチャを設定し、テクスチャも読み込みたい場合は、fk IndexFaceSet ではなく 6.1.3 節のfk IFSTexture を用いる必要がある。

4.11.5 MQO データの取り込み
MQO ファイルデータは、4.11.4 節ではファイルからの読み込み方法を述べたが、このファイル中のデータを全て展開した配列データからも読み込むことが可能である。関数は readMQOData() というもので、ファイル名を示す文字列のかわりに unsigned char 型配列の先頭アドレスを示すポインタを渡す以外は、readMQOFile() 関数と同じである。

fk_IndexFaceSet

ifs ;unsigned char* buffer ;if ( ifs . readMQOFile ( buffer , " obj1 " , 1) == false ) {fl_alert ( " File ␣ Read ␣ Error ! " );}4.11.6 形状情報の取得と、頂点座標の移動
fk IndexFaceSet クラスは、他の形状を表すクラスと違ってファイルから情報を読み取ることも多いので、入力後に頂点や面の情報を取得する場面が考えられる。そこで、fk IndexFaceSet クラスでは以下に示す 4 種類の情報取得メンバ関数が用意されている。
int getPosSize(void)
形状の頂点数を返す。

int getFaceSize(void)34

形状の面数を返す。

fk Vector getPosVec(int vID)
インデックスが vID である頂点の位置ベクトルを返す。頂点のインデックスは 0 から順番に始まるもので、頂点数をvNum とすると 0 から (vNum-1) までの頂点が存在することになる。もし vID に対応する頂点が存在しなかった場合、零ベクトルが返される。

vector<int> getFaceData(int fID)
インデックスが fID である面の頂点インデックス情報を返す。面のインデックスは 0 から順番に始まるもので、面数を fNum とすると 0 から (fNum-1) までの面が存在することになる。また、返り値の vector<int> は STL と呼ばれる
C++ の機能によるもので、詳細は第 5 章を参照してほしい。この中に、参照した面を構成する頂点のインデックスが入力されて返される。もし fID に対応する面が存在しない場合、中身が空状態のオブジェクトが返される。

また、以下の 3 種類の方法で各頂点を移動することも可能である。
bool moveVPosition(int vID, fk Vector pos)
インデックスが vID である頂点の位置ベクトルを、pos に変更し、true を返す。もし vID に対応する頂点が存在しなかった場合、false を返す。

bool moveVPosition(int vID, double x, double y, double z)
インデックスが vID である頂点の位置ベクトルを、(x, y, z) に変更し、true を返す。もし vID に対応する頂点が存在しなかった場合、false を返す。

bool moveVPosition(int vID, double *p)
インデックスが vID である頂点の位置ベクトルを、(p[0], p[1], p[2]) に変更する。もし vID に対応する頂点が存在しなかった場合、false を返す。

4.11.7 形状データの各種ファイルへの出力
fk IndexFaceSet クラスでは、形状データをファイルに出力することが可能である。現在サポートされている形式はVRML、STL、DXF、MQO の 4 種類である。それぞれの出力関数の仕様は以下の通りである。
bool writeVRMLFile(string ﬁleName)
形状データを VRML 2.0 形式で出力する。「ﬁleName」は出力ファイル名を指定する。成功すれば true、失敗した場合
は false を返す。

bool writeSTLFile(string ﬁleName)
形状データを STL 形式で出力する。「ﬁleName」は出力ファイル名を指定する。成功すれば true、失敗した場合はfalse を返す。

bool writeDXFFile(string ﬁleName)
形状データを DXF 形式で出力する。「ﬁleName」は出力ファイル名を指定する。成功すれば true、失敗した場合はfalse を返す。

bool writeMQOFile(string ﬁleName)
35形状データを MQO 形式で出力する。「ﬁleName」は出力ファイル名を指定する。成功すれば true、失敗した場合はfalse を返す。なお、オブジェクト名は「obj1」が自動的に付与される。

4.12 光源 (fk Light)
この光源クラスのみ、他の fk Shape の派生クラスとは性質が異なる。その他のクラスがなんらかの形状を表現するのに用いられるのに対し、このクラスは空間中の光源を設定するのに利用される。光源には、平行光源、点光源、スポットライ
トの 3 種類がある。これらの方向やその他のステータスは、基本的には fk Model に代入を行ってから操作するものであり、fk Light クラスのオブジェクトとして定義されるときは光源の種類を設定するのみである。
平行光源とは、空間中のあらゆる場所に同一方向から照らされる光の光源をいう。地球における太陽光のようなものと考えればよい。もっとも扱いやすいので、光を利用した特別な効果を用いないのであればこれで十分である。平行光源は属性
として方向のみを持つ。
点光源は、空間中のある 1 点から光を放射する光源である。宇宙空間での恒星や、部屋の中での灯りなどは点光源を利用するとよい。点光源は、属性として位置と減衰係数を持つ。減衰係数とは、光源からの距離と照射される明るさとをどのような関係にするかを定義するもので、これはさらに一定減衰係数、線形減衰係数、2 次減衰係数の 3 つの係数がある。通常
はデフォルトのままでよいだろう。詳細はリファレンスマニュアルを参照してほしい。
スポットライトは点光源の特殊な場合で、ある 1 定方向を特別に強く照射する働きを持ち、文字通りスポットライトとしての機能を持つ。そのため、スポットライトは属性として位置と方向の両方を持つが、さらに 3 つの属性も持っている。第1 の属性は点光源と同じく減衰係数である。第 2 の属性はカットオフ係数で、これはスポットライトの照射角度のことである。この値が大きければ、スポットライトによって照らされる領域が広くなる。第 3 の属性は「スポットライト指数」と呼ばれるもので、この値が大きいと照射点の中心に近いほど明るくなる効果が強くなる。この値を 0 にすると、スポットライ
トの中心であろうが外側付近であろうが明るさは変わらない。このスポットライト指数も扱いが難しいパラメータなので、減衰係数と同じく通常は 0 でよい。
光源の定義時に、初期値として光源の種類を指定する。

fk_Light parallel ( fk_LightType :: PARALLEL );fk_Light point ( fk_LightType :: POINT );fk_Light spot ( fk_LightType :: SPOT );parallel は平行光源、point は点光源、spot はスポットライトとして定義される。指定のなかった場合は、平行光源として定義される。
平行光源以外であれば、減衰係数を設定できる。減衰係数の設定には setAttenuation() メンバ関数を使用する。

point . setAttenuation (0.0 , 0.01 , 1.0);
spot . setAttenuation (0.01 , 0.0 , 1.0);

引数はそれぞれ左から順番に線形減衰係数 kl 、2 次減衰係数 kq 、一定減衰係数 kc を意味し、以下のような式で減衰関数
f (d) は表される。
f (d) =

1kl d + kq d 2 + kc

ただし、d は光源からの距離を表す。デフォルトでは線形減衰係数、2 次減衰係数が 0, 一定減衰係数が 1 に設定されており、これは距離による減衰がまったくないことを意味している。
36

スポットライトのカットオフ係数とスポットライト指数は、それぞれ setSpotCutOﬀ() と setSpotExponent() というメンバ関数で設定する。

spot . setSpotCutOff ( fk_Math :: PI /6.0);
spot . setSpotExponent (0.00001);

setSpotCutOﬀ() の引数は弧度法による角度を入力する。fk Math::PI は円周率を表すので、例の場合は π/6 = 30◦ となる。
なお、ここまで触れなかったが光源の大事な要素として色がある。色に関しては他の fk Shape クラスと同じく要素として持つことはなく、fk Model の属性として設定されていることに注意しなければならない。
本節で現れる用語は非常に難解で効果がわかりずらいものが多いと思われる。これらに関する詳しい解説や具体的な効果
を (数学的に) 知りたい場合は、fk Light のリファレンスマニュアルを参照してほしい。

4.13 パーティクル用クラス
FK では、パーティクルアニメーションをサポートするためのクラスとして fk Particle 及び fk ParticleSet が用意されている。厳密には、これらは fk Shape クラスの派生クラスではなく、形状を直接表すものではないのだが、本質的に役割が似ていることから本章にて解説する。ここでは機能紹介にとどめるが、具体的な利用例に関しては 13 章にある「パーティクルアニメーション」サンプルを参照してほしい。

4.13.1 fk Particle クラス
fk Particle クラスは、パーティクル単体を表すクラスで、次のようなメンバ関数が用意されている。
void init(void)
パーティクルを初期化する。

int getID(void)
パーティクルの ID を取得する。

unsigned int getCount(void)
パーティクルの年齢を取得する。

fk Vector getPosition(void)
パーティクルの現在位置を取得する。

void setPosition(fk Vector pos)void setPosition(double x, double y, double z)
パーティクルの位置を pos または (x,y,z) に設定する。

fk Vector getVelocity(void)
パーティクルの速度ベクトルを取得する。この値が有効なのは、setVelocity() 関数か setAccel() 関数を利用したときのみである。

37

void setVelocity(fk Vector vel)
void setVelocity(double x, double y, double y)
パーティクルの速度ベクトルを vel または (x,y,z) に設定する。

fk Vector getAccel(void)
パーティクルの加速度ベクトルを取得する。この値が有効なのは、setAccel() 関数を利用したときのみである。

void setAccel(fk Vector acc)
void setAccel(double x, double y, double z)
パーティクルの加速度ベクトルを acc または (x,y,z) に設定する。

int getColorID(void) パーティクルの色 ID を取得する。

void setColorID(int)
パーティクルの色 ID を設定する。

bool getDrawMode(void)
現在の描画状態を取得する。

void setDrawMode(bool ﬂag)
描画状態を設定する。true ならば描画有効、false ならば無効となる。

4.13.2 fk ParticleSet クラス
fk ParticleSet クラスは、パーティクルの集合を表すクラスである。このクラスは、他のクラスの様に直接利用するものではなく、このクラスを継承させて仮想関数を上書きする形で利用する。まず、上書きすることになる仮想関数を紹介する。

4.13.2.1

fk ParticleSet クラスの仮想関数

void genMethod(fk Particle *p)
パーティクルの生成時に、パーティクルに対して行う処理を記述する。p には、新たに生成されたパーティクルオブ
ジェクトが入っている。

void allMethod(void)
毎ループ時に行う全体処理を記述する。

void indivMethod(fk Particle *p)
毎ループ時の各パーティクルに個別に行う処理を記述する。p には、操作対象となるパーティクルオブジェクトが入っている。

4.13.2.2

fk ParticleSet クラスの通常のメンバ関数

また、fk ParticleSet クラスは他にも以下のようなメンバ関数を持っている。
void setAllMode(bool ﬂag)

38

allMethod() 関数による処理の有効化/無効化を設定する。

void setIndivMode(bool ﬂag)
indivMethod() 関数による処理の有効化/無効化を設定する。

void handle(void)
実際に処理を 1 ステップ実行する。

fk Shape * getShape(void)
パーティクルを表す fk Shape オブジェクトを返す。

fk Particle * newParticle(void)
fk Particle * newParticle(fk Vector pos)
fk Particle * newParticle(double x, double y, double z)
パーティクルを新たに生成する。初期位置を引数で設定することも可能である。新たに生成されたパーティクルオブ
ジェクトを返す。

bool removeParticle(fk Particle *p)bool removeParticle(int)
パーティクルを消去する。引数として、パーティクルオブジェクトそのものと ID の 2 種類がある。通常は true を返すが、対象となるパーティクルが存在しなかった場合や、すでに消去されたパーティクルだった場合は false を返す。

unsigned int getCount(void)
パーティクル集合の年齢を取得する。

unsigned int getParticleNum(void)
現状でのパーティクル個数を取得する。

fk Particle * getParticle(int)
ID を入力し、その ID を持つパーティクルを取得する。ID に相当するパーティクルが存在していない場合は nullptrを返す。

fk Particle * getNextParticle(fk Particle *)allMethod 中で全パーティクルを取得する際に利用する。引数の種類によって、以下のようにパーティクルを返す。
1. 引数が nullptr の場合は、ID が最も小さなパーティクルを返す。
2. 引数が最大の ID を持つパーティクルの場合は、nullptr を返す。
3. 引数がそれ以外の場合は、入力パーティクル ID よりも大きな ID を持つものの中で最も小さな ID を持つパー
ティクルを返す。
例えば、allMethod 中で全てのパーティクルの平均座標ベクトルを求めるには、以下のように記述すればよい。
(fk ParticleSet クラスの派生クラス名を「MyParticle」とする。)

void MyParticle :: allMethod ( void ){fk_Particle * p ;39

fk_Vector vec (0.0 , 0.0 , 0.0);
p = ge tN e xt Pa r ti c le ( nullptr );while ( p != nullptr ) {vec += p - > getPosition ();p = ge tN e xt Pa r ti c le ( p );}vec /= double ( getPa rticl eNum ());}

void setMaxSize(unsigned int)
パーティクルの最大個数を設定する。パーティクル個数が設定した最大値に達した場合、newParticle() を呼んでも新たに生成されない。

void setColorPalette(int ID, double r, double g, double b)
色パレットに色を設定する。

4.14 D3DX 形式中のアニメーション動作
fk IndexFaceSet クラスや、第 6.1.3 節で後述する fk IFSTexture で D3DX 形式のファイルを入力したとき、ファイル中にアニメーションデータがある場合は、動的にアニメーション変形を行うことができる。アニメーションを実現するには、
setAnimationTime() 関数を用いる。引数として、時間を表わす数値を double 型で入力する。以下のプログラムは、アニ
メーション動作を表示するサンプルである。

fk _I nde xF ace Se t ifs ;double timeCount ;timeCount = 0.0;
while ( true ) {::// 描 画 処 理:
ifs . set A n i m a t i o n T i m e ( timeCount );timeCount += 10.0;}

上記では、1 回の描画につきアニメーション時間を 10 ずつ増加させている。fk IndexFaceSet を例にしているが、fk IFSTexture の場合でもまったく同様の方法でアニメーション動作ができる。

4.15 BVH 形式のモーション再生
前節で述べた D3DX 形式のアニメーションデータの代わりに、BVH 形式で記述されたモーションデータを利用することができる。D3DX 形式の形状データにはボーン情報が記述されており、そのボーン名と BVH 形式のデータ側のボーン名を合わせておくことによって、対応したボーンの動きが制御できるようになっている。
BVH 形式のデータを利用する際には fk BVHMotion というクラスを用いる。まず、fk BVHMotion クラスの変数を用意し、その変数に利用したい BVH 形式のデータを入力する。その変数を、D3DX 形式を入力した fk IndexFaceSet か、
fk IFSTexture の変数に対して setBVHMotion() 関数を用いて割り当てる、という流れで利用する。以下にその利用例を示す。
40

fk _I nde xF ace Se t ifs ;fk_BVHMotion bvh ;// 先 に D3DX 形 式 の 形 状 デ ー タ を 読 み 込 ん で お く
if ( ifs . readD3DXFile ( " sample . x " , " obj1 " ) == false ) {fl_alert ( " D3DX ␣ File ␣ Read ␣ Error ! " );}// BVH 形 式 の モ ー シ ョ ン デ ー タ を 読 み 込 む
if ( bvh . readBVHFile ( " sample . bvh " ) == false ) {fl_alert ( " BVH ␣ File ␣ Read ␣ Error ! " );}// 形 状 デ ー タ 側 に モ ー シ ョ ン デ ー タ を セ ッ ト す る
ifs . setBVHMotion (& bvh );モーションデータをセットした後は、D3DX 形式のアニメーションと同様に、setAnimationTime() 関数でアニメーショ
ン再生を制御できる。複数の BVH データをあらかじめ読み込んでおき、setBVHMotion() 関数で再生したいモーションを切り替える、といった利用方法が可能である。

41

第5章

動的な形状生成と形状変形

この章では、プログラム中で動的に形状を生成する手法を述べる。FK システムでは、形状に対する生成、参照、変形と言った操作の方法が数多く提供されているが、この章ではそれらの機能の中で比較的容易に扱える形状生成方法を解説する。

5.1 立体の作成方法 (1)
独立した頂点や線分ではなく、面を持つ立体を作成したい場合には、「インデックスフェースセット (Index Face Set)」(以下 IF セット) と呼ばれるデータを作成する必要がある。IF セットは、次の 2 つのデータから成り立っている。
• 各頂点の位置ベクトルデータ。
• 各面が、どの頂点を結んで構成されているかを示すデータ。
例として次のような三角錐を作成してみる。
ID 0

ID 3

ID 1
ID 2
図 5.1 三角錐と各頂点 ID

ここで、それぞれの頂点の位置ベクトルは以下のようなものと想定する。
表 5.1 各頂点の位置ベクトル

頂点 ID 位置ベクトル0(0, 10, 0)

1(−10, −10, 10)

2(10, −10, 10)

3(0, −10, −10)

このとき、4 枚の面はそれぞれ次のような頂点を結ぶことで構成されていることが、図を参照することで確認できる。

42

表 5.2 各面の構成頂点 ID

平面番号構成される頂点の ID

1 枚目

0, 1, 2

2 枚目

0, 2, 3

3 枚目

0, 3, 1

4 枚目

1, 3, 2

この 2 つのデータを、次のようにして入力する。「pos」が頂点の位置ベクトルを格納する配列、「IFSet」が各面の頂点
ID を格納する配列である。IFSet は、面数と角数を掛けた分を用意し、例にあるように続き番号で入力していく。

fk _I nde xF ace Se t ifs ;fk_Vector pos [4];int IFSet [3*4];
pos [0]. set (0.0 , 10.0 , 0.0);
pos [1]. set ( -10.0 , -10.0 , 10.0);
pos [2]. set (10.0 , -10.0 , 10.0);
pos [3]. set (0.0 , -10.0 , -10.0);
IFSet [0] = 0;

IFSet [1] = 1;

IFSet [3] = 0;

IFSet [4] = 2;

IFSet [2] = 2;
IFSet [5] = 3;

IFSet [6] = 0;

IFSet [7] = 3;

IFSet [8] = 1;

IFSet [9] = 1;

IFSet [10] = 3; IFSet [11] = 2;

ifs . makeIFSet (4 , 3 , IFSet , 4 , pos );最終的には、makeIFSet というメンバ関数を用いて fk IndexFaceSet 型に情報を与えることになる。makeIFSet は、次のような形式で用いることができる。

変 数 . makeIFSet ( 面 数 , 角 数 , 各 面 頂 点 配 列 , 頂 点 数 , 位 置 ベ ク ト ル 配 列 ) ;例の場合、面数が 4、角数は三角形なので 3、頂点数は 4 になっている。今のところ、角数として用いることができるのは 3 か 4 (つまり三角形か四角形) のいずれかのみに制限されている。

5.2 立体の作成方法 (2)
前節では、全ての面が同じ角数であることが前提となっているが、ここでは各面で角数が同一でない立体の作成方法を解説する。今回は、次のような四角錐 (ピラミッド型) を想定する。

43

ID 0

ID 3

ID 4

ID 1
ID 2

図 5.2 四角錐と各頂点 ID

前節と同様に、各頂点の位置ベクトルと面を構成する頂点 ID の表を記述すると次のようになる。
表 5.3 各頂点の位置ベクトル

頂点 ID 位置ベクトル0(0, 10, 0)

1(−10, −10, 10)

2(10, −10, 10)

3(10, −10, −10)4(−10, −10, −10)

表 5.4 各面の構成頂点 ID

平面番号構成される頂点の ID

1 枚目

0, 1, 2

2 枚目

0, 2, 3

3 枚目

0, 3, 44 枚目

0, 4, 1

5 枚目

4, 3, 2, 1

今回は、三角形と四角形が混在しているが、このような立体を作成するには次の例のようなプログラムを作成する。

fk _In de xF aceSet shape ;fk_Vector pos ;std :: vector < fk_Vector > posArray ;std :: vector < int > polygon ;std :: vector < std :: vector < int > > IFSet ;pos . set (0.0 , 10.0 , 0.0); posArray . push_back ( pos );pos . set ( -10.0 , -10.0 , 10.0); posArray . push_back ( pos );pos . set (10.0 , -10.0 , 10.0); posArray . push_back ( pos );pos . set (10.0 , -10.0 , -10.0); posArray . push_back ( pos );pos . set ( -10.0 , -10.0 , -10.0); posArray . push_back ( pos );// 1 枚 目
polygon . clear ();polygon . push_back (0);polygon . push_back (1);polygon . push_back (2);
IFSet . push_back ( polygon );// 2 枚 目
polygon . clear ();44

polygon . push_back (0);polygon . push_back (2);polygon . push_back (3);
IFSet . push_back ( polygon );// 3 枚 目
polygon . clear ();polygon . push_back (0);polygon . push_back (3);polygon . push_back (4);IFSet . push_back ( polygon );// 4 枚 目
polygon . clear ();polygon . push_back (0);polygon . push_back (4);polygon . push_back (1);IFSet . push_back ( polygon );// 5 枚 目
polygon . clear ();polygon . push_back (4);polygon . push_back (3);polygon . push_back (2);polygon . push_back (1);IFSet . push_back ( polygon );shape . makeIFSet (& IFSet , & posArray );上記のプログラム中では、「std::vector<***>」という形式で定義された変数が 3 個登場する。ここではこの形式 (STLと呼ばれる C++ の機能) の詳細は解説しないが、これを用いて次のようにして形状を定義していくことができる。
1. まず、std::vector<fk_Vector> 型の変数 (例では posArray) に対し、push back 関数で頂点の位置ベクトルを次々に与えていく。
2. polygon を std::vector<int> 型の変数、IFSet を std::vector< vector<int> > 型の変数として、次のような形式で面を定義していく。

polygon . clear ();polygon . push_back ( 頂 点 ID );polygon . push_back ( 頂 点 ID );:polygon . push_back ( 頂 点 ID );IFSet . push_back ( polygon );5.3 頂点の移動
第 5.1 節 〜 第 5.2 節で述べた方法で作成した様々な形状に対し、頂点を移動することで変形操作を行うことができる。
頂点移動をするには、moveVPosition() を用いる。以下のプログラムは、ID が 2 である頂点を fk Vector を用いて (0, 1, 2)へ移動し、ID が 3 である頂点を数値だけで (1.5, 2.5, 3, 5) へ移動させるものである。

fk _I nde xF ace Se t shape ;fk_Vector

pos ;

45

:
:
pos . set (0.0 , 1.0 , 2.0);
shape . moveVPosition (2 , pos );shape . moveVPosition (3 , 1.5 , 2.5 , 3.5);

46

第6章

テクスチャマッピングと画像処理

この章では、画像を 3D 空間上に表示する「テクスチャマッピング」と呼ばれる技術の利用方法と、画像処理機能の使用方法を述べる。基本的に、テクスチャマッピングは 4 章で述べてきた形状の一種であり、利用方法は他の形状クラスとあまりかわらない。しかし、画像情報を扱うため独特の機能を多く保持するため、独立した章で解説を行う。

6.1 テクスチャマッピング
テクスチャマッピングとは、2 次元画像の全部及び一部を 3 次元空間上に配置して表示する技術である。テクスチャマッ
ピングは、細かな質感を簡単に表現できることや、高速な表示機能が搭載されているハードウェアが普及してきていることから、非常に有用な技術である。FK システムでは、現在「矩形テクスチャ」、「三角形テクスチャ」、「IFS テクスチャ」の3 種類のテクスチャマッピング方法をサポートしている。現在、入力可能な画像フォーマットは Windows Bitmap 形式、
PNG 形式、JPEG 形式の 3 種類である。

6.1.1 矩形テクスチャ
最初に紹介するのは「矩形テクスチャ」と呼ばれるものである。これは、2 次元画像全体をそのまま (つまり長方形の状
態で) 表示するための機能で、非常に簡単に利用できる。クラスとしては、「fk RectTexture」というものを利用することになる。

6.1.1.1 基本的な利用方法
生成は、他の形状オブジェクトと同様に普通に変数を定義するだけでよい。

fk_RectTexture

texture ;画像ファイルの入力は、Windows Bitmap 形式の場合 readBMP() 関数を用いる。この関数は、入力に成功した場合はtrue を、入力に失敗した場合は false を返す。

if ( texture . readBMP ( " samp . bmp " ) == false ) {fl_alert ( " Image ␣ File ␣ Read ␣ Error ! " );}PNG 形式の画像ファイルを読み込みたい場合は、上記の readBMP() を readPNG() 関数に置き換える。
また、テクスチャの 3 次元空間上での大きさの指定には setTextureSize() 関数を用いる。

texture . setTextureSize (50.0 , 30.0);

この状態で、中心を原点、向きを +z 方向としたテクスチャが生成される。
47

6.1.1.2 画像中の一部分の切り出し
fk RectTexture クラスでは、画像の一部分を切り出して表示することも可能である。切り出し部分の指定方法として、「テクスチャ座標系」と呼ばれる座標系を用いる。テクスチャ座標系というのは、画像ファイルのうち一番左下の部分を(0, 0)、右上の部分を (1, 1) として、画像の任意の位置をパラメータとして表す座標系のことである。例えば、画像の中心を表わすテクスチャ座標は (0.5, 0.5) となる。また、100 × 100 の画像の左から 70 ピクセル、下から 40 ピクセルの位置のテクスチャ座標は (0.7, 0.4) ということになる。
切り出す部分は、切り出し部分の左下と右上のテクスチャ座標を設定することになる。指定には setTextureCoord() 関数を利用する。以下は、左下のテクスチャ座標として (0.2, 0.3)、右上のテクスチャ座標として (0.5, 0.6) を指定するサンプル
である。

texture . setTextureCoord (0.2 , 0.3 , 0.5 , 0.6);

6.1.1.3 リピートモード
ビルの外壁や地面を表すテクスチャを生成するときに、1 枚の画像をタイルのように行列状に並べて配置したい場合がある。これを全て別々のテクスチャとして生成するのはかなり処理時間に負担がかかってしまう。このようなとき、
fk RectTexture の「リピートモード」を用いると便利である。リピートモードとは、テクスチャを 1 枚だけ張るのではな
く、タイル状に並べて配置するモードである。これを用いるには、次のようにすればよい。

texture . setTextureSize (100.0 , 100.0);
texture . setRepeatMode ( true );texture . setRepeatParam (5.0 , 10.0);

setRepeatMode 関数はリピートモードを用いるかどうかを設定するメンバ関数で、引数に true を代入するとリピートモードとなる。次の setRepeatParam メンバ関数は並べる個数を設定するもので、例の場合は横方向に 5 枚、縦方向に 10
枚の合計 50 枚を並べることになる。それら全体のサイズが 100x100 なので、1 枚のサイズは 20x10 ということになる。ただし、リピートモードを用いる場合には画像サイズに制限があり、縦幅と横幅はいずれも 2n (n は整数) である必要があり、現在のサポートは 26 = 64 から 216 = 65536 までの間のいずれかのピクセル幅でなければならない。(ただし、縦幅と横幅は一致する必要はない。) 従って、リピートモードを用いるときはあらかじめ画像ファイルを補正しておく必要がある。
また、リピートモードを用いた場合は一部の切り出しに関する設定は無効となる。

6.1.2 三角形テクスチャ
次に紹介するのは「三角形テクスチャ」である。これは、入力した画像の一部分を三角形に切り出して表示する機能を持つ。これは、「fk TriTexture」というクラスを利用する。テクスチャ用変数の定義や画像ファイル読み込みに関しては
fk RectTexture と同様である。

fk_TriTexture

texture ;texture . readBMP ( " samp . bmp " );fk TriTexture の場合も、fk RectTexture と同様に readBMP() を readPNG() に置き換えることで、PNG 形式の画像ファイルを入力できる。
次に、画像のどの部分を切り出すかを指定する。指定の方法は、前節で述べた「テクスチャ座標系」を利用する。切り出48

す部分は、このテクスチャ座標系を利用して 3 点それぞれを指定することになる。指定には setTextureCoord() 関数を利用する。最初の引数は、各頂点の ID を表わし、0, 1, 2 の順番で反時計回りとなるように設定する。2 番目、3 番目の引数はテクスチャ座標の x, y 座標を入力する。

texture . setTextureCoord (0 , 0.0 , 0.0);
texture . setTextureCoord (1 , 1.0 , 0.0);
texture . setTextureCoord (2 , 0.5 , 0.5);

次に、3 点の 3 次元空間上での座標を設定する。設定には setVertexPos() 関数を利用する。最初の引数が頂点 ID、2,3,4番目の引数で 3 次元座標を指定する。

texture . setVertexPos (0 , 0.0 , 0.0 , 0.0);
texture . setVertexPos (1 , 50.0 , 0.0 , 0.0);
texture . setVertexPos (2 , 20.0 , 30.0 , 0.0);

setVertexPos() 関数は、fk Vector 型の変数を引数に持たせることも可能である。

fk_Vector

vec (100.0 , 0.0 , 0.0);

texture . setVertexPos (0 , vec );6.1.3

IFS テクスチャ

次に紹介する「IFS テクスチャ」は、多数の三角形テクスチャをひとまとめに扱うための機能を持つクラスで、クラス名
は「fk IFSTexure」である。このクラスでは、Metasequoia によって作成したテクスチャ付きの MQO ファイルを入力することができる。入力用関数 readMQOFile() の引数構成は、以下のようになっている。

readMQOFile ( string fileName , string objName , int matID , bool contFlg );「ﬁleName」には MQO のファイル名、「objName」はファイル中のオブジェクト名を入力する。「matID」は、特定のマテリアルを持つ面のみを抽出する場合はその ID を指定する。全ての要素を読み込みたい場合は matID に -1 を入力する。
最後の「contFlg」はテクスチャ断絶のための設定である。これは、テクスチャ座標が不連続な箇所に対し、形状の位相を断絶する操作を行うためのものである。これを true にした場合断絶操作が行われ、テクスチャ座標が不連続な箇所が幾
何的にも不連続に表示されるようになる。ほとんどの場合、この操作を行った場合の方がより適した描画となる。注意しなければならないのは、この断絶操作によって MQO データ中の位相構造とは異なる位相状態が内部で形成されることである。そのため、頂点、稜線、面といった位相要素は MQO データよりも若干増加する。
なお、「matID」と「contFlg」はそれぞれ「-1」と「true」というデフォルト引数が設定されており、このままで良いのであれば省略可能である。また、4.11.5 節と同様の用途として、readMQOData() 関数も利用できる。引数の仕様は最初の引数が unsigned char のポインタ型になる以外は上記 readMQOFile() 関数とと同様である。
以下の例は MQO ファイルからの読み込みのサンプルで、テクスチャ用画像ファイル名 (Windows Bitmap 形式) が「sample.bmp」、MQO ファイル名が「sample.mqo」、ファイル中のオブジェクト名が「obj1」であることを想定している。

49

fk_IFSTexture

texture ;if ( texture . readBMP ( " sample . bmp " ) == false ) {fl_alert ( " Image ␣ File ␣ Read ␣ Error ! " );}if ( texture . readMQOFile ( " sample . mqo " , " obj1 " ) == false ) {fl_alert ( " Shape ␣ File ␣ Read ␣ Error ! " );}ちなみに、ここで読み込んだ形状データは 8.6 節で述べているスムースシェーディングの制御に対応している。
fk IFSTexture クラスが持つその他のメンバ関数として、以下のようなものがある。
bool init(void)
テクスチャデータ及び形状データの初期化を行う。

fk TexCoord getTextureCoord(int triID, int vID)
三角形 ID が triID、頂点 ID が vID である頂点に設定されているテクスチャ座標を返す。

void setTextureCoord(int triID, int vID, fk TexCoord coord)
三角形 ID が triID、頂点 ID が vID である頂点に coord をテクスチャ座標として設定する。

fk IndexFaceSet * getIFS(void)
fk IFSTexture クラスは、形状データとして内部では fk IndexFaceSet クラスによる変数を保持しており、その中に形状データを格納している。この関数は、その変数へのポインタを返すものである。これを利用すると、頂点の移動など
も fk IndexFaceSet の機能を用いて可能となる。

6.1.4 メッシュテクスチャ
最後に、「メッシュテクスチャ」を紹介する。メッシュテクスチャは、前述した三角形テクスチャを複数枚同時に定義できる機能を持っている。これは、「fk MeshTexture」というクラスを用いて実現できる。このクラスは、前述の 6.1.3 節で述
べた IFS テクスチャとよく似ているが、以下のような点が異なっている。これらの性質を踏まえて、両方を使い分けてほしい。両者の比較を表 6.1 に示す。
表 6.1 IFS テクスチャとメッシュテクスチャの比較

項目

IFS テクスチャ

メッシュテクスチャ

形状生成

ファイル入力のみ

ファイル入力とプログラムによる動的生成

描画速度

高速

IFS テクスチャより若干低速

テクスチャ断絶対応

非対応

D3DX アニメーション

対応

非対応

使い方は、まず生成する三角形テクスチャの枚数を setTriNum() 関数で指定する。その後、fk TriTexture と同様に
setTextureCoord() 関数で各頂点のテクスチャ座標を、setVertexPos() で空間上の位置座標を入力していくが、それぞれの50関数の引数の最初に三角形の ID を入力するところだけが異なっている。

fk_MeshTextu re

texture ;texture . setTriNum (2);texture . set T ex tu r eC o or d (0 , 0 , 0.0 , 0.0);
texture . set T ex tu r eC o or d (0 , 1 , 1.0 , 0.0);
texture . set T ex tu r eC o or d (0 , 2 , 0.5 , 0.5);
texture . set T ex tu r eC o or d (1 , 0 , 0.0 , 0.0);
texture . set T ex tu r eC o or d (1 , 1 , 0.5 , 0.5);
texture . set T ex tu r eC o or d (1 , 2 , 0.0 , 1.0);
texture . setVertexPos (0 , 0 , 0.0 , 0.0 , 0.0);
texture . setVertexPos (0 , 1 , 50.0 , 0.0 , 0.0);
texture . setVertexPos (0 , 2 , 20.0 , 30.0 , 0.0);
texture . setVertexPos (1 , 0 , 0.0 , 0.0 , 0.0);
texture . setVertexPos (1 , 1 , 20.0 , 30.0 , 0.0);
texture . setVertexPos (1 , 2 , 0.0 , 50.0 , 0.0);

別の生成方法として、Metasequoia によって生成したテクスチャ付きの MQO ファイルを読み込むことも可能である。
以下のように、readMQOFile() 関数を利用する。例では、テクスチャ用画像ファイル名が「sample.bmp」、MQO ファイル名が「sample.mqo」、ファイル中のオブジェクト名が「obj1」と想定している。

fk_MeshTextu re

texture ;if ( texture . readBMP ( " sample . bmp " ) == false ) {fl_alert ( " Image ␣ File ␣ Read ␣ Error ! " );}if ( texture . readMQOFile ( " sample . mqo " , " obj1 " ) == false ) {fl_alert ( " Shape ␣ File ␣ Read ␣ Error ! " );}また、fk MeshTexture クラスは複数のテクスチャ三角形平面によって構成されることになるが、putIndexFaceSet 関数
を用いることによりその形状を fk IndexFaceSet 型の形状として出力することが可能である。

fk_MeshTexture texture ;fk_IndexFaceSet ifset ;texture . putIndexFaceSet (& ifset );6.1.5 テクスチャのレンダリング品質設定
矩形テクスチャ、三角形テクスチャ、IFS テクスチャ、メッシュテクスチャの全てにおいて、レンダリングの品質を設定することができる。やりかたは、以下のように setTexRendMode() 関数で設定を行えばよい。

fk_RectTexture

texture ;:51

:texture . setTexRendMode ( fk_TexRendMode :: SMOOTH );モードは、通常モードである「fk TexRendMode::NORMAL」と、アンチエイリアシング処理で高品質なレンダリングを行う「fk TexRendMode::SMOOTH」が指定できる。デフォルトでは通常モード (fk TexRendMode::NORMAL) となっている。
上記の例は矩形テクスチャで行っているが、fk RectTexture, fk TriTexture, fk IFSTexture のいずれの型でも同様に利用できる。

6.2 画像処理用クラス
第 6.1 節で述べたテクスチャは、画像をファイルから読み込むことを前提としていたが、用途によってはプログラム中で画像を生成し、それをテクスチャマッピングするという場合もある。そのような場合、fk Image というクラスを用いて画像を生成することが可能である。fk RectTexture、fk TriTexture、fk IFSTexture、fk MeshTexture にはそれぞれ setImage()というメンバ関数が用意されており、fk Image クラスの変数をこのメンバ関数で入力することによって、それぞれのテク
スチャに画像情報が反映されるようになっている。
以下のプログラムは、赤から青へのグラデーションを表す画像を生成し、fk RectTexture に画像情報を反映させるプログラムである。

int i , j ;fk_RectTextu re texture ;fk_Image image ;// 画 像 サ イ ズ を 256 x256 に 設 定
image . newImage (256 , 256);// 各 画 素 に 色 を 設 定
for ( i = 0; i < 256; i ++) {for ( j = 0; j < 256; j ++) {image . setRGB (256 - j , 0 , j );}}// テ ク ス チ ャ に 色 を 設 定
texture . setImage (& image );fk Image クラスのメンバ関数を以下に羅列する。
void init(void)
画像情報を初期化する。

bool readBMP(const string ﬁleName)
ファイル名が ﬁleName である Windows Bitmap 形式の画像ファイルを読み込む。成功すれば true を、失敗すればfalse を返す。

bool readPNG(const string ﬁleName)52

ファイル名が ﬁleName である PNG 形式の画像ファイルを読み込む。成功すれば true を、失敗すれば false を返す。

bool readJPG(const string ﬁleName)
ファイル名が ﬁleName である JPEG 形式の画像ファイルを読み込む。成功すれば true を、失敗すれば false を返す。

bool readBMPData(fk ImType *buﬀer)
buﬀer に Windows Bitmap 形式のデータが格納されているという仮定のもと、readBMP() 関数と同様の挙動を行う。

bool readPNGData(fk ImType *buﬀer)
buﬀer に PNG 形式のデータが格納されているという仮定のもと、readPNG() 関数と同様の挙動を行う。

bool writeBMP(const string ﬁleName, bool transFlag)
現 在 格 納 さ れ て い る 画 像 情 報 を、Windows Bitmap 形 式 で フ ァ イ ル 名 が ﬁleName で あ る フ ァ イ ル に 書 き 出 す。
transFlag を true にすると、透過情報を付加した 32bit データとして出力し、false の場合通常のフルカラー 24bit 形式
で出力する。書き出しに成功すれば true を、失敗すれば false を返す。

bool writePNG(const string ﬁleName, bool transFlag)
現在格納されている画像情報を、PNG 形式でファイル名が ﬁleName であるファイルに書き出す。transFlag を trueにすると、透過情報も合わせて出力する。false の場合は透過情報を削除したファイルを生成する。書き出しに成功す
れば true を、失敗すれば false を返す。

bool writeJPG(const string ﬁleName, int quality)
現在格納されている画像情報を、JPEG 形式でファイル名が ﬁleName であるファイルに書き出す。quality は 0 から
100 までの整数値を入力し、画像品質を設定する。数値が低いほど圧縮率は高いが画像品質は低くなる。数値が高いほ
ど圧縮率は悪くなるが画像品質は良くなる。書き出しに成功すれば true を、失敗すれば false を返す。

void newImage(int w, int h)
画像の大きさを横幅 w, 縦幅 h に設定する。これまでに保存されていた画像情報は失われる。

void copyImage(const fk Image *image)
image の画像情報をコピーする。

void copyImage(const fk Image *image, int x, int y)
現在の画像に対し、image の画像情報を左上が (x, y) となる位置に上書きを行う。image はコピー先の中に完全に包含されている必要があり、はみ出てしまう場合には上書きは行われない。

void subImage(const fk Image *image, int x, int y, int w, int h)
元画像 image に対し、左上が (x, y)、横幅 w, 縦幅 h となるような部分画像をコピーする。x, y, w, h に不適切な値が与えられた場合は、コピーを行わない。

int getWidth(void)
画像の横幅を int 型で返す。

int getHeight(void)53

画像の縦幅を int 型で返す。

int getR(int x, int y)(x, y) の位置にある画素の赤要素の値を int 型で返す。

int getG(int x, int y)(x, y) の位置にある画素の緑要素の値を int 型で返す。

int getB(int x, int y)(x, y) の位置にある画素の青要素の値を int 型で返す。

int getA(int x, int y)(x, y) の位置にある画素の透明度要素の値を int 型で返す。

fk Color getColor(int x, int y)(x, y) の位置にある画素の色要素を fk Color 型で返す。

bool setRGBA(int x, int y, int r, int g, int b, int a)(x, y) の位置にある画素に対し、赤、緑、青、透明度をそれぞれ r, g, b, a に設定する。成功すれば true を、失敗すれば false を返す。

bool setRGB(int x, int y, int r, int g, int b)(x, y) の位置にある画素に対し、赤、緑、青をそれぞれ r, g, b に設定する。成功すれば true を、失敗すれば false を返す。

bool setR(int x, int y, int r)(x, y) の位置にある画素に対し、赤要素を r に設定する。成功すれば true を、失敗すれば false を返す。

bool setG(int x, int y, int g)(x, y) の位置にある画素に対し、緑要素を g に設定する。成功すれば true を、失敗すれば false を返す。

bool setB(int x, int y, int b)(x, y) の位置にある画素に対し、青要素を b に設定する。成功すれば true を、失敗すれば false を返す。

bool setA(int x, int y, int a)(x, y) の位置にある画素に対し、透明度要素を a に設定する。成功すれば true を、失敗すれば false を返す。

bool setColor(int x, int y, const fk Color &col)(x, y) の位置にある画素に対し、色要素を col が表す色に設定する。成功すれば true を、失敗すれば false を返す。

void ﬁllColor(const fk Color &col)
画像中の全てのピクセルの色要素を col が表わす色に設定する。

void ﬁllColor(int r, int g, int b, int a)54画像中の全てのピクセルの色要素を、r を赤、b を青、b を緑、a を透明度として設定する。

55

第7章

文字列表示

第 6 章でテクスチャマッピングについての解説を述べたが、FK システムでは特別なテクスチャマッピングとして、文字列を画面上に表示するクラスが用意されている。
文字列用テクスチャは 2 種類あり、容易に表示を実現する「fk SpriteModel」クラスと、高度な機能を持つ「fk TextImage」
クラスである。この節では、まず fk SpriteModel について述べる。

7.1 スプライトモデル
まず、簡易に文字列表示を実現する fk SpriteModel クラスの利用方法を解説する。このクラスによって表示する文字列(等) を「スプライトモデル」と呼ぶ1）。
スプライトモデルを使用した文字列表示は、以下のような手順を踏む。
1. fk SpriteModel 型の変数を用意する。
2. フォント情報を読み込む。
3. サイズや表示位置などの各種設定を行っておく。
4. fk Scene または fk AppWindow に登録する。
5. drawText() によって文字列を設定する。
以下、各項目を個別に説明する。

7.1.1 変数の準備とフォントの読み込み
まず、fk SpriteModel 型の変数を準備する。

fk_SpriteModel sprite ;バージョン 4.2.10 以降の FK ではデフォルトで「Rounded M+」と呼ばれるフォントが設定してある。そのままデフォ
ルトフォントを利用する場合はフォント情報を設定する必要はないが、もしフォントを変更したい場合はフォント情報の入力を行う。
fk SpriteModel オブジェクトは、TrueType 日本語フォントを読み込むことができるので、まずは TrueType 日本語フォントを準備する。大抵の場合、拡張子が「ttf」または「ttc」となっているファイルである。TrueType フォントが格納されている場所は OS によって異なるが、容易に取得できるはずである2）。
TrueType フォントファイルが準備できたら、あとはそのファイル名を initFont() メンバ関数を使って設定する。この関数は、フォントファイルの読み込みに成功したときは true を、失敗したときは false を返す。プログラムは、以下のように記述しておくとフォント読み込みの成功失敗を判定することができる。(エラー表示には ﬂ alert 関数を用いている。)

1） 本来の「スプライト」という単語は技術用語で、1980 年代頃の PC やゲーム機に搭載されていた機能であり、現在の PC やゲーム機ではこの技術は用いられていない。しかし、画面上の文字列やアイコンの表示に当時スプライト技術が用いられていた慣例から、現在でも画面上に表示される文字やアイコンを「スプライト」と呼称することがある。
2） 各 OS に搭載されているフォントデータは、他の PC にコピーすることがライセンス上禁じられていることも多い。別の PC にコピーすることを前提とする場合は、フリーライセンスを持つフォントを用いる必要がある。

56

if ( sprite . initFont ( " sample . ttf " ) == false ) {fl_alert ( " Font ␣ Init ␣ Error ! " );}7.1.2 各種設定
実際に表示を行う前に、必要な各種設定を行っておく。最低限必要な設定は表示位置の設定で、これは setPositionLT()を利用する。

sprite . setPositionLT ( -280.0 , 230.0);

指定する数値はウィンドウ (描画領域) を原点とし、x の正方向が右、y の正方向が上となる。また、単位は (本来の 3D 座
標系とは違い) ピクセル単位となる。スプライトモデルは、空間中の 3D オブジェクトとして配置するものではなく、画面の特定位置に固定して表示されることを前提としているためである。
その他、setSpriteSize() でスプライトを表す画像のサイズを設定しなおすなど、様々な設定項目があるが、これらについてはリファレンスマニュアルを参照してほしい。
文 字 色 な ど、文 字 表 示 に 関 す る 細 か な 設 定 は fk SpriteModel ク ラ ス に は 用 意 さ れ て い な い。こ れ ら の 設 定 は、
fk SpriteModel 型の public メンバである「text」に対して行う。例えば、以下のコードは文字色を白、背景色を黒に設定している。

fk_SpriteModel sprite ;sprite . text . setForeColor (1.0 , 1.0 , 1.0);
sprite . text . setBackColor (0.0 , 0.0 , 0.0);

この「text」メンバは fk TextImage 型である。詳細は 7.2 節を参照してほしい。

7.1.3 シーンやウィンドウへの登録
ウィンドウに fk AppWindow を用いている場合は、通常のモデルと同様に entry() 関数によってスプライトを登録する。

fk_AppWindow window ;fk_SpriteModel sprite ;window . entry (& sprite );fk Scene に対して登録を行う場合は、fk SpriteModel 型の entryFirst() 関数を用いる。引数には fk Scene 型のインスタンスだけでなく、表示を想定する fk Window 型のインスタンスも必要となる。

fk_Scene scene ;fk_Window window ;fk_SpriteModel sprite ;57

sprite . entryFirst (& window , & scene );7.1.4 文字列設定
表示する文字列の設定は、drawText() 関数を用いて行う。

fk_SpriteModel sprite ;sprite . drawText ( " Sample " );引数は std::string 型なので、結果として std::string 型となるものであればよい。例えば、int 型の score という変数の値を用いて「SCORE = 100」のような表示を行いたい場合は、以下のようにすればよい。

fk_SpriteModel sprite ;int score = 100;

sprite . drawText ( " SCORE ␣ = ␣ " + std :: to_string ( score ));なお、drawText() を二回以上呼び出した場合、通常は以前の文字列に追加した形で表示される。例えば、

sprite . drawText ( " ABCD " );sprite . drawText ( " EFGH " );とした場合、画面には「ABCDEFGH」と表示される。以前の「ABCD」を消去し「EFGH」と表示したい場合は、

sprite . drawText ( " ABCD " );sprite . drawText ( " EFGH " , true );というように、第 2 引数に「true」を入れるとよい。
また、表示した文字列を完全に消去したい場合は clearText() 関数を用いる。

7.2 高度な文字列表示
fk SpriteModel に て 簡 単 な 文 字 列 表 示 を 実 現 で き る が、よ り 高 度 な 文 字 列 表 示 機 能 を 提 供 す る ク ラ ス と し て「fk TextImage」がある。fk TextImage では、以下のような機能が実現できる。
• 画面上でのスプライト表示ではなく、3D 空間中の任意の位置に文字列テクスチャを表示する。
• 文字列に対し、色、大きさ、文字間や行間の幅、影効果など様々な設定を行う。
• 各文字を一文字ずつ順番に表示していくなどの文字送り機能を使う。

58

ま た、fk SpriteModel の text メ ン バ は fk TextImage 型 で あ り、こ の 節 で 述 べ ら れ て い る 設 定 を 施 す こ と に よ り、
fk SpriteModel による文字表示においても同様の細かな設定を行うことができる。
fk TextImage による文字列テクスチャの表示を行うには、以下のようなステップを踏むことになる。
1. fk TextImage, fk RectTexture 型のオブジェクトを用意する。
2. フォント情報を読み込む。
3. 文字列テクスチャに対する各種設定を行う。
4. 文字列情報を読み込む。
5. fk RectTexture 型のオブジェクトに fk TextImage 型のオブジェクトを設定する。
あとは、普通の fk RectTexture 型と同様にして表示が可能となる。これらの項目は、次節以降でそれぞれを解説する。

7.2.1 文字列テクスチャの生成
fk TextImage による文字列テクスチャを作成するには、最低でも fk TextImage 型のインスタンスと fk RectTexture 型のインスタンスが必要となる。従って、まずはそれぞれの変数を準備する。そして、fk RectTexture の setImage() メンバ関数を用いて文字列テクスチャ (の画像イメージ) を fk RextTexture インスタンスに設定しておく。

fk_TextImage textImage ;fk_RectTexture texture ;texture . setImage (& textImage );7.2.2 フォント情報の読み込み
次に、フォント情報の読み込みを行う。フォント情報は fk SpriteModel と同様に initFont() を用いて行う。詳細は 7.1.1
節を参照してほしい。

7.2.3 文字列テクスチャの各種設定
次に、文字列テクスチャの各種設定を行う。設定できる項目として、以下のようなものが fk TextImage のメンバ関数として提供されている。なお、fk TextImage クラスは fk Image クラスの派生クラスであり、以下のものに加えて第 6.2 節で述べた fk Image クラスのメンバ関数も全て利用することができる。

7.2.3.1 フォントに関する設定
void setDPI(int dpi)
void setPTSize(int ptsize)
setDPI は文字列の解像度を設定し、setPTSize は文字の大きさを設定する。デフォルトは両方とも 48 である。現状のFK システムではこの 2 つには機能的な差異がなく、結果的に 2 つの数値の積が文字の精細さを表すことになっている。以後、この 2 つの数値の積 (解像度 × 文字の大きさ) を「精細度」と呼ぶ。

void setMonospaceMode(bool ﬂg)
文字を等幅で表示するかどうかを設定する。true で等幅、false で非等幅となる。true の場合、元々のフォントが等幅でない場合でも等幅に補正して表示する。デフォルトでは true に設定されている。

void setMonospaceSize(int size)
文字を等幅で表示する場合の、文字幅を設定する。デフォルトでは 0 に設定されているので、この関数で幅を設定しな
59

いと表示自体が行われない。

void setBoldStrength(int strength)
文字の太さを数値に応じて太くする。初期状態を 1 とし、高い値を与えるほど太くなる。どの程度太くなるのかは精細度による。

void setSmoothMode(bool ﬂg)
出力される画像に対しアンチエイリアシング処理を行うかどうかを設定する。デフォルトでは true に設定されている。

void setShadowMode(bool ﬂg)
影付き効果を行うかどうかを設定する。デフォルトでは false に設定されている。

void setForeColor(fk Color col)void setForeColor(ﬂoat r, ﬂoat g, ﬂoat b, ﬂoat a)void setForeColor(double r, double g, double b, double a)
文字列テクスチャの文字色を指定する。デフォルトでは (1, 1, 1, 1) つまり無透明な白に設定されている。

void setBackColor(fk Color col)void setBackColor(ﬂoat r, ﬂoat g, ﬂoat b, ﬂoat a)void setBackColor(double r, double g, double b, double a)
文字列テクスチャの背景色を指定する。デフォルトでは (0, 0, 0, 1) つまり無透明な黒に設定されている。

void setShadowColor(fk Color col)void setShadowColor(ﬂoat r, ﬂoat g, ﬂoat b, ﬂoat a)void setShadowColor(double r, double g, double b, double a)
影付き効果の影の色を指定する。デフォルトでは (0, 0, 0, 1) つまり無透明な黒に設定されている。

void setShadowOﬀset(int x, int y)
影付き効果の、影の相対位置を指定する。x が正の場合右、負の場合左にずれる。y が正の場合下、負の場合上にずれる。デフォルトの値は、両方とも 0 に設定されている。

void setCacheMode(bool mode)
文字画像のキャッシュを保持するかどうかを設定する。true の場合、一度読み込んだ文字のビットマップをキャッシュとして保持するようになるため、再度その文字を利用する際に処理が高速になる。ただし、キャッシュを行う分シ
ステムが利用するメモリ量は増加することになる。なお、キャッシュはシステム全体で共有するため、異なるインスタ
ンスで読み込んだ文字に関してもキャッシュが効くことになる。デフォルトでは false に設定されている。

void clearCache(void)
setCacheMode でキャッシュモードが有効であった場合に、保存されているキャッシュを全て解放する。この関数はstatic 宣言されているため、インスタンスがなくても「fk_TextImage::clearCache();」とすることで利用可能である。

60

7.2.3.2 文字列配置に関する設定
void setAlign(fk TextAlign align)
テキストのアライメントを設定する。設定できるアライメントには以下のようなものがある。
表 7.1 文字列坂テキストのアライメント

fk TextAlign::LEFT

文字列を左寄せに配置する。

fk TextAlign::CENTER 文字列をセンタリング (真ん中寄せ) に配置する。
fk TextAlign::RIGHT

文字列を右寄に配置する。

デフォルトでは fk TextAlign::LEFT、つまり左寄せに設定されている。

void setOﬀset(int up, int down, int left, int right)
文字列テクスチャの、縁と文字のオフセット (幅) を指定する。引数は順番に上幅、下幅、左幅、右幅となる。デフォル
トでは全て 0 に設定されている。この値は、setCharSkip や setLineSkip と同様に、精細度に依存するものである。

void setCharSkip(int skip)
文字同士の横方向の間にある空白の幅を設定する。デフォルトでは 0、つまり横方向の空間は「なし」に設定されている。この値は、前述した精細度に依存するもので、精細度が高い場合には skip が表す数値の 1 あたりの幅は狭くなる。
従って、精細度が高い場合にはこの数値を高めに設定する必要がある。

void setLineSkip(int skip)
文字同士の縦方向の間にある空白の高さを設定する。デフォルトでは 0、つまり縦方向の空間は「なし」に設定されている。この値も精細度に依存するので、setCharSkip() と同様のことが言える。

void setSpaceLineSkip(int)
空行が入っていた場合、その空行の高さを指定する。デフォルトでは 0、つまり空行があった場合は結果的に省略される状態に設定されている。この値も精細度に依存するので、setCharSkip() と同様のことが言える。

void setMinLineWidth(int width)
通常、画像の横幅はもっとも横幅が長い行と同一となる。この関数は、生成される画像の横幅の最小値を width に設定する。生成される画像の幅が width 以内であった場合、強制的に width に補正される。

7.2.3.3 文字送りに関する設定
void setSendingMode(fk TextSendingMode mode)
文字送り (7.2.8 節を参照のこと) のモードを設定する。設定できるモードには以下のようなものがある。
表 7.2 文字送りのモード

fk TextSendingMode::ALL

文字送りを行わず、全ての文字を一度に表示する。

fk TextSendingMode::CHAR

一文字ずつ文字送りを行う。

fk TextSendingMode::LINE

一行ずつ文字送りを行う。

デフォルトでは fk TextSendingMode::ALL に設定されている。

61

7.2.4 文字列の設定
次に、表示する文字列を設定する。文字列を設定するには、fk UniStr という型の変数を用いる。具体的には、次のようなコードとなる。

fk_UniStr

str ;

:
:
str . convert ( "サンプルの文字列です" , fk_StringCode :: SJIS );このように、convert メンバ関数を用いて設定する。1 番目の引数には文字列を、2 番目の引数にプログラムコードの文字
コードに対応して以下のように設定する。
表 7.3 文字コード対応表

JIS コード

fk StringCode::JIS

ShiftJIS コード

fk StringCode::SJIS

EUC コード

fk StringCode::EUC

ユニコード (UTF8)

fk StringCode::UTF8

ユニコード (UTF16) fk StringCode::UTF16
文字列設定は、convert 関数の他に printf 関数がある。これは、いわゆる標準の printf() 関数とほぼ同一の機能を持つ物
で、変数の値や計算結果などを書式付きで文字列に設定することができる。ただし、第 1 引数に前述した文字コードが入る
点だけが異なっている。以下のコードは、str[0]〜str[9] に「0 です」〜「9 です」という文字列を格納する。

fk_UniStr

str [10];for ( int i = 0; i < 10; i ++) {str [ i ]. printf ( fk_StringCode :: SJIS , " % d で す " , i );}fk UniStr 型変数に格納した文字列を fk TextImage に設定するには、loadUniStr() メンバ関数を用いる。

fk_UniStr str ;fk_TextImage image ;::str . convert ( "サンプルの文字列です" , fk_StringCode :: SJIS );image . loadUniStr (& str );7.2.5 文字列情報の読み込み
文字列を設定する方法は、前述した fk UniStr 型を用いる方法の他に、テキストファイルを読み込むという方法もある。
まず、文字列テクスチャに貼りたい文字列を事前にテキストファイルをどこか別のファイルに保存しておく。文字列を保存する際には、文字列テクスチャ内で改行したい箇所とテキストファイル内の改行は必ず合わせておく。あとは、そのファイ
ル名を fk TextImage オブジェクトに loadStrFile() メンバ関数を用いて入力する。以下は、テキストファイル「str.txt」を62

入力する例である。

textImage . loadStrFile ( " str . txt " , fk_StringCode :: SJIS );loadStrFile() 関数の 2 番目の引数は、テキストの文字コードによって 7.2.4 節の表 7.3 に対応した値を入力する。

7.2.6 文字列読み込み後の情報取得
実際に文字列を読み込んだ後、様々な情報を得るためメンバ関数として以下のようなものが提供されている。
int getLineNum(void)
読み込んだ文字列の行数を返す。

int getAllCharNum(void)
文字列全体の文字数を返す。

int getLineCharNum(int lineID)
最初の行を 0 行目としたときの、lineID 行目の文字数を返す。

int getLineWidth(int lineID)
最初の行を 0 行目としたときの、lineID 行目の行幅 (単位ピクセル) を返す。

int getLineHeight(int lineID)
最初の行を 0 行目としたときの、lineID 行目の行の高さ (単位ピクセル) を返す。

int getMaxLineWidth(void)
生成された行のうち、もっとも行幅 (単位ピクセル) が大きかったものの行幅を返す。

int getMaxLineHeight(void)
生成された各行のうち、もっとも行の高さ (単位ピクセル) が大きかった行の高さを返す。

int getLineStartXPos(int lineID)
最初の行を 0 行目としたときの、lineID 行目の行の左上を表わす画素の x 方向の位置を返す。

int getLineStartYPos(int lineID)
最初の行を 0 行目としたときの、lineID 行目の行の左上を表わす画素の y 方向の位置を返す。

7.2.7 文字列テクスチャ表示のサンプル
前節までで各項目の解説を述べたが、ここではこれまでの記述を踏まえて典型的なコード例を示す。以下のコードは次のような条件を満たすようなコードである。
• TrueType フォント名は「fontsample.ttf」。
• ソースコードの文字コードは ShiftJIS コード。
• 解像度、文字の大きさはそれぞれ 72, 72。

63

• 影付き効果を有効にする。
• 文字列の行間を「20」に設定。
• 文字色は「(0.5, 1, 0.8)」で無透明にする。
• 背景色は「(0.2, 0.7, 0.8)」で半透明にする。
• 影色は「(0, 0, 0)」で無透明にする。
• 影の相対配置は「(5, 5)」に設定。
• アラインはセンタリングにする。
fk_TextImage textImage ;fk_RectTextu re texture ;fk_Model model ;fk_UniStr str ;texture . setImage (& textImage );if ( textImage . initFont ( " fontsample . ttf " ) == false ) {fl_alert ( " Font ␣ Init ␣ Error ! " );}textImage . setDPI (72);textImage . setPTSize (72);textImage . setShadowMode ( true );textImage . setLineSkip (20);textImage . setForeColor (0.5 , 1.0 , 0.8 , 1.0);
textImage . setBackColor (0.2 , 0.7 , 0.8 , 0.3);
textImage . setSha dowCo lor (0.0 , 0.0 , 0.0 , 1.0);
textImage . s et S ha do w Of fs e t (5 , 5);
textImage . setAlign ( fk_TextAlign :: CENTER );str . convert ( "サンプルです。" , fk_StringCode :: SJIS );textImage . loadUniStr (& str );model . setShape (& texture );7.2.8 文字送り
「文字送り」とは、読み込んだ文字列を最初は表示せず、一文字ずつ、あるいは一行ずつ徐々に表示していく機能のことである。この制御のために利用する関数は、簡単にまとめると以下のとおりである。
表 7.4 文字送り用関数

setSendingMode()

文字送りモード設定

loadUniStr()

新規文字列設定

loadStrFile()

新規文字列をファイルから読み込み

send()

文字送り

ﬁnish()

全文字出力

clear()

全文字消去

以下に、詳細を述べる。
文字送りのモード設定に関しては前述した setSendingMode() 関数を用いる。ここでfk TextSendingMode::CHAR または fk TextSendingMode::LINE が設定されていた場合、loadUniStr() 関数や loadStrFile()
関数で文字列が入力された時点では文字は表示されない。

64

send() は、文字送りモードに応じて一文字 (fk TextSendingMode::CHAR)、一列 (fk TextSendingMode::LINE)、あるいは文字列全体 (fk TextSendingMode::ALL) をテクスチャ画像に出力する。既に読み込んだ文字を全て出力した状態で send()関数を呼んだ場合、特に何も起らずに false が返る。そうでない場合は一文字、一列、あるいは文字列全体をモードに従って出力を行い、true を返す。(つまり、最後の文字を send() で出力した時点では true が返り、その後にさらに send() を呼び出した場合は false が返る。)
ﬁnish() 関数は、文字送りモードに関わらずまだ表示されていない文字を全て一気に出力する。返り値は bool 型で、意味は send() と同様である。
clear() 関数は、これまで表示していた文字を全て一旦消去し、読み込んだ時点と同じ状態に戻す。いわゆる「巻き戻し」である。1 文字以上表示されていた状態で clear() を呼んだ場合 true が返り、まだ 1 文字も表示されていない状態で clear()を呼んだ場合 false が返る。
具体的なプログラムは、以下のようになる。このプログラムは、描画ループが 10 回まわる度に一文字を表示し、現在表示中の文字列で、文字が全て表示されていたら str[] 配列中の次の文字列を読み込むというものである。処理の高速化をはかるため、setCacheMode() でキャッシュを有効としている。また、「c」キーを押した場合は表示されていた文字列を一旦消去し、「f」キーを押した場合は現在表示途中の文字列を全て出力する。(ウィンドウやキー操作に関しては、11 章を参照のこと。)

fk_AppWindow window ;fk_TextImage textImage ;fk_UniStr str [10];int loopCount , strCount ;::textImage . setSen dingM ode ( f k _ T e x t S e n di n gM o de :: CHAR );textImage . setCacheMode ( true );textImage . loadUniStr (& str [0]);loopCount = 1;
strCount = 1;
while ( true ) {:if ( window . getKeyStatus ( ’c ’) == true ) {// 「 c 」 キ ー を 押 し た 場 合
textImage . clear ();} else if ( window . getKeyStatus ( ’f ’) == true ) {// 「 f 」 キ ー を 押 し た 場 合
textImage . finish ();} else if ( loopCount % 10 == 0) {if ( textImage . send () == false && strCount != 9) {textImage . loadUniStr (& str [ strCount ]);strCount ++;}}loopCount ++;
}

65

第8章

モデルの制御この章では、fk Model というモデルを司るクラスの使用法を述べる。「モデル」という単語は非常に曖昧な意味を持っている。FK でいうモデルとは、位置や方向を持った 1 個のオブジェクトとしての存在のことを指す。例えば、FK シス
テムの中で建物や車や地形といったものを創造したいならば、それらをひとつのモデルとして定義して扱うことになる。
fk Shape クラスの派生クラス群による形状は、このモデルに代入されて初めて意味を持つことになる。形状は、形状でしかない。逆に、モデルにとって形状は 1 つのステータスである。
形状が、モデルのステータスであることは重要な意味を持つ。もし、モデルに不変の形状が存在してしまうなら、そのモデルの形状を変化させる手段は直接形状を変化させる以外にない。しかし、ある条件によってモデルの持つ形状を「入れ換えたい」と思うことはよくあることである。
例えば、視点から遠くにあるオブジェクトが大変細かなディティールで表現されていたとしても、処理速度の面から考えれば明らかに無駄である。それよりも、普段は非常に簡素な形状で表現し、視点から近くなったときに初めてリアルな形状が表現できればよい。このとき、モデルに対して形状を簡単に代入できる機能は大変重宝することになる。あるいは、アニメーション機能などの実現も容易に行なうことが可能であろう。

8.1 形状の代入
形状の代入法は大変単純である。次のように行なえばよい。

fk_Vector sPos (100.0 , 0.0 , 0.0) , ePos (0.0 , 0.0 , 0.0);
fk_Line line ;fk_Model model ;line . setVertex (0 , sPos );line . setVertex (1 , ePos );model . setShape (& line );つまり前章で示した形状を持つオブジェクトのポインタを、setShape() メンバ関数の引数として与えてやればよい。この例では fk Line 型のオブジェクトを用いたが、fk Shape クラスから派生したクラスのオブジェクトならばなんでもよい。
setShape() によって形状の設定を行った後に、形状そのものに対し編集を行った場合、その編集結果は setShape() によって設定したモデルに直ちに反映する。次のプログラムを見てほしい。

fk_Block block (100.0 , 50.0 , 200.0);
fk_Model model ;model . setShape (& block );::// 様 々 な 処 理 が 行 わ れ て い る 。

:
block . setSize (100.0 , fk_Y );66

このプログラムの最後の行で、block の持つ形状が変化するわけだが、同時に model の持つ形状も変化することを意味する。
この考えを発展させれば、1 つの形状に対して複数のモデルに設定することが可能であることがわかる。次のプログラム
はそれを示している。

fk_Sphere sphere (4 , 100.0);
fk_Model model [4];int i ;for ( i = 0; i < 4; i ++) {model [ i ]. setShape (& sphere );}このような手法は、プログラムの効率を上げるためにも効果的なものである。従って同じ形状を持つモデルは、同じ変数に対して setShape() による設定を行うべきである。球などは、その最も好例であると言える。
ただし、この方式には落し穴がある。次のようなプログラムは、一見正常に動作するようかのように見えるが、実はそうではない。

int main (){fk_Model * model ;model = retBlockModel (100.0 , 100.0 , 100.0)
delete model ;}fk_Model * retBlockModel ( double x , double y , double z ){fk_Block block (x , y , z );fk_Model * model = new fk_Model ;model - > setShape (& block );return model ;}なぜならば、retBlockModel 関数が終了した時点で関数内の block オブジェクトも自動的に消滅してしまうからである。
このとき、model のもつ形状ポインタは行き場を失ってしまう。しかも、コンパイラが事前にチェックすることは不可能なため、複雑なプログラムになってくると無意識にこのようなコーディングをしてしまい、泥沼にはまりかねない。実際、コンパイラがどのような挙動を起こすのかは予想できない。FK システムでは、多くの部分でこのような設計を行っているので、オブジェクトの存在の管理には常に気を配らなければならない。

8.2 色の設定
モデルは、色を表すマテリアルステータスを持っている。色の指定には setMaterial というメンバ関数を用いるが、引数には 3 章で述べた fk Material 型のオブジェクトを用いる。

fk_Model model ;67

fk_Material material ;: // こ の 部 分 で material を: // 作 成 し て お く 。
model . setMaterial ( material );あるいは、付録 A に記述されているマテリアルオブジェクトをそのまま代入してもよい。

model . setMaterial ( Material :: Green );このとき、(setShape 関数とは異なり) 値は実際に model の中に確保されたメモリー内に全てコピーされるので、マテリ
アルオブジェクトが途中で消滅してしまっても問題はない。

8.3 描画モードと描画状態の制御モデルに与えられた形状が例えば球 (fk Sphere) であった場合通常は面表示がなされるが、これをワイヤーフレーム表示
や点表示に切り替えたい場合、setDrawMode メンバ関数を用いることで実現することができる。例えば球をワイヤーフ
レーム表示したい場合は、以下のようにすればよい。

fk_Sphere sphere (4 , 10.0);
fk_Model sphereModel ;sphereModel . setShape (& Sphere );sphereModel . setDrawMode ( fk_Draw :: LINE );現在選択できる描画モードは、以下の通りである。
表 8.1 選択できる描画モード一覧

fk Draw::POINT

形状の頂点を描画する。

fk Draw::LINE形状の稜線を描画する。

fk Draw::FACE

形状の面のうち、表面のみを描画する。

fk Draw::BACK FACE

形状の面のうち、裏面のみを描画する。

fk Draw::FRONTBACK FACE 形状の面のうち、表裏両面を描画する。
fk Draw::TEXTURE

テクスチャを描画する。

fk Draw::GEOM POINT

曲線や曲面の点描画を行う。

fk Draw::GEOM LINE

曲線や曲面グリッド線を描画する。

fk Draw::GEOM FACE

曲面を描画する。

点表示したい場合は fk Draw::POINT、ワイヤーフレーム表示したい場合は fk Draw::LINE、面表示したい場合はfk Draw::FACE を引数として与えることで、モデルの表示を切り替えることができる。ただし、形状が fk Point である場
合に fk Draw::LINE を与えたり、fk Line である場合に fk Draw::FACE を与えるといったような、表示状態が解釈できな
いような場合は何も表示されなくなるので注意が必要である。

68

また、この描画モードは 1 つのモデルに対して複数のモードを同時に設定することができる。例えば面表示とワイヤーフ
レーム表示を同時に行いたい場合は、次のように各モードを「|」で続けて記述することで実現できる。

fk_Sphere sphere (4 , 10.0);
fk_Model model ;fk_Scene scene ;model . setShape (& sphere );modelsetDrawMode ( fk_Draw :: FACE | fk_Draw :: LINE );scene . entryModel (& model );なお、fk Draw::POINT と fk Draw::LINE では光源設定は意味がない。線や点に対する色設定に関しては、8.4 を参照してほしい。

8.4 線や点の色付け (マテリアル)
線や点に対して色を設定するには、それぞれ setPointColor()、setLineColor() というメンバ関数を用いる。それぞれの関数は引数として fk Color のポインタか、RGB を表す ﬂoat 型の実数 3 個のいずれかを代入する。次の例は、1 つの球を面の色が黄色、線の色が赤、点の色が緑に表示されるように設定したものである。

fk_Sphere sphere (4 , 10.0);
fk_Model model ;fk_Material :: initDefault ();model . setShape (& sphere );model . setDrawMode ( fk_Draw :: FACE | fk_Draw :: LINE | fk_Draw :: POINT );model . setMaterial ( Yellow );model . setLineColor (1.0 , 0.0 , 0.0);
model . setPointColor (0.0 , 1.0 , 0.0);

8.5 線の太さや点の大きさの制御
描画モードで fk Draw::POINT か fk Draw::LINE を選択した場合、デフォルトでは描画される点の pixel における大きさ、線の幅はともに 1 に設定されている。これをもっと大きく (太く) したい場合は、それぞれ setSize()、setWidth() メン
バ関数を用いることで実現できる。次の例は、球に対して点描画と線描画を同時に行うモデルを作成し、点の大きさや線幅を制御しているものである。
fk_Sphere sphere (4 , 10.0);
fk_Model model ;model . setShape (& sphere );model . setDrawMode ( fk_Draw :: POINT | fk_Draw :: LINE );model . setSize (3.0);
model . setWidth (5.0);69

線の太さや点の大きさに関しては、環境による制限が生じる場合がある。例えば、ある太さ・大きさで固定されてしまう
場合や、一定以上の太さ・大きさでは描画されないといった現象が起きることがある。この原因は主にグラフィックスハー
ドウェア側の機能によるもので、プログラムで直接制御することは難しい場合が多い。

8.6 スムースシェーディング
FK システムでは、隣り合う面同士をスムースに描画する機能を保持している。これは、fk Model 中の setSmoothMode()というメンバ関数を用いることで制御が可能である。これを用いると、例えば球などの本来は曲面で表現されている形状をよりリアルに表示することが可能となる。次のように、関数の引数に true を与えることによってそのモデルはスムース
シェーディングを用いて描画される。

fk_Model model ;model . setSmoothMode ( true ); // ス ム ー ス モ ー ド ON
model . setSmoothMode ( false ); // ス ム ー ス モ ー ド OFF
なお、この設定は後述するモデル間の継承関係の影響を受けない。

8.7 モデルの位置と姿勢
通常 3 次元のアプリケーションを作成する場合には、とても厄介な座標変換に悩まされる。これは、平行移動や回転を行
列によって表現し、それらの合成によって状況を構築しなければならないからである。どのような 3 次元アプリケーショ
ンも結果的には行列によって視点やオブジェクトの位置や姿勢を表現するのだが、直接的に扱う場合は多くの困難な壁がある。
もう少し直感的な手段として、位置と姿勢を表現する方法が 2 つ存在する。1 つはベクトルを用いた方法であり、もう 1
つはオイラー角を用いた方法である。ベクトルを用いる場合、次の 3 つのステータスをオブジェクトは保持する。
• 位置ベクトル• 前方向を表すベクトル (方向ベクトル)• 上方向を表すベクトル (アップベクトル)
この表現は多くの開発者にとって直感的であろう。特に LookAt — オブジェクトがある位置からある位置を向く — の実
装と大変相性が良い。この手段を用いた場合には最終的にはすべてを回転変換で表現できるような変換式を用いる。このとき問題となるのが、オブジェクトが真上と真下を見た場合に、変換式が不定になってしまうことである。
もう 1 つの手段としてのオイラー角は、普段から聞きなれた言葉ではない。オイラー角とは、実際には 3 つの角度から構成されている。それぞれヘディング角、ピッチ角、バンク角 (しばしばロール角とも表現される) と呼ばれる。簡単に述べると、ヘディング角は東西南北のような緯度方向を、ピッチ角は高度を示す経度方向を、バンク角は視線そのものを回転軸とした回転角を表す。
この表現はすべての状況に矛盾を起こさないとても便利な手段である。しかしアプリケーションを作成する側から見ると、LookAt のような機能の実装には球面逆三角関数方程式と呼ばれる式を解かねばならず、骨が折れることだろう。また、この場合でも変換の際には解が不定となる場合が存在するので、根本的な解決とはならない。
FK システムでは、独自の方法でこれを回避している。FK システムは、モデルの各々が次のようなステータスを保持し
ている。

70

• 位置
• 方向ベクトルとアップベクトル• オイラー角• 行列
実はこの方法では同じ意味を違う方法で 3 通りにも渡って表現していることになる。ここでは詳しく述べないが、この 3
通りもの表現は、互いに弱点を補間しあっており、あるステータスが不定になるような場合には他のステータスが適用されるようにできている。
一方で、fk Model クラスではメンバ関数によってこれらの制御を行うのであるが、FK システムを用いた開発ではさきほ
ど述べたようなわずらわしさからは一切解放される。これらはすべて内部的に行われ、ベクトル表現、オイラー角表現、行
列表現のいずれもが常に整合性を保ち続けることを保証している。
次節からは、モデルに対しての具体的な位置と姿勢の操作を行うためのメンバ関数を、具体的な説明を交えながら述べていく。数は多いが、体系的なものなので理解はさほど難しくないだろう。

8.8 グローバル座標系とローカル座標系
3 次元アプリケーションの持つ座標系の重要な概念として、グローバル座標系とローカル座標系が挙げられる。グローバ
ル座標系は、しばしばワールド座標系とも呼ばれる。平易な言葉で述べるなら、グローバル座標系は客観的な視点であり、ローカル座標系は主観的な視点である。
理解しやすくするために、車の運転を例にとって説明する。車が走っている場面をある場所から傍観しているとしよう。
車は、背景の中を運転者の気の向くままに挙動している。つまり、3 次元座標内を前方向に前進しているということになる。一定時間が経てば、車の位置は進行方向にある程度進んでいることだろう。グローバル座標系は、このような運動を扱
うときに用いられる。グローバル座標系はすべてのモデルが共通して持つ座標系であり、静的なモデル — この例では背景— の位置座標は変化しない。
今度は車の運転者の立場を考えよう。運転者にとっては、前進することによって背景が後ろに過ぎ去っていくように見える。ハンドルを切れば、背景が回転しているように見える。もし北に向かって走っていれば右方向は東になるが、西に向かっていれば右は北になる。このとき、運転者にとっての前後左右がローカル座標系である。それに対し、東西南北にあたるものがグローバル座標系となる。
FK システムにおけるモデルの制御では、一部の例外を除いて常にグローバル座標系とローカル座標系のどちらを使用することもできる。グローバル座標系は、次のような制御に適している。
• 任意の位置への移動。
• グローバル座標系で指定された軸による回転。
• グローバル座標系による方向指定。
それに対し、ローカル座標系は次のような制御に適している。
• 前進、方向転換。
• オブジェクトを中心とした回転。
• ローカル座標系による方向指定。
かなり直観的な表現を使うと、グローバル座標系は東西南北を指定するときに用いられ、ローカル座標系は前後左右を指定するときに用いられると考えられる。どちらも、それぞれに適した場面が存在する。具体的な使用例は、13 章のプログラ
ム例に委ねることにする。この章での目的は、実際の機能の紹介にある。
FK システムでは、グローバル座標系を扱うメンバ関数ではプレフィックスとして gl を冠し、ローカル座標系を扱うメンバ関数は lo を冠するよう統一されている。以上のことを念頭において、ここからの記述を参照されたい。ちなみに、FK71

システムでは次のような左手座標系を採用しており、ローカル座標系もこれにならう。
1. モデルにとって、前は −z 方向を指す。
2. モデルにとって、上は +y 方向を指す。
3. モデルにとって、右は +x 方向を指す。

8.9 モデルの位置と姿勢の参照
モデルの位置を参照したいときには、getPosition メンバ関数を用いる。この関数は fk Vector 型の返り値を持つ。

fk_Model model ;fk_Vector position ;::position = model . getPosition ();同様にして、モデルの方向ベクトルとアップベクトルもそれぞれ getVec 関数と getUpvec 関数で参照できる。

fk_Model model ;fk_Vector vec , upvec ;::vec = model . getVec ();upvec = model . getUpvec ();したがって、あるモデルの位置と姿勢を別のモデルにそっくりコピーしたいときは、この 3 つのステータスを代入すればよい。(代入法に関しては後述する。)
その他、モデルの持つオイラー角や行列も参照できる。それぞれ、getAngle、getMatrix という名のメンバ関数を用いる。

fk_Model model ;fk_Angle angle ;fk_Matrix matrix ;::angle = model . getAngle ();matrix = model . getMatrix ();fk Angle 型は、オイラー角を表現するクラスである。位置と方向ベクトルとアップベクトルを用いてモデルの状態をコピーすることを前述したが、これは位置とオイラー角を用いても可能である。単にコピーするだけならば、オイラー角を用いた方が便利であろう。ある法則を持ってずらして移動させる (たとえば元モデルの後部に位置させるなど) ような高度な制御を行うような場合には、ベクトル表現を用いて処理する方が良いときも多い。適宜選択するとよい。

8.10 平行移動による制御モデルの方向を変化させず、モデルを移動させる手段として、fk Model クラスでは 6 種類のメンバ関数を用意している。
72

void glTranslate(fk Vector);void glTranslate(double, double, double);
void loTranslate(fk Vector);void loTranslate(double, double, double);
void glMoveTo(fk Vector);void glMoveTo(double, double, double);

8.10.1 glTranslate
glTranslate 関数は、モデルの移動ベクトルをグローバル座標系で与えるためのメンバ関数である。例えば、

fk_Vector vec (1.0 , 0.0 , 0.0);
fk_Model model ;int i ;for ( i = 0; i < 10; i ++) {model . glTranslate ( vec );::}というプログラムは、ループの 1 周毎に model を x 方向に 1 ずつ移動させる。glTranslate 関数は多重定義されており、ベクトルの各要素を直接代入してもよい。

model . glTranslate (1.0 , 0.0 , 0.0);

オブジェクトに対して非常に静的な制御を行う場合には、むしろこの方が便利であろう。

8.10.2 loTranslate
loTranslate 関数は、ローカル座標系で移動を制御する。最も多用される表現は、前進を表す次の記述である。

fk_Model model ;double length ;int i ;for ( i = 0; i < 10; i ++) {length = double ( i )*10.0;
model . loTranslate (0.0 , 0.0 , length );::}これにより、等加速度運動が表現されている (length は 10 ずつ増加しているから)。また (向いている方向によらずに) モ
デルを自身の右へ平行移動させることも、次の記述で可能である。

for ( int i = 0; i < 10; i ++) {73

model . loTranslate (0.0 , 10.0 , 0.0);::}

例では述べられていないが、引数として fk Vector 型のオブジェクトをとることも許されている。

8.10.3 glMoveTo
glTranslate 関数が移動量を与えるのに対して、glMoveTo 関数は実際に移動する位置を直接指定する関数である。したがってこの関数においては、現在位置がどこであってもまったく関係がない。glMoveTo 関数を用いた移動表現は、Translate 関数群を用いるよりも直接的なものとなる。

for ( int i = 0; i < 10; i ++) {model . glMoveTo (0.0 , 0.0 , double ( i )*10.0);::}

このプログラムは、次のプログラムと同じ挙動をする。

model . glMoveTo (0.0 , 0.0 , 0.0);
for ( int i = 0; i < 10; i ++) {model . glTranslate (0.0 , 0.0 , 10.0);::}

大抵の場合は工夫次第で同じ動作を多種に渡る表現によって実現可能であることは多い。できるだけ素直な表現を選択するよう努めるとよいだろう。よほど多くのモデルを相手にするのでなければ、選択によるパフォーマンスの差は問題にならない程度である。
なお、loMoveTo 関数は loTranslate で代用できるため、loVec 関数と同一の理由で提供されていない。

8.11 方向ベクトルとアップベクトルの制御
FK システムにおいて、モデルの姿勢を制御する手法は大別すると方向ベクトルとアップベクトルを用いるもの、オイ
ラー角を用いるもの、回転変換を用いるものの 3 種類がある。この節では、このうち方向ベクトルとアップベクトルを用いて制御するために提供されているメンバ関数を紹介する。3 種類のうち、この手法がもっとも直接的 (Primitive) である。
この節では次の 8 種類のメンバ関数を紹介する。
void glFocus(fk Vector);void glFocus(double, double, double);
void loFocus(fk Vector);void loFocus(double, double, double);
void glVec(fk Vector);void glVec(double, double, double);74

void glUpvec(fk Vector);void glUpvec(double, double, double);
void loUpvec(fk Vector);void loUpvec(double, double, double);
このうち、多重定義されている関数は移動関数群と同じように fk Vector によるか、3 次元ベクトルを表す 3 つの実数を代入するかの違いでしかないので、実質的には 4 種類となる。

8.11.1 glFocus
glFocus 関数は簡単に述べてしまうと、任意の位置を代入することによってその位置の方にモデルを向けさせる関数である。これは、あるモデルが別のモデルの方向を常に向いているというような制御を行いたいときに、特に真価を発揮する。
次のプログラムは、それを容易に実現していることを示すものである。

fk_Model modelA , modelB ;for (;;) {: // こ こ で 、 modelA と modelB の 移 動 が: // 行 わ れ て い る と す る 。
:
modelB . glFocus ( modelA . getPosition ());:}

この関数で注意しなければならないのは、直接方向ベクトルを指定するものではないということである。直接指定するような処理を行いたい場合には、glVec 関数を用いればよい。

8.11.2 loFocus
loFocus 関数は、glFocus のローカル座標系版である。lo 関数群に共通の、主観的な制御には好都合な関数である。例えば、model . loFocus (0.0 , 0.0 , 1.0);// 後 ろ を 向 く 。

model . loFocus (1.0 , 0.0 , 0.0);// 右 を 向 く 。

model . loFocus ( -1.0 , 0.0 , 0.0);// 左 を 向 く 。

model . loFocus (0.0 , 1.0 , 0.0);// 上 を 向 く 。

model . loFocus (0.0 , -1.0 , 0.0);// 下 を 向 く 。

model . loFocus (1.0 , 1.0 , 0.0);// 右 上 を 向 く 。

model . loFocus ( -1.0 , 1.0 , -1.0); // 左 前 上 方 を 向 く 。
model . loFocus (0.01 , 0.0 , -1.0); // わ ず か に 右 を 向 く 。
model . loFocus (0.0 , 0.01 , -1.0); // わ ず か に 上 を 向 く 。

といったような扱い方が代表的なものである。

8.11.3 glVec
この関数は、モデルの方向ベクトルを直接指定するものである。この関数を用いた場合、アップベクトルの方向が前の状
態とは関係なく自動的に算出されるため、モデルの姿勢を glUpvec 等を用いて制御しない場合、思わぬ姿勢になることがある。
こ の関数は、もちろ ん glUpvec 等と併用してモ デルの姿勢を定 義するのに有 効だが、特に光源 (fk Light) や 円盤

75

(fk Circle) のようにアップベクトルの方向に意味がないモデルを簡単に制御するのに向いているといえる。
なお、loVec 関数は提供されていない。なぜならば loVec 関数は意味的には loFocus 関数とまったく同じ機能を持つので、そのまま代用が可能となるからである。

8.11.4 glUpvec
この関数は、アップベクトルを直接代入する。アップベクトルは本来方向ベクトルと直交している必要があるが、与えられたベクトルが方向ベクトルと平行であったり零ベクトルであったりしない限り、適当な演算が施されるので心配はいらない。逆に、この関数は方向ベクトルに依存して与えたアップベクトルを書き換えてしまうので、非常に融通の利かない関数ともいえる。実際この関数は、モデルのアップベクトルを常に固定しておく以外にはあまり使用することはない。アップベ
クトルを直接扱うことはある程度難解である。大抵の場合は、後述の回転変換を用いれば解決してしまう。ブランコのような表現や、コマのような表現も、回転変換を用いた方が明らかに簡単である。

8.11.5 loUpvec
察しの通り、この関数は glUpvec のローカル座標系版である。この関数はアップベクトルが方向ベクトルと直交していなければならないという理由から、z 方向の値は意味を持たない。この関数は glFocus 関数と比べてもさらに特殊な状況でしか扱われないであろう。ここでは紹介程度にとどめておく。

8.12 オイラー角による姿勢の制御
この節では、オイラー角による制御を提供する 4 種類の関数に関しての紹介が記述されている。4 つの関数は、次に示す通りである。
void glAngle(fk Angle);void glAngle(double, double, double);
void loAngle(fk Angle);void loAngle(double, double, double);
それぞれ多重定義がなされているが、3 つの実数を引数にとる 2 つの関数はこれまでのようにベクトルを意味しているのではなくオイラー角の 3 要素を示しており、3 つの引数はそれぞれヘディング角、ピッチ角、バンク角を表している。
fk Angle クラスはオイラー角を表現するためのクラスであり、次のように定義されている。

class fk_Angle {public :double h ; // ヘ デ ィ ン グ 角
double p ; // ピ ッ チ 角
double b ; // バ ン ク 角};

従って、ベクトルの場合と同様に直接メンバへのアクセスが可能である。
fk Angle のメンバにしても、glAngle(double, double, double) や loAngle(double, double, double) にしても、値はすべて.
弧度法 (ラジアン) による。つまり、直角の値は π2 = 1.570796 となる。

8.12.1 glAngle
glAngle 関数はオイラー角を直接設定する関数である。相対的な変化量ではなく絶対的なオイラー角の値をここでは代入する。そういった点では、これは glTranslate 関数よりも glMoveTo 関数に近い。

76

オイラー角による表現は非常に手軽である反面、慣れないと把握が難しい。また、制御をベクトルによって行うかオイ
ラー角によって行うかはアプリケーションそのものの設計にも深く関わってくる。あまり明示的な動作の指定や位置座標の指定を多用しないアプリケーションなら、オイラー角を用いた方が効果的な場合もある。しかし、glFocus と glAngle を
getAngle を用いずに併用することは、明らかに混乱を巻き起こすだろう。
glAngle 関数の効果的な使用法の 1 つとして、姿勢の初期化が上げられる。初期状態の姿勢を fk Angle 型のオブジェク
トに保管しておくことによって、いつでも姿勢を初期化できる。

fk_Model model ;fk_Vector init_pos ;fk_Angle init_angle ;::init_pos = model . getPosition (); // 位 置 の ス ナ ッ プ シ ョ ッ ト
init_angle = model . getAngle ();// 姿 勢 の ス ナ ッ プ シ ョ ッ ト::// ス ナ ッ プ シ ョ ッ ト を 行 っ た 状 態 に 戻 す 。
model . glMoveTo ( init_pos );model . glAngle ( init_angle );また、オイラー角の変化による立体の回転はアプリケーションのユーザにとって直観的であるため、ユーザインター
フェースを介して立体を意のままに動かすようなアプリケーションにも威力を発揮するであろう。

8.12.2 loAngle
オイラー角による制御は、glAngle 関数よりもむしろローカル座標系関数である loAngle で真骨頂を発揮する。loAngle関数では、先に述べた loFocus 関数と非常によく似た機能を持つが、バンク角の要素を持つために loFocus よりも応用性は高い。ここにその機能を羅列してみる。(fk Math::PI は円周率である。)

model . loAngle ( fk_Math :: PI , 0.0 , 0.0);// 後 ろ を 向 く

model . loAngle ( fk_Math :: PI /2.0 , 0.0 , 0.0);// 右 を 向 く

model . loAngle ( - fk_Math :: PI /2.0 , 0.0 , 0.0);// 左 を 向 く

model . loAngle (0.0 , fk_Math :: PI /2.0 , 0.0);// 上 を 向 く

model . loAngle (0.0 , - fk_Math :: PI /2.0 , 0.0);// 下 を 向 く

model . loAngle ( fk_Math :: PI /2.0 , fk_Math :: PI /4.0 , 0.0);// 右 上 を 向 く

model . loAngle ( - fk_Math :: PI /4.0 , fk_Math :: PI /4.0 , 0.0); // 左 前 上 方 を 向 く
model . loAngle ( fk_Math :: PI /100.0 , 0.0 , 0.0);// わ ず か に 右 を 向 く

model . loAngle (0.0 , fk_Math :: PI /100.0 , 0.0);// わ ず か に 上 を 向 く

model . loAngle (0.0 , 0.0 , fk_Math :: PI );// 視 線 を 軸 に 半 回 転 。

model . loAngle (0.0 , 0.0 , fk_Math :: PI /2.0);// モ デ ル を 右 に 傾 け る 。

model . loAngle (0.0 , 0.0 , fk_Math :: PI /100.0);// わ ず か に 右 に 傾 け る 。

loFocus と比較してみてほしい。この loAngle の特徴は、回転を角度代入によって行うことにある。こちらのほうが、開発者は直観的に定量的な変化を行うことが可能であろう。また、loFocus と違ってアップベクトルの挙動の予想もできる。
loFocus の乱用は、時としてアップベクトルに対して予想と食い違った処理を施す可能性もある。loAngle ではその心配はない。

77

8.13 回転による制御
前節のオイラー角による制御が姿勢を定義するためのものであるならば、ここで述べる関数群は位置を回転によって制御するためのものといえる。ここで述べられる関数は全部で 16 種類ある。
void glRotate(fk Vector, fk Axis, double);
void glRotate(double, double, double, fk Axis, double);
void glRotate(fk Vector, fk Vector, double);
void glRotate(double, double, double, double, double, double, double);
void loRotate(fk Vector, fk Axis, double);
void loRotate(double, double, double, fk Axis, double);
void loRotate(fk Vector, fk Vector, double);
void loRotate(double, double, double, double, double, double, double);
void glRotateWithVec(fk Vector, fk Axis, double);
void glRotateWithVec(double, double, double, fk Axis, double);
void glRotateWithVec(fk Vector, fk Vector, double);
void glRotateWithVec(double, double, double, double, double, double, double);
void loRotateWithVec(fk Vector, fk Axis, double);
void loRotateWithVec(double, double, double, fk Axis, double);
void loRotateWithVec(fk Vector, fk Vector, double);
void loRotateWithVec(double, double, double, double, double, double, double);
この関数群も、実質 8 種類の関数が引数として fk Vector 型をとるものと 3 つの実数をとるもので多重定義がなされている。行間なく記されているもの同士が対応している。

8.13.1 glRotate と glRotateWithVec
glRotate 関数は、大きく 2 つの機能を持っている。次の引数を持つ場合、モデルはグローバル座標軸を中心に回転する。

fk_Model model ;fk_Vector pos (0.0 , 0.0 , 0.0);::
model . glRotate ( pos , fk_Axis :: X , fk_Math :: PI /4.0);

このうち、最初の引数には回転の中心となる軸上の点を指定する。例の場合は原点を指定している。次の引数は回転軸をどの軸に平行な直線にするかを指定するもので、fk Axis::X、fk Axis::Y、fk Axis::Z から選択する。最後の引数は回転角を弧度法で入力する。中心は原点でなくてもよい。
一方ベクトル 2 つと実数 1 つを引数に取る場合には、glRotate 関数は任意軸回転演算として働く。回転軸直線上の 2 点を代入すればよい。最後の引数は回転角である。実数 7 つをとる場合も、1 番目から 3 番目、4 番目から 6 番目がそれぞれ2 点の位置ベクトルを表す。次のプログラムは、モデルを (100, 50, 0), (50, 100, 0) を通る回転軸を中心に 1 回転させるものである。

for ( int i = 0; i < 200; i ++) {model . glRotate (100.0 , 50.0 , 0.0 , 50.0 , 100.0 , 0.0 , fk_Math :: PI /100.0);

78

:
:
}

glRotate 関数はあくまでモデルの位置を回転移動するためのものであり、姿勢や方向ベクトルはまったく変化しない。
したがって、回転軸がモデルの位置を通る場合には、位置の移動がないために変化がない。回転移動の際に、方向ベクトルも同じように回転してほしい場合には glRotateWithVec 関数を用いるとよい。この関数は位置の回転とともに姿勢の回転も行われる。また回転軸がモデルの位置を通るように設定すれば、モデルは移動せずに方向だけ回転させることができる。
これらの関数はモデルの挙動を予想しやすいので、安心して使用することができるであろう。

8.13.2 loRotate と loRotateWithVec
loRotate メ ン バ 関 数 と loRotateWithVec メ ン バ 関 数 は、ロ ー カ ル 座 標 系 版 で あ る こ と を 除 け ば glRotate や
glRotateWithVec となんら変わりはない。特に loRotateWithVec は、loAngle 関数と多くの機能が重複している。その例を
表 8.2 に示す。ただし、origin は原点を、angle は回転角度を指す。
表 8.2 RotateWithVec と loAngle の比較

RotateWithVec による表現

loAngle による表現

loRotateWithVec(origin, fk Axis::X, angle)

loAngle(0.0, angle, 0.0)

loRotateWithVec(origin, fk Axis::Y, angle)

loAngle(angle, 0.0, 0.0)

loRotateWithVec(origin, fk Axis::Z, angle)

loAngle(0.0, 0.0, angle)

回転の中心や軸の方向を任意にできることから、loRotate の方が loAngle よりも柔軟であるといえるだろう。

8.14 モデルの拡大縮小
FK システムでは、モデルに対して拡大や縮小を行うことが可能であり、次のようなメンバ関数が提供されている。
void setScale(double);
void setScale(double, fk Axis);
void setScale(double, double, double);
void prdScale(double);
void prdScale(double, fk Axis);
void prdScale(double, double, double);
setScale() メンバ関数はモデルの絶対倍率を設定するための関数である。引数として double 1 個のみをとる関数は、モデルの拡大や縮小を単純に行う。fk Axis を引数にとる場合、指定された軸方向に対して拡大や縮小を行う。具体的には、次のように記述を行う。

fk_Model model ;model . setScale (2.0 , fk_Axis :: X ); // X 方 向 に 2 倍 に 拡 大model . setScale (0.4 , fk_Axis :: Y ); // Y 方 向 に 0.4 倍 に 縮 小model . setScale (2.5 , fk_Axis :: Z ); // Z 方 向 に 2.5 倍 に 拡 大79

また、引数が double 3 個のものはそれぞれ x 方向、y 方向、z 方向への拡大率を示す。上の例は、次のように書き換えられる。

fk_Model model ;model . setScale (2.0 , 0.4 , 2.5);

setScale() は、現在のモデルの拡大率に対して相対的な拡大率を設定するものではなく、リンクされた形状に対する絶対的な拡大率を設定するための関数である。もし相対的な指定を行いたい場合は、setScale() ではなく prdScale() を用いる。
引数の意味は setScale() と同様である。
また、現在の拡大率を調べたい場合は次のような拡大率が用意されている。
double getScale(void);
double getScale(fk Axis);
引数がない場合、全体の拡大率が返る。fk Axis 型の引数を入れた場合、その引数が示す軸方向の拡大率が返る。

8.15 モデルの親子関係と継承
8.15.1 モデル親子関係の概要
モデルに関する最後のトピックは継承に関してのものである。
FK システムを利用した開発者が車をデザインしたいと思ったとしよう。車は多くの部品から成り立っている。それらを最初から形状モデラで作成し、1 つの fk Solid として読み込むのも 1 つの手である。しかしその他のプリミティブな形状、
例えば fk Block や fk Sphere 等を利用して簡単な疑似自動車をデザインするような場合を考える。当然プリミティブな立体から車をデザインすることも容易な作業ではないが、問題はその後である。タイヤにあたる部分は車の中心から 4 つの隅
方に地面に接して並んでいる。問題は、車が回転するような運動を行なった時にタイヤ自体は非常に複雑な動作をすることにある。これはベクトルの合成を用いて解決することは可能だが、プログラムが複雑になることに変わりはない。
そこで、FK システムでは複数のモデルをまとめて 1 つのモデルとして扱えるような機能を用意している。もし車体とタイヤの全てをグルーピングし、1 つのモデルとして制御できるのならば何の問題もない。これは、継承と呼ばれる手法を用いて実現することができるのである。
車の場合を考えよう。まず車体を準備する。次に 4 つのタイヤを車体に対して適当な位置に設定する。この相対的な位置関係が固定されれば、自動車は 1 つのモデルとして扱えるわけである。そこで、4 つのタイヤに対して自分の親モデルが車体であることを教えてやるのである。こうすれば、親の動きに合わせて子モデルは相対的な位置を保つような挙動を起こす。もう少し厳密に言うならば、子モデルは親モデルのローカル座標を固定されているわけである。
この機能は、fk Model の持つ setParent メンバ関数によって実現されている。引数として親モデルのポインタを与える。
このときに子モデルの持っていたグローバル座標系での位置と姿勢は、親モデルのローカル座標系でのそれとして扱われるようになる。具体的なプログラムをここに示す。

fk_Sphere sphere (4 , 50.0);
fk_Block block (300.0 , 100.0 , 500.0);
fk_Model CarBody ;fk_Model CarTire [4];int i ;
CarBody . setShape (& block );80

CarBody . glTranslate (0.0 , 100.0 , 500.0);
for ( i = 0; i < 4; i ++) CarTire [ i ]. setShape (& sphere );CarTire [0]. glMoveTo (150.0 , -50.0 , 150.0);
CarTire [1]. glMoveTo ( -150.0 , -50.0 , 150.0);
CarTire [2]. glMoveTo (150.0 , -50.0 , -150.0);
CarTire [3]. glMoveTo ( -150.0 , -50.0 , -150.0);
for ( i = 0; i < 4; i ++) CarTire [ i ]. setParent (& CarBody );プログラムを簡易なものにするため車体を fk Block、タイヤを fk Sphere で表現している。まず、CarBody モデルを
glTranslate によってある程度移動させる。次にタイヤの位置を、親モデルとの相対位置になる地点に CarTire を持ってくる。上記例の場合は球なので方向は関係ないが、必要ならばこのときに姿勢を定義しておく。そして、親モデルがCarBody であることを示すために setParent 関数を呼んでいる。
このプログラムは、以後に CarBody を動作させるとそれに付随して CarTire も動作するようになる。もし CarTire に対して移動を行うメンバ関数を呼ぶとどうなるか? このとき、CarTire は CarBody に対しての相対位置が変更される。これは、例のプログラムにおいて CarTire[i].setShape() と CarTire[i].glMoveTo() の順序を反転させても支障がないことを示す。
また、既にあるモデルの子モデルとなっているモデルに対し、さらにその子モデル (元の親モデルからすれば、いわゆる「孫モデル」) を指定できる。例えば、タイヤをさらにリアルにするためにボルトを付加させることもできる。このときには、やはりボルトを (タイヤに対して) 相対的な位置に設定しておけばよい。
また、継承は座標系だけではなく、モデルのマテリアル属性にも反映される。もし子モデルのマテリアルが未定義であった場合、親モデルの色が設定される。子モデルにすでに色が設定されている場合には子モデルのマテリアルが優先される。

8.15.2 親子関係とモデル情報取得
モデルが親子関係を持った場合、モデルの情報取得は単純な話ではなくなる。例えば、モデルの位置を取得する
getPosition 関数やオイラー角を得る getAngle 関数の場合を考えてみる。
通常、これらの値はグローバル座標系における自身の座標ベクトルやオイラー角が返ってくるのだが、子モデルとなっている場合には親モデルに対する相対座標ベクトルや相対オイラー角が返り値となっている。
これは多くの場合は都合が悪い。これらのみを用いる場合、子モデルの中心が実際にグローバル座標系でどこに位置するのかを知ることができない。そこで、子モデルのグローバル座標系における位置や姿勢等を得たいときのために、fk Modelは getInhPosition (Inh は Inheritance – 継承の略) というメンバ関数を持っている。この関数は getPosition 関数とまったく同様の使用法ではあるが、例え親モデルを持っていても正確なグローバル座標系による位置が返ってくる。これと同様にして getAngle に対応した getInhAngle、getVec に対応した getInhVec、getUpvec に対応した getInhUpvec、getMatrix に対応した getInhMatrix といったメンバ関数を fk Model クラスは持っている。
もしモデルが親を持っていたとき、表 8.3 に示す関数群は親に対する相対的な値を返す。
表 8.3 相対的な値を返す関数群

返り値の型

関数名

fk Vector

getPosition

fk Vector

getVecfk Vector

getUpvecfk Angle

getAngle

fk Matrix

getMatrix81

それに対し、表 8.4 の関数群は絶対的なグローバル座標を返す。
表 8.4 絶対的な値を返す関数群

返り値の型 関数名
fk Vector

getInhPosition

fk Vector

getInhVec

fk Vector

getInhUpvec

fk Angle

getInhAngle

fk Matrix

getInhMatrix

8.16 親子関係とグローバル座標系
通常、モデル同士に親子関係を設定したとき、子モデルは位置・姿勢共に変化する。これは前述したように、元々子モデルが持っていた位置や姿勢が、親モデルからの相対的なものとして扱われるようになるためである。
しかし、モデル間の親子関係は結びたいが、子モデルの位置や姿勢は変化させたくないというケースもある。同様に、親子関係を解消しても、子モデルの位置や姿勢を変化させたくないということもありえる。このような要求に応えるため、
setParent 関数は 2 番目に bool 型の引数と取ることで制御することが可能である。

fk_Model modelA , modelB ;modelA . glMoveTo (10.0 , 0.0 , 0.0);
modelB . setParent (& modelA , true );上記のプログラムで、通常では setParent によって modelB もグローバル座標系では移動するのであるが、setParent 関
数の 2 番目の引数に「true」を指定すると、setParent による設定後も modelB は元の位置・姿勢を保つ。2 番目の引数に「false」を指定した場合、または 2 番目の引数を省略した場合、元の位置や姿勢や modelA からの相対的なものとして扱われるため、結果的に modelB は移動することになる。

8.16.1 親子関係に関する関数
以下に、親子関係に関する fk Model のメンバ関数を羅列する。
void setParent(fk Model *p, bool ﬂag)
p を親モデルとして設定する。ﬂag が true の場合、設定後も元モデルのグローバル座標系での位置・姿勢が変化しな
い。false の場合は、元の位置・姿勢が p の相対的な位置・姿勢として扱われる。2 番目の引数を省略した場合、falseと同じとなる。

void deleteParent(bool ﬂag)
設定してあった親モデルとの関係を解除する。元々親モデルが設定されていなかった場合は何も起こらない。ﬂag に関しては、setParent() 関数と同様。

void entryChild(fk Model *c, bool ﬂag)
c を子モデルの 1 つとして設定する。ﬂag に関しては、setParent() 関数と同様。

82

void deleteChild(fk Model *c, bool ﬂag)
c が子モデルであった場合、親子関係を解除する。c が子モデルではなかった場合は何も起こらない。ﬂag に関しては、
setParent() 関数と同様。

void deleteChildren(bool ﬂag)
自身に設定されている全ての子モデルに対し、親子関係を解除する。ﬂag に関しては、setParent() 関数と同様。

fk Model * getParent(void)
自身に設定されている親モデルを返す。親モデルが設定されていない場合、nullptr を返す。

fk Model * foreachChild(fk Model *c)
自身に設定されている子モデルを順番に返す。まず c に nullptr が入力されたとき、最初に設定された子モデルが返る。ただし、子モデルが登録されていなかった場合は nullptr が返ってくる。
次に、1 番目に返ってきたモデルを引数として入力したとき、2 番目に設定された子モデルが返ってくる。このようにして、設定されている子モデルを順番に参照することができる。最後の子モデルが引数に入力されたとき、nullptr
が返る。

83

第9章

モデルや光線の干渉・衝突判定

ゲームのようなアプリケーションの場合、物体同士の衝突を検出することは重要な処理である。FK では、モデル同士の干渉や衝突を検出する機能を利用することができる。ここではまず、本書における用語の定義を行う。
干渉判定::
ある瞬間において、物体同士の干渉部分が存在するかどうかを判定する処理のこと。
衝突判定::
ある瞬間から一定時間の間に、物体同士が衝突するかどうかを判定する処理のこと。
干渉判定は、あくまで「ある瞬間」の時点での干渉状態を調べるものである。それに対し衝突判定は、「ある瞬間」において干渉状態になかったとしても、それから一定時間の間に衝突するようなケースも考慮することになる。文献によっては、両者を区別することがなかったり、干渉判定のことを「衝突判定」と呼称しているものも多いが、本書においては両者は厳密に区別する。

9.1 境界ボリューム
3 次元形状は多くの三角形 (ポリゴン) によって構成されており、これら全てに対し干渉判定や衝突判定に必要な幾何計算を行うことは、莫大な処理時間を要することがある。そのため、通常は判定する物体に対し簡略化された形状によって近似的に干渉・衝突判定を行うことが多い。このときの簡略化形状を境界ボリュームと呼ぶ。図 9.1 に境界ボリュームのイメー
ジ図を示す。

図 9.1 境界ボリューム

以下、FK で利用可能な境界ボリュームを紹介する。

9.1.1 境界球
最も簡易な境界ボリュームとして境界球がある。球同士の干渉判定は他の境界ボリュームと比べて最も高速であり、また
FK 上で衝突判定を行える唯一の境界ボリュームである。その反面、長細い形状などでは判定の誤差が大きくなるという難点がある。

9.1.2 軸平行境界ボックス (AABB)「軸平行境界ボックス」(Axis Aligned Boundary Box, 以下「AABB」) は、境界球と並んで処理の高速な干渉判定手法である。AABB では図形を直方体で囲み、その直方体同士で判定を行う。ただし、境界となる直方体の各辺は必ず x, y, z 軸のいずれかと平行となるように配置する。

84

直方体は、一般的に球よりも物体の近似性が高いため、物体の姿勢が常に変化せずに移動のみを行う場合はしばしば最適な手法となる。しかしながら、物体が回転する場合は注意が必要である。それは、物体が回転した場合に AABB の大きさも変化するためである。その様子を図 9.2 に示す。

図 9.2 回転による AABB の変化

9.1.3 有向境界ボックス (OBB)「有向境界ボックス」(Oriented Bounding Box, 以下「OBB」) は、AABB と同様に直方体による境界ボリュームであるが、モデルの回転に伴ってボックスも追従して回転する。モデルが回転してもボックスの大きさは変わらないので、非常に扱いやすい境界ボリュームである。図 9.3 にモデルの回転と追従するボックスの様子を示す。

図 9.3 モデル回転に追従する OBB

しかしながら、OBB の干渉判定処理は実際にはかなり複雑であり、全境界ボリューム中最も処理時間を要する。モデルが少ない場合は支障はないが、多くのモデル同士の干渉判定を行う場合は処理時間について注意が必要である。

9.1.4 カプセル型「カプセル型」とは、円柱に対し上面と下面に同半径の半球が合わさった形状である。モデルが回転した場合は、OBB と同様に方向は追従する。図 9.4 は、カプセル型境界ボリュームがモデルの回転に追従する様子である。

図 9.4 カプセル型の境界ボリューム

カプセル型は、形状は複雑に感じられるが、干渉判定の処理速度は OBB よりもかなり高速である。境界球では誤差が大
きく、境界ボリューム自体もモデルの回転に追従してほしいが、多くのモデル同士の干渉判定が必要となる場面では、カプセル型が最も適していると言える。

85

9.2 モデル同士の干渉判定
9.2.1 干渉判定の基本
干渉判定を行うには、大きく以下の手順を行う。
1. どの境界ボリュームを利用するかを決める。
2. 境界ボリュームの大きさを設定する。
3. (モデルに対し干渉判定関係の設定を行う。)
4. 実際に他のモデルとの干渉判定を行う。
まずはどの境界ボリュームを利用するかを検討しよう。境界ボリューム今のところ 4 種類あり、それぞれ長所と短所があるので、モデルの形状や利用用途を考えて決定しよう。ここでは借りに「OBB」を利用すると想定する。境界ボリュームの種類設定は、setBMode() で行う。以降、サンプルプログラム中の「modelA」、「modelB」という変数は共に fk Model 型であるとする。

modelA . setBMode ( f k_ B ou nd a ry M od e :: OBB );setBMode() の引数の種類は、以下の表 9.1 の通りである。
表 9.1 境界ボリュームの設定値

fk BoundaryMode::SPHERE

境界球

fk BoundaryMode::AABB

軸平行境界ボックス (AABB)

fk BoundaryMode::OBB

有向境界ボックス (OBB)

fk BoundaryMode::CAPSULE

カプセル型

次に、境界ボリュームの大きさを設定する。これは自分自身で具体的な数値を設定する方法と、既にモデルに設定してある形状から自動的に大きさを算出する方法がある。自身で設定する場合は、fk Model クラスの基底クラスとなっている
fk Boundary クラスのメンバ関数を利用することになるので、そちらのリファレンスマニュアルを参照してほしい。自動的に設定する場合は、あらかじめ setShape() で形状を設定しておき、以下のように adjustOBB() を呼び出すだけでよい。

modelA . setShape (& shape );modelA . adjustOBB ();あとは、他のモデルを引数として isInter() 関数を用いれば干渉判定ができる。(もちろん、modelB の方も事前に上記の各種設定をしておく必要がある。)

if ( modelA . isInter (& modelB ) == true ) {// modelA と modelB は 干 渉 し て い る 。
}

86

9.2.2 干渉継続モード
通常、干渉判定はその瞬間において干渉しているかどうかを判定するものであるが、「過去に他のモデルと干渉したことがあるか」を知りたいという場面は多い。そのようなときは、ここで紹介する「干渉継続モード」を利用するとよい。この機能を用いると、(明示的にリセットするまでは) 過去の干渉判定の際に一度でも干渉があったかどうかを取得することができる。
干渉継続モードを有効とするには、setInterMode() 関数で true を引数に入力する。

modelA . setInterMode ( true );その後、干渉が生じたことがある場合は getInterStatus() で true が返ってくる。

if ( modelA . getInt erStat us () == true ) {// 過 去 に 干 渉 が あ っ た 場 合}

この情報をリセットしたい場合は、resetInter() を用いる。

modelA . resetInter ();なお、このモードを利用するときに注意することとして、「過去に干渉があった」というのはあくまで「isInter() を用いて干渉と判断された」ということを意味するということである。実際には他モデルと干渉していたとしても、そのときに
isInter() を用いて判定を行っていなかった場合は、このモードで「過去に干渉があった」とはみなされないので、注意が必要である。

9.2.3 干渉自動停止モード
他の物体と接触したときに、自動的に動きを停止するという制御がしばしば用いられる。例えば、動く物体が障害物に当たった場合に、その障害物にめり込まないようにするという場合である。そのような処理を自動的に実現する方法として「干渉自動停止モード」がある。このモードでは、あらかじめ干渉判定を行う他のモデルを事前に登録しておき、内部で常に干渉判定処理を行うので、自身で干渉判定を行う必要はない。
まずは事前に境界ボリュームの設定を行っておく。その上で、干渉判定を行いたいモデルを entryInterModel() で登録しておく。

modelA . entry I nt er M od el (& modelB );その後、setInterStopMode() で true を引数に与えることで、干渉自動停止モードが ON となる。

modelA . set I n t e r S t o p M o d e ( true );このモデルは、今後移動や回転を行った際に登録した他モデルと干渉してしまう場合、その移動や回転が無効となり、物体は停止する。この判定に関しての詳細は fk Model::setInterStopMode() のリファレンスマニュアルに掲載されている。
87

このモードは便利ではあるが、状況次第ではモデルがまったく動かせなくなってしまう恐れもあるので、状況に応じてOFF にするなどの工夫が必要となることもあるので、注意してほしい。

9.3 モデル同士の衝突判定
衝突判定は、干渉判定と比べると複雑な処理を行うことになるが、物体同士の衝突を厳密に判定できるという利点がある。衝突判定を利用するには、以下のような手順を行う。
1. 境界球の大きさを設定しておく。
2. 衝突判定を行うモデルで事前に snapShot() を呼んでおく。
3. モデルの移動や回転を行う。
4. isCollision() を用いて衝突判定を行う。
5. 衝突していた場合、restore() を衝突した瞬間の位置にモデルを移動させる。
まず、モデルに対し境界球の大きさを設定する。これも半径を直接指定する方法と、adjustSphere() を用いて自動的に設定する方法がある。
次に、衝突判定を行うモデルに対し、毎回のメインループの中で snapShot() を呼ぶ。

while ( true ) {:modelA . snapShot ();modelB . snapShot ();:}その後、モデルの移動や回転などを行った後に、isCollision() で他モデルとの衝突判定を行う。isCollision() 関数は返値として衝突判定結果を返すが、衝突があった場合は第二引数にその時間を返すようになっている。もし衝突していた場合にrestore() を呼ぶようにしておく。

doublet;while ( true ) {:if ( modelA . isCollision (& modelB , & t ) == true ) {modelA . restore ( t );}:}restore() では、引数を省略すると 0 を入力したときと同じ意味となり、その場合は snapShot() を用いた時点での位置と姿勢に戻る。自身のプログラム中での挙動として、適している方を用いること。

9.4 光線とモデルの干渉判定
干渉判定はモデル同士だけではなく、光線とモデルの間でも行うことができる。この機能を使えば、例えばキャラクター
の視線から見える物体の判定や、マウスが指しているモデルを検出することなど、様々な用途に利用することができる。

88

9.4.1

fk Ray による光線設定

光線の生成は、fk Ray クラスを用いて行う。fk Ray 型変数では、始点と終点を設定することで空間上の線として配置される。始点と終点は変数生成時のコンストラクタで設定できる。(コンストラクタで引数を省略した場合、両端点が原点に設定される。)
fk_Vector S (0.0 , 0.0 , 0.0) , E (0.0 , 0.0 , 1.0);
fk_Ray ray (S , E );また、set() 関数を用いて動的に変更することも可能である。

fk_Ray ray ;fk_Vector S (0.0 , 0.0 , 0.0) , E (0.0 , 0.0 , 1.0);
ray . set (S , E );fk Ray が持つ形状の種類は「直線」「半直線」「有向線分」の 3 種類が選択できる。本書では、この 3 種の総称を「直線群」と呼称する。直線群の種類設定は setType() 関数を用いる。

fk_Ray ray ;ray . setType ( fk_LineType :: HALF );設定値は以下の表 9.2 の通りである。
表 9.2 直線群の種類

種類

設定値

直線

fk LineType::OPEN

半直線

fk LineType::HALF

有向線分

fk LineType::CLOSE

光線の位置を直接指定する他に、fk Model の変数を親モデルとして設定することもできる。設定には setParent() 関数を用いる。

fk_Ray ray ;fk_Model model ;ray . setParent (& model );親子関係を用いることで、モデルの移動に追随して光線も移動することができる。例えば、カメラモデルの子モデルとして光線を設定しておけば、カメラの真正面にあるモデルを抽出することができる。

9.4.2 光線とモデルの干渉判定
光線とモデルの干渉判定を行うには、isInter() 関数を用いる。

89

fk_Ray ray ;fk_Model model ;model . setBMode ( f k _B ou n da r yM od e :: OBB );model . adjustOBB ();if ( ray . isInter (& model ) == true ) {// 光 線 と モ デ ル が 干 渉 し て い る 。
}

干渉判定を行う際、モデル側は事前に境界ボリュームの種類と大きさを設定しておく必要がある。境界ボリュームの種類
は、サポートされている全ての種類が使用できる。また境界ボリュームの種類を動的に変更しても問題ない。
複数のモデルに対して、一括して干渉判定を行いたい場合、entryModel() と getInterList() を用いると便利である。以下のプログラムは、複数のモデルを干渉判定対象として事前に登録するプログラムである。

fk_Ray ray ;fk_Model model [100];for ( int i = 0; i < 100; ++ i ) {model [ i ]. setBMode ( f k_ B ou nd a ry M od e :: OBB );model [ i ]. adjustOBB ();ray . entryModel (& model [ i ]);}

登録したモデルに対して、一括干渉判定を行うには getInterList() を用いる。

fk_Ray ray ;fk_Model model [100];
std :: list < std :: tuple < fk_Model * > , double > > list ;fk_Model * interModel = nullptr ;// ( model [0] 〜 model [99] が 事 前 に 登 録 さ れ て い る も の と す る 。
ray . getInterList (& list , true );if ( list . empty () == false ) {interModel = std :: get <0 >( list . front ());}

getInterList() を用いると、list に干渉している全てのモデルが入る。一つも干渉していない場合、list は空となるので、まずはそのチェックを行う。getInterList() の第 2 引数は、干渉モデルを始点に近い順にソートするかどうかの設定である。
もし始点に一番近いモデルを取得したいのであれば、true にしておくとよい。(false の場合は entryModel() での登録順となる。) getInterList() の詳細は、リファレンスマニュアルを参照してほしい。

90

第 10 章シーン

この章ではシーンと呼ばれる概念と、それを FK システム上で実現した fk Scene というクラスに関しての使用法を述べる。
シーンは、複数のモデルとカメラからなる「場面」を意味する。シーンには複数の描画するためのモデル及びカメラを示
すモデルを登録する。このシーンをウィンドウに設定することによって、そのシーンに登録されたモデル群が描画される仕組みになっている。
このシーンは、(fk Scene クラスのオブジェクトというかたちで) 複数存在することができる。これは、あらかじめ全く異なった世界を複数構築しておくことを意味する。状況によって様々な世界を切替えて表示したい場合には、どのシーンをウィンドウに設定するかをうまく選択していけばよい。

10.1 モデルの登録モデルの登録は、entryModel() というメンバ関数を用いる。これは次のように使用される。

fk_Model model1 , model2 , model3 ;fk_Scene scene ;scene . entryModel (& model1 );scene . entryModel (& model2 );scene . entryModel (& model3 );モデルを登録する際、引数はポインタとして渡す。scene オブジェクトの中でこのポインタが参照される。登録したモ
デルをリストから削除したい場合は、removeModel() 関数を用いる。もし、シーン中に登録されていないモデルに対して
removeModel() を用いた場合には、特に何も起こらない。以下の例は、DrawModeFlag が true の場合はモデルをシーンに登録し、そうでない場合はモデルをシーンから削除する。

fk_Model model1 ;fk_Scene disp ;bool DrawModelFlag ;::if ( DrawModelFlag == true ) {scene . entryModel (& model1 );} else {scene . removeModel (& model1 );}また、一旦シーン中のモデルを全てクリアしたい場合には、clearModel() という関数を呼ぶことで実現できる。
透明度が設定されているモデルがある場合、シーンへの登録の順序によって結果が異なることがある。具体的に述べる

91

と、透明なモデルの後に登録されたモデルは、透明なモデルの裏側にあっても表示されなくなる。これを防ぐには、透明な立体を常にディスプレイリストのできるだけ後ろに登録しておく必要がある。具体的には、別のモデルを登録した後に透明なモデルを entryModel() メンバ関数によって再び登録しなおせばよい1）。
ちなみに、実際に描画の際に透過処理を行うには fk Scene オブジェクトにおいて setBlendStatus() メンバ関数を用いて透過処理の設定を行う必要がある。これは第 10.4 節に詳しく述べる。

10.2 カメラ (視点) の設定
カメラ (視点) の設定は、モデル (のポインタ) を entryCamera() メンバ関数によって代入することによって行われる。

fk_Model camera ;fk_Scene scene ;scene . entryCamera (& camera );entryCamera() によってカメラが登録された場合、カメラを意味するオブジェクトの位置や方向が変更されれば、再代入する必要なくカメラは変更される。これは多くの場合都合がよい。もし、別のモデルをカメラとして利用したいのならば、別のモデルのポインタを代入すればよい。

10.3 背景色の設定
FK システムでは、背景色はデフォルトで黒に設定されているが、次のように fk Scene の setBGColor() メンバ関数を用いることで背景色を変更することができる。引数としては、RGB 要素を示す 0 〜 1 までの数値 3 つ、あるいは fk Color 型の変数 1 つの 2 種類がある。以下の例は、背景色を青色に設定している例である。

fk_Scene scene ;::scene . setBGColor (0.0 , 0.0 , 1.0);

10.4 透過処理の設定
モデルのマテリアルにおいて、setAlpha() メンバ関数を用いて立体に透明度を設定することが可能であるが、実際に透過処理を行うには fk Scene クラスのオブジェクトにおいて setBlendStatus() メンバ関数を用いて設定を行わなければならない。具体的には、次のように引数に true を入力することによって透過処理の設定が行える。

fk_Scene scene ;::scene . setBlendSta tus ( true );透過処理を無効にしたい場合は、false を入力すればよい。もし透過処理を ON にした場合、(実際に透明なモデルが存在するか否かに関わらず) 描画処理がある程度低速になる。
1） わざわざ removeModel() を呼ばなくても、自動的に重複したモデルはシーンから削除されるようになっている。

92

10.5 霧の効果
FK システムでは、シーン全体に霧効果を出す機能がサポートされている。まず、霧効果の典型的な利用法を 10.5.1 節で解説し、霧効果の詳細な利用方法を 10.5.2 節で述べる。

10.5.1 霧効果の典型的な利用方法
霧効果は、次の 4 項目を設定することで利用できる。
• 減衰関数の設定。
• オプションの設定。
• 係数の設定。
• 霧の色設定。
これを全て行うコード例は、以下のようなものである。

fk_Scene scene ;scene . setFogMode ( fk_FogMode :: LINEAR );scene . setFogOption ( fk_FogOption :: FASTEST );scene . s etFog Li n ea rM a p (0.0 , 400.0);
scene . setFogColor (0.3 , 0.4 , 1.0 , 0.0);

まず、setFogMode() によって減衰関数を指定する。通常は、例にあるように fk FogMode::LINEAR を指定すれば良い。
次に、setFogOption() でオプションを指定する。これは 10.5.2 節で述べるような 3 種類があるので、目的に応じて適切に設定する。
次に、setFogLinearMap() によって霧効果の現れる領域を設定する。最初の数値が霧が出始める距離、後の数値が霧によって何も見えなくなる距離である。
最後に、setFogColor() によって霧の色を設定する。通常は背景色と同一の色を設定すればよい。また、最後の透過度数
値は 0 を指定すれば良い。
以上の項目を設定するだけで、シーンに霧効果を出すことが可能となる。より詳細な設定が必要な場合は、次節を参照すること。

10.5.2 霧効果の詳細な利用方法
霧効果に関連する機能として、各関数の解説を述べる。
void setFogMode(fk FogMode mode)
霧による減衰の関数を設定する。表 10.5.2 にある項目入力できる。各数値の設定はその他の設定関数を参照すること。
表 10.1 減衰関数一覧

fk FogMode::EXP

E−zが選択される。
E−S
減衰関数として e−dz が選択される。

fk FogMode::EXP2減衰関数として e−(dz) が選択される。

fk FogMode::NONE

霧効果を無効とする。

fk FogMode::LINEAR

減衰関数として2

なお、デフォルトでは fk FogMode::NONE が選択されている。

93

void setFogOption(fk FogOption opt)
霧効果における描画オプションを設定する。以下のような項目が入力できる。
表 10.2 霧効果オプション一覧

fk FogOption::FASTEST

描画の際に、速度を優先する。

fk FogOption::NICEST

描画の際に、精細さを優先する。

fk FogOption::NOOPTION 特にオプションを設定しない。
なお、デフォルトでは fk FogOption::NOOPTION が選択されている。

void setFogDensity(double d)
減衰関数として fk FogMode::EXP (e−dz ) 及び及び fk FogMode::EXP2 (e−(dz) ) が選択された際の、 減衰指数係数 d2を設定する。ここで、z はカメラから対象地点への距離を指す。

void setFogLinearMap(double S, double E)
E−z
減衰関数として FK LINEAR FOG () が選択された際の、減衰線形係数 S, E を設定する。もっと平易に述べる
E−Sと、霧効果が現れる最初の距離 S と、霧で何も見えなくなる距離 E を設定する。

void setFogColor(fk Color col)void setFogColor(ﬂoat R, ﬂoat G, ﬂoat B, ﬂoat A)
void setFogColor(double R, double G, double B, double A)
霧の色を設定する。大抵の場合、背景色と同一の色を指定し、透過度は 0 にしておく。

10.6 オーバーレイモデルの登録表示したい要素の中には、物体の前後状態に関係なく常に表示されてほしい場合がある。例えば、画面内に文字列を表示
する場合などが考えられる。このような処理を実現するため、通常のモデル登録とは別に「オーバーレイモデル」として登録する方法がある。モデルをオーバーレイモデルとしてシーンに登録した場合、表示される大きさや色などは通常の場合と変わらないが、描画される際に他の物体よりも後ろに存在していたとしても、常に全体が表示されることになる。
オーバーレイモデルは一つのシーンに複数登録することが可能である。その場合、表示は物体の前後状態は関係なく、後に登録したモデルほど前面に表示されることになる。
基本的には、通常のモデル登録と同様の手順でオーバーレイモデルを登録することができる。オーバーレイモデルを扱う
メンバ関数は、以下の通りである。
void entryOverlayModel(fk Model *model)
モデルをオーバーレイモデルとしてシーンに登録する。「model」が既にオーバーレイモデルとして登録されていた場
合は、そのモデルが最前面に移動する。

void removeOverlayModel(fk Model *model)「model」がオーバーレイモデルとして登録されていた場合、リストから削除する。

void clearOverlayModel(void)
全ての登録されているオーバーレイモデルを解除する。

94

第 11 章ウインドウとデバイス

FK システムでは、ウィンドウを制御するクラスとして fk AppWindow クラスと fk Window クラスを提供している。
fk AppWindow は簡易に様々な機能を実現できるものであり、実装を容易に行うことを優先したものとなっている。
fk Window は fk AppWindow よりも利用方法はやや複雑であるが、多くの高度な機能を持っており、マルチウィンドウやGUI と組み合わせたプログラムを作成することができる。
本章では、まず fk AppWindow による機能を紹介し、その後に fk Window 固有の機能について解説を行う。

11.1 ウィンドウの生成
ウ ィ ン ド ウ は、一 般 的 な ウ ィ ン ド ウ シ ス テ ム に お い て 描 画 を す る た め の 画 面 単 位 で あ る。FK シ ス テ ム で は、
fk AppWindow クラスのオブジェクトを作成することによって 1 つのウィンドウを生成できる。

fk_AppWindow window ;fk AppWindow に対して最低限必要な設定は大きさの設定である。以下のように、setSize() 関数を使ってピクセル単位
で大きさを指定する。

window . setSize (600 , 600);

また、背景色を設定するには setBGColor() を用いる。

window . setBGColor (0.3 , 0.5 , 0.2);

setBGColor() は fk Color 型変数を引数に取ることもできる。
これらの設定を行った後、open() 関数を呼ぶことで実際にウィンドウが画面に表示される。

window . open ();11.2 ウィンドウの描画
ウィンドウの描画は、ウィンドウ用の変数 (インスタンス) で update() 関数を呼び出すことで行われる。この関数が呼ばれた時点で、リンクされているシーンに登録されている物体が描画される。
update() 関数は、ウィンドウが正常に描画された場合に true を、そうでない場合は false を返す。false を返すケースは、ウィンドウが閉じられた場合となる。そのため95

while ( window . update () == true ) {:}というコードでは、ウィンドウが閉じられると while ループを脱出するようになる。これを踏まえ、実際の描画ループは次のようになる。

fk_AppWindow window ;window . setSize (600 , 600);
window . setBGColor (0.1 , 0.2 , 0.3);
window . open ();while ( window . update () == true ) {:: // モ デ ル の 制 御:}

なお、生成されたウィンドウは通常の OS によるウィンドウ消去の方法以外に、ESC キーを押すことで消去する機能がある。

11.3 座標軸やグリッドの表示
fk AppWindow には座標軸やグリッドを表示する機能が備わっている。座標軸とは、原点から座標軸方向に描画される線分のことである。また、グリッドとは座標平面上に表示されるメッシュのことである。
座標軸とグリッドの表示は共に showGuide() 関数を用いて行われる。表示したい対象を以下のように並べて指定する。

window . showGuide ( fk_Guide :: AXIS_X | fk_Guide :: AXIS_Y | fk_Guide :: AXIS_Z | fk_Guide :: GRID_XZ );showGuide() で指定できる項目は以下の表 11.1 の通りである。
表 11.1 座標軸・グリッドの指定項目

fk Guide::AXIS X

x軸

fk Guide::AXIS Yy軸

fk Guide::AXIS Z

z軸

fk Guide::GRID XY xy 平面上のグリッド
fk Guide::GRID YZ

yz 平面上のグリッド

fk Guide::GRID XZ

xz 平面上のグリッド

なお、引数を省略した場合は x, y, z 各座標軸と xz 平面グリッドが表示される。
座標軸の長さやグリッドの幅、数などは以下の関数によって制御可能である。
hideGuide(): 座標軸・グリッドを消去する。
setGuideScale(double s):
setGuideNum(int n):

グリッドの幅を s に設定する。

グリッドの分割数を n に設定する。
96

11.4 デバイス情報の取得
fk AppWindow 上でのマウスやキーボードの状態を調べるため、fk AppWindow クラスは様々なメンバ関数を提供している。ここでは、それらの使用法を説明する。

11.4.1 getKeyStatus() メンバ関数
この関数は、キーボード上の文字キーが現在押されているかどうかを調べるための関数である。たとえば、’g’ というキーが押されているかどうかを調べたければ、

if ( window . getKeyStatus ( ’g ’ , fk_Switch :: PRESS ) == true ) {: // キ ー が 押 さ れ た 時 の 処 理 を 行 う 。
}という記述を行う。この関数は、1 番目の引数にキーを表す文字を代入する。2 番目の引数はどのようなキーの状態を検知するかを設定するもので、以下の表 11.2 に挙げている種類がある。
表 11.2 キーの状態設定

fk Switch::RELEASE 離しっぱなしの状態
fk Switch::UP

離した瞬間

fk Switch::DOWN

押した瞬間

fk Switch::PRESS

押しっぱなしの状態

上記のサンプルプログラムは「押した状態にあるかどうか」を検知するものであるが、「押した瞬間」であるかどうかを検知する場合は「fk Switch::PRESS」のかわりに「fk Switch::DOWN」を第 2 引数に入力する。
なお、第 2 引数が fk Switch::PRESS の場合は省略が可能である。先述のプログラムは、

if ( window . getKeyStatus ( ’g ’) == true ): // キ ー が 押 さ れ た 時 の 処 理 を 行 う 。
}

としてもよい。

11.4.2 特殊キーの状態取得
エンターキーやシフトキーなど、文字として表現できないキーについては、getKeyStatus() 関数の第 1 引数に fk Key 型の特定の値を入力すればよい。たとえば、左シフトキーが押されているかどうかを調べたければ、

if ( window . getKeyStatus ( fk_Key :: SHIFT_L ) == true ) {: // キ ー が 押 さ れ て い る 時 の 処 理 を 行 う 。
}

といったような記述を行う。特殊キーと関数の引数の対応は以下の表 11.3 のとおりである。

97

表 11.3 特殊キーと引数値の対応

引数名

対応特殊キー

引数名

対応特殊キー

fk Key::SHIFT R

右シフトキー

fk Key::CAPS LOCK

Caps Lock キー

fk Key::SHIFT L

左シフトキー

fk Key::PAGE UP

Page Up キー

fk Key::CTRL R

右コントロールキー

fk Key::PAGE DOWN

Page Down キー

fk Key::CTRL L

左コントロールキー

fk Key::HOME

Home キーfk Key::ALT R

右 ALT キーfk Key::END

End キー

fk Key::ALT L

左 ALT キーfk Key::INSERT

Insert キー

fk Key::ENTER

改行キー

fk Key::RIGHT

右矢印キー

fk Key::BACKSPACE

Back Space キー

fk Key::LEFT

左矢印キー

fk Key::DELETE

Del キー

fk Key::UP

上矢印キー

fk Key::TAB

Tab キー

fk Key::DOWN

下矢印キー

fk Key::F1 〜 fk Key::F12

F1 〜 F12 キー

fk Key::SPACE

スペースキー

11.4.3 getMousePosition() 関数
この関数は、現在のマウスポインタの位置を調べる時に使用する。使い方は、

fk_Vector pos ;fk_AppWindow window ;::pos = window . g e t M o u s e P o s i t i o n ();というように、fk Vector 型の変数にマウスポインタのウィンドウ座標系による現在位置を得られる。ウィンドウ座標系では、描画領域の左上部分が原点となり、x 成分は右方向、y 成分は下方向に正となり、数値単位はピクセルとなる。

getMouseStatus() 関数
こ の 関 数 は、現 在 マ ウ ス ボ タ ン が 押 さ れ て い る か ど う か を 調 べ る 時 に 使 用 す る。引 数 値 と し て 左 ボ タ ン が
fk MouseButton::M1、中ボタンが fk MouseButton::M2、右ボタンが fk MouseButton::M3 に対応しており、

fk_AppWindow window ;if ( window . getMou seStat us ( f k_Mous eButton :: M1 , fk_Switch :: PRESS ) == true ) {:// 左 ボ タ ン が 押 さ れ て い る 。

:}

といった様にして現在のボタン状態を調べることができる。この関数も他と同様に、fk AppWindow 上にマウスポインタがない場合は常に false が返ってくる。

9811.5 ウィンドウ座標と 3 次元座標の相互変換
3D のアプリケーションを構築する際、ウィンドウ中のある場所が、3 次元空間ではどのような座標になるのかを知りたいということがしばしば見受けられる。あるいは逆に、3 次元空間中の点が実際にウィンドウのどの位置に表示されるのかをプログラム中で参照したいということもよくある。FK でこれを実現する方法として h、fk AppWindow クラスに
getWindowPosition(), getProjectPosition() というメンバ関数が準備されている。以下に、3 次元からウィンドウへの変換、ウィンドウから 3 次元への変換を述べる。

11.5.1 3 次元座標からウィンドウ座標への変換
3 次元空間中のある座標は、fk AppWindow クラスの getWindowPosition() というメンバ関数を用いることで、ウィンド
ウ中で実際に表示される位置を知ることができる。引数として入力、出力を表す fk Vector 型の変数を取る。以下に例を示す。

fk_Vector in ;fk_AppWindow window ;::auto [ status , out ] = win . g e t W i n d o w P o s it ion ( in );ここで、in には元となる 3 次元空間の座標を設定しておく。出力となる 2 番目の引数は getMousePosition() の場合と同様にアドレス渡しをする必要がある。これにより、out の x 成分、y 成分にそれぞれウィンドウ座標が設定される。なお、この場合の out の z 成分には 0 から 1 までのある値が入るようになっており、カメラから遠いほど高い値が設定される。また、status は算出に成功した場合に true が、失敗した場合に false が入る。

11.5.2 ウィンドウ座標から 3 次元座標への変換
3 次元→ウィンドウの場合と比べて、ウィンドウ座標から 3 次元座標への変換はやや複雑である。というのも、3 次元座標からウィンドウ座標へ変換する場合は、結果が一意に定まるのであるが、その逆の場合は単にウィンドウ座標だけでは 3次元空間中の位置が決定しないからである。もう少し具体的に述べると、本来得たい空間中の位置とカメラ位置を結ぶ直線(以下これを「指定直線」と呼ぶ) が求まるが、その直線上のどこなのかを特定するにはもう 1 つの基準を与えておく必要がある。FK ではこの基準として• カメラからの距離• 任意平面の 2 種類を用意している。
まずカメラからの距離によって指定する方法を紹介する。3 次元空間上の座標を取得するには getProjectPosition() メン
バ関数を利用する。引数は以下の通りである。

g e tP r o j e c t P o s i t i o n ( ウ ィ ン ド ウ x 座 標 , ウ ィ ン ド ウ y 座 標 , 距 離 ) ;例えば、以下の例は現在のマウスが指す 3 次元空間の座標を得るプログラムである。このプログラム中ではカメラからの距離を 500 としている。

fk_AppWindow window ;

99

:
:
auto [ status1 , pos ] = win . g e t M o u s e P osition ();auto [ status2 , out ] = win . g e t P r o j e c t Po s it i on ( pos .x , pos .y , 500.0);

もう 1 つの方法として、平面を指定する方法がある。前述の指定直線と与えた平面が平行でないならば、その交点を出力することになる。これは、例えば xy 平面上の点や部屋の壁のようなものを想定するような場合に便利である。
まずは平面を作成する必要があるが、これは fk Plane というクラスの変数を利用する。平面指定の方法として、• 平面上の任意の 1 点と平面の法線ベクトルを指定する。
• 平面上の (同一直線上にない) 任意の 3 点を指定する。
• 平面上の任意の 1 点と、平面上の互いに平行ではない 2 つのベクトルを指定する。
の 3 種類があり、以下のように指定する。

fk_Planeplane ;// 平 面 を 表 す 変 数::// 1 点 + 法 線 ベ ク ト ル の パ タ ー ン//

pos ... 平 面 上 の 任 意 の 1 点 で 、 fk_Vector 型//

norm .. 平 面 の 法 線 ベ ク ト ル で 、 fk_Vector 型

plane . setPosNormal ( pos , norm );// 3 点 の パ タ ー ン (3 点 は 同 一 直 線 上 に あ っ て は な ら な い )pos1 〜 pos3 ... 平 面 上 の 任 意 の 点 で 、 全 て fk_Vector 型//

plane . set3Pos ( pos1 , pos2 , pos3 );// 1 点 + 2 つ の ベ ク ト ル の パ タ ー ン//

pos ... 平 面 上 の 任 意 の 1 点 で 、 fk_Vector 型//

uVec .. 平 面 に 平 行 な ベ ク ト ル ( f k _ V e c t o r 型 )//vVec .. 平 面 に 平 行 な ベ ク ト ル で 、 uVec に 平 行 で な い も の ( fk_Vector 型 )plane . setPosUVVec ( pos , uVec , vVec );これにより、平面が生成できたら、以下の形式で 3 次元空間中の座標を取得することができる。

g e tP r o j e c t P o s i t i o n ( ウ ィ ン ド ウ x 座 標 成 分 , ウ ィ ン ド ウ y 座 標 成 分 , 平 面 ) ;ちなみに、平面と出力変数はアドレス渡しにしておく必要がある。以下の例は、マウス位置が指している場所の xy 平面上の座標を得るサンプルである。

fk_Vector outPos ; // 出 力 用 変 数
fk_AppWindow win ; // ウ ィ ン ド ウ 変 数
fk_Vector pos ; // マ ウ ス 座 標 用 変 数
fk_Plane plane ; // 平 面 を 表 す 変 数
fk_Vector planePos , planeNorm ; // 平 面 生 成 用 変 数::// 平 面 の 任 意 点 と 法 線 ベ ク ト ル を 設 定 す る 。
100

planePos . set (0.0 , 0.0 , 0.0);
planeNorm . set (0.0 , 0.0 , 1.0);// 情 報 を 平 面 に 設 定
plane . setPosNormal ( planePos , planeNorm );// ウ ィ ン ド ウ か ら マ ウ ス 座 標 を 得 る 。
std :: tie ( ignore , pos ) = win . g e t M o u s e Position ();// ウ ィ ン ド ウ 座 標 と 平 面 か ら 、 3 次 元 空 間 中 の 座 標 を 得 る 。
std :: tie ( ignore , outPos ) = win . g e t P r o j ec t Po s it i on ( pos .x , pos .y , & plane );11.6 高度なウィンドウ制御
FK では、ウィンドウ用のクラスとして fk AppWindow の他に fk Window というクラスがある。fk AppWindow が簡易的な制御を優先した設計であるのに対し、fk Window はより高度な機能を用いる際に用いられるものである。この節では、fk Window クラスの主要な機能について説明する。ただし、このマニュアル中だけでは全てを掲載することはかなりの文量を要してしまうため、概略だけに留める。詳細はリファレンスマニュアルを参照してほしい。

11.6.1 ウィンドウの生成
fk Window クラスは内部的に FLTK と呼ばれる GUI ツールキットを用いており、まず FLTK のメインウィンドウを開き、その中に FK システム用の描画ウィンドウを生成する。
手順としては、まず FLTK 用のウィンドウオブジェクトである Fl Window クラスのオブジェクトを生成し、その後にfk Window クラスのオブジェクトを定義する。

Fl_Window mainWin (320 , 320 , " FK ␣ Test " );fk_Window window (10 , 10 , 300 , 300);
mainWin . end ();ここで、Fl Window オブジェクトの引数はそれぞれ幅、高さ、タイトルバーに表示する文字列を表す。fk Window オブ
ジェクトの引数は (x, y, w, h) とすると (x, y) はメインウィンドウとの相対位置を、(w, h) は fk Window 自体の大きさを表す。mainWin.end() は、全ての fk Window オブジェクトを定義し終わった後に呼ぶ関数である。したがって、fk Window
オブジェクトを 2 つ利用してマルチウィンドウなプログラムを作りたい場合には、

Fl_Window mainWin (640 , 320 , " Multi ␣ Win ␣ Test " );fk_Window window1 (10 , 10 , 300 , 300);
fk_Window window2 (330 , 10 , 300 , 300);
mainWin . end ();というようにすればよい。また、ウィンドウを実際に開く関数は show() 関数である。具体的には、

mainWindow . show ();window . show ();101

というように、ウィンドウを実際に開きたい部分で show() 関数を呼び出せばよい。

11.6.2 シーンの設定
fk Window ク ラ ス は fk AppWindow ク ラ ス と は 異 な り、シ ー ン 制 御 機 能 を 自 身 で は 保 持 し て い な い。そ の た め、
fk Window クラスを用いる際には fk Scene クラスのインスタンスを作成し、そのインスタンスでシーン制御を行う必要がある。
作成した fk Window クラスのウィンドウに対し、次のように setScene() 関数を用いることによって fk Scene クラス内のシーンをウィンドウ中に表示することができる。

Fl_Window mainWin
fk_Window window ;fk_Scene scene ;mainWin . end ();::window . setScene (& scene );11.6.3 ウィンドウの描画
ウィンドウの描画は、drawWindow() 関数を用いて行う。

window . drawWindow ();この関数が呼ばれた時点で、リンクされているシーンに登録されている物体が描画される。ただし、この関数はウィンド
ウが実際にまだ開いていないときにも描画されてしまうため、そのまま使用した場合は誤動作を起こす場合がある。そこ
で、ウィンドウが実際に開いているかどうかを判定する関数として winOpenStatus() 関数が用意されている。これは true
か false かを返し、ウィンドウが開いている場合には true を返す。また、FLTK が提供しているチェック関数もここで呼んでおかなければならない。これは、Fl Window クラスの visible() 関数と、Fl::check() を用いる。
これを踏まえ、実際の描画ループは次のようになる。

Fl_Window mainWindow (320 , 320 , " FK ␣ TEST " );fk_Window window (10 , 10 , 300 , 300);
mainWindow . end ();mainWindow . show ();window . show ();while ( true ) {// メ イ ン ウ ィ ン ド ウ が 開 い て い る か ど う か の チ ェ ッ ク
if ( mainWindow . visible () == 0) {// 開 い て い な か っ た ( 最 小 化 の 状 態 の ) 場 合
if ( Fl :: wait () == 0) {// 終 了 命 令 が で た 場 合break ;
} else {// そ の ま ま ル ー プ の 最 初 に 戻 る
continue ;

102

}
}
// 描 画 処 理
if ( window . drawWindow () == 0) break ;// FLTK か ら 重 大 な エ ラ ー の 報 告 が な い か を チ ェ ッ ク
if ( Fl :: check () == 0) break ;// fk_Window が 描 画 で き る 状 態 に な っ て い る か を チ ェ ッ ク
if ( window . winOpenStatus () == false ) continue ;::// モ デ ル の 制 御:}}drawWindow() 関数は、もし window 上で ESC キーが押された場合には 0 は返ってくるようになっている。従って、例のプログラムの場合は ESC が押されると終了するようになっている。

11.6.4 デバイス情報取得と座標系変換
fk Window におけるデバイス情報取得機能は、fk AppWindow とほぼ互換のメンバ関数が準備されている。具体的な利用方法は 11.4 節を参照されたい。
また、11.5 節で説明した座標系変換の機能も fk AppWindow と共通である。

11.6.5 ウィンドウ表示状態の画像情報取り込み
fk Window に表示されている状態を、画像ファイルや画像データに取り込みたい場合は、snapImage() というメンバ関数を利用する。仕様は以下の通りである。
bool snapImage(string ﬁleName, fk ImageType type, fk SnapProcMode mode)
現在ウィンドウに表示されている画像を、指定した画像形式で ﬁleName にあるファイル名で保存する。type は以下のいずれかを指定する。
• fk ImageType::BMP (Windows Bitmap 形式)• fk ImageType::PNG (PNG 形式)、• fk ImageType::JPG (JPEG 形式)
また、mode はどの画像バッファから情報を取得するかを指定する物で、以下のいずれかを設定する。
• fk SnapProcMode::FRONT (OpenGL フロントバッファ)• fk SnapProcMode::BACK (OpenGL バックバッファ)• fk SnapProcMode::WIN32 GDI (Windows のみ)
type と mode はデフォルト引数が設定されており、省略可能である。省略した場合、type は fk ImageType::BMP、mode は fk SnapProcMode::FRONT となる。
出力に成功すれば true を、失敗すれば false を返す。

bool snapImage(fk Image *image, fk SnapProcMode mode)
現在ウィンドウに表示されている画像を、fk Image 型の変数に格納する。mode に関しては上記のファイル出力の場合と同様である。また、mode はデフォルト引数が設定されており、省略可能である。省略した場合 mode は
fk SnapProcMode::FRONT となる。
出力に成功すれば true を、失敗すれば false を返す。

103

引数としてファイル名を取る物と fk Image 型 (のポインタ) 変数を取るものがある。前者は、指定されたファイル名で指「image.bmp」
定した形式で保存するものである。後者は、fk Image 型の変数に画像情報を格納する。以下のプログラムは、というファイルと「image」変数にそれぞれ表示画像を格納するものである。

fk_Window win (0 , 0 , 300 , 300);
fk_Image image ;win . snapImage ( " image . bmp " , fk_ImageType :: BMP , fk_SnapProcMode :: BACK );win . snapImage (& image , f k _S na p Pr oc M od e :: BACK );11.6.6 ウィンドウのサイズ変更
fk Window で生成したウィンドウ領域を途中で変更したい場合には、resizeWindow() というメンバ関数を利用する。この関数は、引数として生成時と同様に 4 つの数値を取る。

fk_Window win (0 , 0 , 300 , 300);
win . resizeWindow (0 , 0 , 200 , 200);

この関数は、元の Fl Window の大きさ変更に追随する機能を持たせる時に利用することができる。次のプログラムは、
ウィンドウの大きさ変更を可能とする典型的なサンプルである。

Fl_Window

mainWin (300 , 300 , " Resize ␣ Test " );fk_Window

fkWin (0 , 0 , 300 , 300);

mainWin . end ();// 全 体 の 大 き さ の 最 小 値 を (100 , 100) に セ ッ ト 。
mainWin . size_range (100 , 100);
mainWin . show ();fkWin . show ();while ( true ) {// ウ ィ ン ド ウ の 大 き さ を mainWin と 同 一 に 合 わ せ る 。
fkWin . resizeWindow (0 , 0 , mainWin . w () , mainWin . h ());// 後 は 通 常 と 共 通
if ( mainWin . visible () == 0) {if ( Fl :: wait () == 0) {break ;
} else {continue ;}}if ( fkWin . drawWindow () == 0) break ;if ( Fl :: check () == 0) break ;if ( fkWin . winOpenStatus () == false ) continue ;
:
:

104

}

11.6.7 テクスチャメモリの解放
テクスチャマッピングを行う際、何度も画像の切り替えを行っているとアプリケーションが利用するメモリ量が非常に増加することがある。これは、テクスチャの画像情報をシステムが保持し続けることが原因である。そこで、これらのメモリをクリアする clearTextureMemory(void) 関数を利用することで、一旦システムが保持したメモリを解放することができる。
ただし、この関数によってメモリを解放した後は、テクスチャの画像情報を再度読み込み直すことになるので、処理時間が増える可能性もある。適宜タイミングを調整してほしい。

11.6.8 メッセージ出力
fk Window には、メッセージを出力する機能として以下のようなメンバ関数が提供されている。
void setPutStrMode(fk PutStrMode mode)
メッセージを出力する際の、出力方法を指定する。引数として選択できるものは、以下の 11.6.8 のとおりである。
表 11.4 出力モード値

意味

fk PutStrMode::BROWSER

メッセージ出力用ブラウザに出力する。

fk PutStrMode::CONSOLE

標準出力に出力する。

fk PutStrMode::ERR CONSOLE エラー出力に出力する。
fk PutStrMode::FILE

setPutFile() 関数で指定したファイルに出力する。

fk PutStrMode::NONE

出力を行わない。

デフォルトは fk PutStrMode::BROWSER となっている。

fk PutStrMode getPutStrMode(void)
現在のメッセージ出力モードを返す。

bool setPutFile(string ﬁleName)
メッセージ出力のモードとして「fk PutStrMode::FILE」を指定した場合の出力先となるファイル名を指定する。この関数が呼ばれた時点で、指定したファイルが存在していなかった場合は新規に空ファイルが作成される。既に存在していた場合は、その中身を消去せずに最後部分から後にメッセージを出力する。ファイル書き込みの準備に成功した場合
true を、失敗した場合に false を返す。

void putString(string message)
message の内容を出力する。

void printf(char *format, ...)
標準の printf 関数に準拠した書式指定に従って文字列を出力する。

void clearBrowser(void)
メッセージ出力用ブラウザの内容を消去する。

105

デフォルトの出力ブラウザを利用してメッセージを出力する場合は、単に putString() 関数あるいは printf() 関数を利用すればよい。以下のプログラムは、printf() を用いたサンプルである。

fk_Window window (10 , 10 , 300 , 300);
int counter ;::counter = 0;
while ( true ) {::counter ++;window . printf ( " counter ␣ = ␣ % d " , counter );}また、ここで紹介した関数群は fk Window のメンバ関数ではあるが、fk Window 型の変数がなくても関数名の前に「fk_Window::」を付加することによって、どこでも利用ができる1）。以下にサンプルを示す。

std :: string

outString ;int value ;::fk_Window :: putString ( outString );fk_Window :: printf ( " value ␣ = ␣ % d " , value );1） これは、これらの関数が static 宣言をしているためである。

106

第 12 章簡易形状表示システム

11 章では、FK システムでの多彩なウィンドウやデバイス制御機能を紹介したが、中には FK システムで作成した形状を簡単に表示し、様々な角度から閲覧したいという用途に用いたい利用者もいるであろう。そのようなユーザは、
fk AppWindow や fk Window によって閲覧する様々な機能を構築するのはやや手間である。そこで、FK システムではより簡単に形状を表示する手段として fk ShapeViewer というクラスを提供している。

12.1 形状表示
利用するには、まず fk ShapeViewer 型の変数を定義する。

fk_ShapeView erviewer ;この時点で、多くの GUI が付加したウィンドウが生成される。形状は、4 章で紹介したいずれの種類でも利用できるが、ここでは例として fk IndexFaceSet 型及び fk Sphere 型の変数を準備する。この変数が表す形状を表示するには、
setShape() メンバ関数を用いる。setShape() メンバ関数は二つの引数を取り、最初の引数は立体 ID を表す整数、後ろの引数には形状を表す変数 (のアドレス) を入力する。複数の形状を一度に表示する場合は、ID を変えて入力していくことで実現できる。

fk_ShapeView erviewer ;fk _I nde xF ace Se t solid ;fk_Sphere

sphere ;viewer . setShape (0 , & solid );viewer . setShape (1 , & sphere );あとは、draw() 関数を呼ぶことで形状が描画される。通常は、次のように繰り返し描画を行うことになる。

while ( viewer . draw () == true ) {// も し 形 状 を 変 形 す る な ら ば 、 こ こ に 処 理 内 容 を 記 述 す る 。
}

もし終了処理 (ウィンドウが閉じられる、「Quit」がメニューで選択されるなど) が行われた場合、draw() 関数は false を返すので、その時点で while ループを抜けることになる。形状変形の様子をアニメーション処理したい場合には、while 文の中に変形処理を記述すればよい。具体的な変形処理のやり方は、5.3 節及び 13.5 節に解説が記述されている。

107

12.2 標準機能
この fk ShapeViewer クラスで生成した形状ブラウザは、次のような機能を GUI によって制御できる。これらの機能は、何もプログラムを記述することなく利用することができる。
• VRML、STL、DXF 各フォーマットファイル入力機能と、VRML ファイル出力機能。
• 表示されている画像をファイルに保存。
• 面画、線画、点画の各 ON/OFF 及び座標軸描画の ON/OFF。
• 光源回転有無の制御。
• 面画のマテリアル及び線画、点画での表示色設定。
• GUI によるヘディング、ピッチ、ロール角制御及び表示倍率、座標軸サイズの制御。
• 右左矢印キーによるヘディング角制御。
• スペースキーを押すことで表示倍率拡大。また、シフトキーを押しながらスペースキーを押すことで表示倍率縮小。
• マウスのドラッギングによる形状の平行移動。

12.3 fk ShapeViewer のメンバ関数
fk ShapeViewer クラスの具体的な利用法は、第 13.5 節に記述するが、ここでは fk ShapeViewer クラスのメンバ関数一覧を掲載する。
bool draw(void)
実際に描画を行う。

void setWindowSize(int w, int h)
描画領域の大きさを (w, h) にする。形状やその他の状態は保持される。

void setShape(int ID, fk Shape *shape)
形状を描画対象として設定する。ID には何か任意の整数値を入れる。複数の形状を同時に描画したい場合、ID を変えることで実現できる。逆に、前に設定した形状と描画対象を入れ替えたい場合は、前に設定した際の ID をそのまま入力することで実現できる。

void setDrawMode(fk DrawMode mode)
形状の描画モードを選択する。mode に入力できる選択肢は、8.3 節で述べているものと同一のものが選択できる。

void setBGColor(fk Color color)
void setBGColor(ﬂoat r, ﬂoat g, ﬂoat b)
形状描画領域の背景色を設定する。

void setHead(double r)
カメラのヘッド角を r ラジアンに設定する。

void setPitch(double r)
カメラのピッチ角を r ラジアンに設定する。

108

void setBank(double r)
カメラのバンク角を r ラジアンに設定する。

void setScale(double s)
表示倍率を s に設定する。

void setAxisMode(bool mode)
座標軸について、mode が true なら描画有り、false なら無しに設定する。

void setAxisScale(double l)
座標軸の長さを l に設定する。

void setCenter(fk Vector pos)void setCenter(double x, double y, double z)
カメラの注視点を pos または (x, y, z) に設定する。

void setMaterial(int ID, fk Material mat)ID が表す形状のマテリアルを mat が表すマテリアルに変更する。

void setEdgeColor(int ID, fk Color col)ID が表す形状の稜線色を、col が表す色に変更する。

void setVertexColor(int ID, fk Color col)ID が表す形状の頂点色を、col が表す色に変更する。

void setPosition(int ID, fk Vector pos)void setPosition(int ID, double x, double y, double z)
ID が表す形状の位置を変更する。

void setAngle(int ID, fk Angle angle)
void setAngle(int ID, double h, double p, double b)
ID が表す形状の姿勢を変更する。h にはヘディング角を、p にはピッチ角を、b にはバンク角を入力する。

void setVec(int ID, fk Vector vec)
void setVec(int ID, double x, double y, double z)
ID が表す形状の方向ベクトルを変更する。

void setUpvec(int ID, fk Vector vec)
void setUpvec(int ID, double x, double y, double z)
ID が表す形状のアップベクトルを変更する。

setBlendStatus(bool mode)
透過処理について、mode が true であれば有効、false であれば無効に設定する。
109

void clearModel(void)
現在の描画登録を全てクリアする。

fk Shape * getShape(int ID)
ID に設定されている形状データ (のアドレス) を返す。

fk DrawMode getDrawMode(void)
現在の描画モードを返す。

double getHead(void)
現在設定されているカメラのヘッド角をラジアンで返す。

double getPitch(void)
現在設定されているカメラのピッチ角をラジアンで返す。

double getBank(void)
現在設定されているカメラのバンク角をラジアンで返す。

double getScale(void)
現在の表示倍率を返す。

bool getAxisMode(void)
現在の座標軸描画の有無を返す。

double getAxisScale(void)
現在の座標軸の長さを返す。

fk Vector getCenter(void)
現在のカメラの注視点を返す。

fk Color getBGColor(void)
背景色を返す。

bool getBlendStatus(void)
現在の透過処理状態を返す。

bool snapImage(const string ﬁleName, fk ImageType type, fk SnapProcMode mode)
現在ウィンドウに表示されている画像を、指定した画像形式で ﬁleName にあるファイル名で保存する。type は以下の3 種類のうちいずれかを指定する。
• fk ImageType::BMP (Windows Bitmap 形式)• fk ImageType::PNG (PNG 形式)• fk ImageType::JPG (JPEG 形式)
また、mode はどの画像バッファから情報を取得するかを指定する物で、以下の 3 種類のいずれかを指定する。
110• fk SnapProcMode::FRONT (OpenGL フロントバッファ)• fk SnapProcMode::BACK (OpenGL バックバッファ)• fk SnapProcMode::WIN32 GDI (Windows のみ)bool snapImage(fk Image *image, fk SnapProcMode mode)
現在ウィンドウに表示されている画像を、fk Image 型の変数に格納する。mode に関しては上記のファイル出力の場合と同様である。また、mode はデフォルト引数が設定されており、省略可能である。省略した場合 mode は
FK SNAP GL FRONT となる。
出力に成功すれば true を、失敗すれば false を返す。

void setPutStrMode(fk PutStrMode mode)
fk PutStrMode getPutStrMode(void)bool setPutFile(string ﬁleName)
void putString(string message)
void printf(char *format, ...)
void clearBrowser(void)
これらの関数は、fk Window クラスと同様にメッセージ出力機能を利用するためのものである。詳細は 11.6.8 節を参
照のこと。

111

第 13 章サンプルプログラム

13.1 基本的形状の生成と親子関係
次に掲載するプログラムは、原点付近に 1 個の直方体と 2 本の線分を作成し表示するプログラムである。ただ表示するだ
けでは面白くないので、線分を直方体の子モデルにし、直方体を回転させると線分も一緒に回転することを試してみる。また、視点も最初は遠方に置いて段々近づけていき、ある程度まで接近したらひねりを加えてみる。
• 1 行目の include 文は、FK システムを使用する場合に必ず記述する。
• 3 行目の using 文を書いておくと、名前空間「FK」を省略することができる。using を用いない場合は、FK のクラス
名の前には「FK::」を追加する必要がある。
• 7 行目において、メインウィンドウを生成している。
• 8 行目において、視点 (camera)、直方体 (blockModel)、線分 2 本 (lineModel[2]) 用の変数を定義している。
• 10 行目は線分形状、11 行目は直方体形状を作成するための変数を作成している。
• 14 行目の記述は、最初から準備されている「標準マテリアル」を用いるための準備である。標準マテリアルを使用したい場合は、プログラムの最初にこの fk Material::initDefault() 関数を呼ぶ。
• 17 行目にて、ウィンドウのサイズをピクセル単位で指定している。
• 20 行目で、blockModel に block の形状情報をリンクしている。
• 21 行目で、blockModel へのマテリアルとして Material::Yellow を設定している。
• 24 〜 31 行目で、線分形状を生成し lineModel[] にリンクしている。28 行目では &pos[0] を代入することによって
pos[0] と pos[1] が、29 行目では &pos[2] を代入することによって pos[2] と pos[3] がそれぞれ線分の端点となる。
• 34,35 行目で、線分に対して色設定を行っている。どのような形状であっても、線に対して色を設定する場合はsetLineColor() を用いる。
• 38,39 行目で、線分を直方体の子モデルに設定している。これにより、直方体を移動すると線分も追従して移動していくようになる。
• 42 〜 44 行目で、視点の位置を設定している。この場合は、位置を (0, 0, 2000)、視線方向を原点に向け、カメラの上方
向 (アップベクトル) が (0, 1, 0) になるように設定している。
• 47 行目でカメラの登録を、48 〜 50 行目で、各モデルをウィンドウへ登録している。
• 52 行目でウィンドウを開いている。この処理を行わないと、fk AppWindow 型の変数を用意しても表示はされないので注意が必要である。
• 56 行目の for 文中にある window.update() が実際に表示を行う処理である。ウィンドウが正常に描画された場合はtrue が返るため、ループが継続する。ウィンドウが閉じられていた場合、false が返ってくるので for 文を抜けることになる。
• 60 行目で、視点位置を −z 方向に speed の数値分移動させている。
• 63 行目で、直方体 (と子モデルである線分) を Y 軸中心に回転させている。回転角度は、speed に π/300 = 0.6◦ を掛け合わせた角度である。
• 66 〜 68 行目で、カメラの z 座標が 1000 未満になる場合に、視点にひねりを加えている。
• 71 〜 73 行目で、もし視点が原点を越えてしまった場合に注視点を原点に向かせるようにしている。
ソースコード 13.1 親子関係サンプル

112

1

# include < FK / FK .h >2
3using namespace FK ;4
5

int main ( int , char *[])

6

{

7

fk_AppWindow

window ;8

fk_Modelcamera , blockModel , lineModel [2];9fk_Vector

pos [4];10

fk_Lineline [2];11

fk_Blockblock (50.0 , 70.0 , 40.0);

1213// マ テ リ ア ル の 初 期 化

14

fk_Material :: initDefault ();15
16// ウ ィ ン ド ウ 設 定

17window . setSize (800 , 800);1819// 直 方 体 の 設 定

20blockModel . setShape (& block );21

blockModel . setMaterial ( Material :: Yellow );22
23// 線 分 の 設 定

24pos [0]. set (0.0 , 100.0 , 0.0);25

pos [1]. set (100.0 , 0.0 , 0.0);26

pos [2] = - pos [0];27

pos [3] = - pos [1];28line [0]. setVertex (& pos [0]);29line [1]. setVertex (& pos [2]);30lineModel [0]. setShape (& line [0]);31lineModel [1]. setShape (& line [1]);3233// 線 分 の 色 設 定

34lineModel [0]. setLineColor (1.0 f , 0.0 f , 0.0 f );35

lineModel [1]. setLineColor (0.0 f , 1.0 f , 0.0 f );36
37// 直 方 体 を 線 分 の 親 モ デ ル に 設 定

38

lineModel [0]. setParent (& blockModel );39

lineModel [1]. setParent (& blockModel );40
41// 視 点 の 位 置 と 姿 勢 を 設 定

42camera . glMoveTo (0.0 , 0.0 , 2000.0);

43

camera . glFocus (0.0 , 0.0 , 0.0);

44

camera . glUpvec (0.0 , 1.0 , 0.0);4546// 各 モ デ ル を ウ ィ ン ド ウ に 登 録

47

window . s etCame raMode l (& camera );48

window . entry (& blockModel );49

window . entry (& lineModel [0]);50window . entry (& lineModel [1]);5152

window . open ();53
54

double speed = 5.0;

55
56
57

for ( int i = 0; window . update () == true ; i ++) {double z = camera . getPosition (). z ;

113

58
59

// 視 点 を 原 点 に 近 づ け る

60camera . glTranslate (0.0 , 0.0 , - speed );61
62// 直 方 体 ( と 子 モ デ ル の 線 分 ) を Y 軸 中 心 に 回 転63

blockModel . g l Ro ta t eW it h Ve c (0.0 , 0.0 , 0.0 , fk_Axis :: Y , speed * fk_Math :: PI /300.0);64
65// カ メ ラ が 1000 よ り 近 く な っ た ら Z 軸 回 転

66

if ( z < 1000.0) {67

camera . lo R ot a te Wi t hV ec (0.0 , 0.0 , 0.0 , fk_Axis :: Z , speed * fk_Math :: PI /500.0);

68

}

69
70

// 視 点 が 原 点 を 越 え た ら 、 向 き を も う 一 度 原 点 に 向 か せ る 。

71

if ( z < - fk_Math :: EPS ) {72

camera . glFocus (0.0 , 0.0 , 0.0);

73

}

74
75

}

76
77
78

return 0;}

13.2 LOD 処理とカメラ切り替え
次のサンプルは、ボールが弾む様子を描いたプログラムである。このプログラムを実行すると、ボールが 2 回弾む間は鳥
瞰的なカメラ視点だが、その後に青色のブロックからボールを見る視点に切り替わる。プログラムのおおまかな流れは自分で解析して頂きたいが、ここでは最初のサンプルにはなかった概念に関して説明する。
まず、17 行目にて Ball クラスを定義し、この中で fk Model や fk Sphere のオブジェクトを持つようにしている。このようなプログラミングスタイルは、ややオブジェクト指向を意識したものと言えよう。
25 〜 27 行目にて球を分割数を変えて 3 種類定義しているが、これは 78 行目からの lod() メンバ関数内で Ball の実際の形状を動的に選択できるようにするためである。78 行目から実現している LOD 処理とは、視点とオブジェクトの距離によって形状の精密さを動的に変化させるテクニックである。たとえば、多くのポリゴンでできている形状は精密で迫力があるが、視点からとても遠くて非常に小さく表示されているような場合は無駄に処理されていることになる。そこで、遠くにあって小さく表示されている場合は粗い形状を表示して処理の高速化を計るのが LOD 処理の目的である。このサンプルのLOD 処理はわかりやすくするために露骨に変化が見られるが、実際にプログラムを作成するときはわかりにくくなるように視点距離との関係を調整する。
ソースコード 13.2
1

LOD 処理サンプル# include < FK / FK .h >2
3

const double

DOWN_ACCEL= 1.0500;// 降 下 時 の 加 速 度

4const double

RISE_ACCEL= 1.0530;// 上 昇 時 の 減 速 度

5const int

DOWN_MODE= 0;// 降 下 モ ー ド

6const int

RISE_MODE= 1;// 上 昇 モ ー ド

7const int

LOW_MODE= 0;// ブ ロ ッ ク 視 点 モ ー ド

8const int

HIGH_MODE= 1;// 鳥 瞰 モ ー ド

9const int

LOD4_HIGH= 200;// 4 分 割 距 離 ( 鳥 瞰 )10const int

LOD3_HIGH= 300;// 3 分 割 距 離 ( 鳥 瞰 )11

const int

LOD4_LOW= 90;// 4 分 割 距 離 ( ブ ロ ッ ク )12const int

LOD3_LOW= 120;// 3 分 割 距 離 ( ブ ロ ッ ク )13const double

TOP_BALL_POS= 300.0;// ボ ー ル 始 点 高 さ

14const double

BTM_BALL_POS= 18.0;// ボ ー ル 跳 ね 返 り 高 さ
114

15

const double

BALL_SIZE= 12.0;// ボ ー ル 半 径

1617class Ball {18
19

private :20

int

direction ;// ボ ー ル の 状 態 ( DOWN_MODE or RISE_MODE )

21

int

view_mode ;// 視 点 モ ー ド

22

int

bound_count ;// バ ウ ン ド 回 数 を 数 え る 変 数

23doubley_trs ;// ボ ー ル の y 座 標 移 動 量

24

fk_Modelball_model ;// ボ ー ル の モ デ ル

25

fk_Sphere

BALL2 ;// 2 分 割 形 状

26

fk_Sphere

BALL3 ;// 3 分 割 形 状

27

fk_Sphere

BALL4 ;// 4 分 割 形 状

28
29

public :30
31

Ball ( void );

32

void

init ( void );33

fk_Model *getModel ( void );34

fk_Vector

getPosition ( void );

35

void

lod ( fk_Vector );

36

void

accel ( void );

37

void

bound ( void );

38

int

draw ( fk_Vector );

39

};

40
41

// コ ン ス ト ラ ク タ

42Ball :: Ball ( void )

43

{

44
45

init ();}

46
47// 初 期 化48

void Ball :: init ( void )

49

{

50

direction

51

y_trs

= 0.1;

52

view_mode= HIGH_MODE ;53

bound_count

= 1;

54

BALL2 . setRadius ( BALL_SIZE );55

BALL2 . setDivide (2);

56

BALL3 . setRadius ( BALL_SIZE );57

BALL3 . setDivide (3);58

BALL4 . setRadius ( BALL_SIZE );59

BALL4 . setDivide (4);= DOWN_MODE ;60
61

ball_model . glMoveTo (0.0 , TOP_BALL_POS , 0.0);6263

ball_model . setShape (& BALL2 );
}

64
65

// fk_Model を 返 す 関 数

66

fk_Model * Ball :: getModel ( void )

67

{

68
69

return & ball_model ;
}

70
71

// ボ ー ル の 現 在 位 置 を 返 す 関 数

115

72

fk_Vector Ball :: getPosition ( void )

73

{

74
75

return ball_model . getPosition ();
}

76
77

// 視 点 か ら の 距 離 に よ っ て ボ ー ル の 分 割 数 を 変 え る 関 数 ( Level Of Detail )78

void Ball :: lod ( fk_Vector pos ){79

doubledistance ;80
81

distance = ( ball_model . getPosition () - pos ). dist ();82
83

switch ( view_mode ) {84case HIGH_MODE :85
86

if ( distance < LOD4_HIGH ) {87

ball_model . setShape (& BALL4 );88} else if ( distance < LOD3_HIGH ) {89

ball_model . setShape (& BALL3 );90} else {91

ball_model . setShape (& BALL2 );

92

}

93

break ;

94
95

case LOW_MODE :96
97

if ( distance < LOD4_LOW ) {98

ball_model . setShape (& BALL4 );99} else if ( distance < LOD3_LOW ) {100

ball_model . setShape (& BALL3 );101} else {102

ball_model . setShape (& BALL2 );

103

}

104

break ;

105
106

default :107

fl_alert ( " Err !! ␣ View ␣ Mode ␣ is ␣ wrong . " );108

break ;

109

}

110
111
112

return ;
}

113
114

// ボ ー ル を 加 速 さ せ る 関 数

115void Ball :: accel ( void )

116

{

117

switch ( direction ) {118case DOWN_MODE :119

y_trs *= DOWN_ACCEL ;120

ball_model . glTranslate (0.0 , - y_trs , 0.0);

121

break ;

122
123
124

case RISE_MODE :y_trs /= RISE_ACCEL ;125

ball_model . glTranslate (0.0 , y_trs ,0.0);

126

break ;

127
128

default :

116

129

fl_alert ( " Err !! ␣ Direction ␣ Mode ␣ is ␣ wrong . " );130

break ;

131
132

}
}

133
134

// ボ ー ル の 跳 ね 返 り 判 定 を す る 関 数

135void Ball :: bound ( void )

136

{

137

if ( ball_model . getPosition (). y < BTM_BALL_POS ) {138

direction = RISE_MODE ;139} else if ( y_trs < 0.01) {140

if ( direction == RISE_MODE ) {141

if ( bound_count % 4 < 2) {142

view_mode = HIGH_MODE ;143} else {144

view_mode = LOW_MODE ;

145

}

146

bound_count ++;

147

}

148

direction = DOWN_MODE ;

149

}

150
151
152

return ;
}

153
154

// ボ ー ル の 運 動 に 関 す る 関 数 . 返 り 値 は 視 点 モ ー ド

155

int Ball :: draw ( fk_Vector pos )

156

{

157

lod ( pos );158bound ();

159

accel ();

160//４回跳ね返ると初期化

161

if ( bound_count > 4) init ();162

return view_mode ;

163

}

164
165

int main ( int argc , char * argv [])

166

{

167

fk_Scene168

int

view_mode = HIGH_MODE ;

169

Ball

ball ;

170

fk_ModelviewModel , lightModel , groundModel , blockModel ;171

fk_Light

light ;

172

fk_Circle

ground (4 , 100.0);

173

fk_Blockblock (10.0 , 10.0 , 10.0);

scene ;

174
175
176

// ### WINDOW ###177

Fl_Window

mainWin (620 , 620 , " BALL ␣ TEST " );178

fk_Window

win (10 , 10 , 600 , 600);

179

mainWin . end ();180

fk_Material :: initDefault ();

181
182

// ### VIEW POINT ###183// 上 の 方 か ら 見 た 視 点

184

viewModel . glMoveTo (0.0 , 400.0 , 80.0);

185

viewModel . glFocus (0.0 , 30.0 , 0.0);

117

186

viewModel . glUpvec (0.0 , 1.0 , 0.0);

187
188

// ### LIGHT ###189

light . setLightType ( FK_ POINT_ LIGHT );190

light . setAt tenuat ion (0.0 , 0.0);

191

lightModel . setShape (& light );192

lightModel . setMaterial ( White );193

lightModel . glTranslate ( -60.0 , 60.0 , 0.0);

194
195

// ### GROUND ###196

groundModel . setShape (& ground );197

groundModel . setMaterial ( LightGreen );198

groundModel . setSmoothMode ( true );199

groundModel . lo R ot at e Wi t hV ec (0.0 , 0.0 , 0.0 , fk_X , - FK_PI /2.0);

200
201

// ### VIEW BLOCK ###202

blockModel . setShape (& block );203

blockModel . setMaterial ( Blue );204

blockModel . glMoveTo (60.0 , 30.0 , 0.0);

205

blockModel . setParent (& groundModel );

206
207

// ### BALL ###208

ball . getModel () - > setMaterial ( Red );209

ball . getModel () - > setSmoothMode ( true );

210
211

// ### Model Entry ###212

scene . entryCamera (& viewModel );213

scene . entryModel (& lightModel );214

scene . entryModel ( ball . getModel ());

215

scene . entryModel (& groundModel );216

scene . entryModel (& blockModel );217

win . setScene (& scene );

218
219

mainWin . show ();220

win . show ();

221
222

// ### MAIN LOOP ###223

while ( true ) {

224
225

if ( mainWin . visible () == 0) {226

if ( Fl :: wait () == 0) {227

break ;

228

} else {229

continue ;

230

}

231

}

232

if ( win . drawWindow () == 0) break ;233

if ( Fl :: check () == 0) break ;234

if ( win . winOpenStatus () == false ) continue ;

235
236

// ボ ー ル を 弾 ま せ て , カ メ ラ の 状 態 を 取 得 。

237

view_mode = ball . draw ( viewModel . getPosition ());

238
239

if ( view_mode == HIGH_MODE ) {240// カ メ ラ を 上 か ら の 視 点 に す る 。

241

viewModel . glMoveTo (0.0 , 400.0 , 80.0);

242

viewModel . glFocus (0.0 , 30.0 , 0.0);

118

243

viewModel . glUpvec (0.0 , 1.0 , 0.0);244

scene . entryModel (& blockModel );245} else {246// カ メ ラ を ブ ロ ッ ク か ら の 視 点 に す る 。

247

viewModel . glMoveTo ( blockModel . getInhPosition ());

248

viewModel . glTranslate (0.0 , 10.0 , 0.0);

249

viewModel . glFocus ( ball . getPosition ());

250

viewModel . glUpvec (0.0 , 1.0 , 0.0);

251

scene . removeModel (& blockModel );

252

}

253
// 地 面 を く る く る 回 転 さ せ ま し ょ う 。

254
255

groundModel . gl R ot at e Wi t hV ec (0.0 , 0.0 , 0.0 , fk_Y , 0.02);

256

}

257
258
259

return 0;}

13.3 FLTK を用いた GUI 構築
本節で説明するサンプルは、GUI (Graphical User Interface) を用いた立体ビューワーである。ウィンドウの章でも少し触れたが、FK システムは内部で FLTK と呼ばれる GUI ツールキットを用いている。ここでは、その FLTK を用いてGUI を作成し、FK システムと併用するサンプルを紹介する。なお、本書では FLTK の詳細な解説は掲載しないが、FLTKの解説は http://www.fltk.org/ という URL に詳しく記載されている。
ボールのサンプルと同じく、プログラム行のそれぞれの意味はコメントで記述しているので、全体の流れを通して幾つか
わかりにくい部分を記述する。
• 最初に include するファイルが FK/FK だけでなく、FLTK 用の様々なファイルが多くある。FLTK を用いる場合は、このように適宜必要なファイルを include しなければならない。
• 10 行目から始まる GUISet クラスは、GUI の部品を管理するためのものである。このクラスのコンストラクタ (41 〜115 行目) 内で様々な部品を作成するようにしてあるので、GUISet オブジェクトの宣言 (264 行目) をするだけでコンストラクタが呼び出され、GUI が作成されるようになっている。
• 122 行目からある getFileName メンバ関数は、Fl File Chooser クラスを用いてファイル名を取得するための関数である。まず 128、129 行目でファイル取得用ダイアログを生成し、131 行目の while 文でダイアログが終了されるまで処理を行う。もしダイアログ中でキャンセルされていた場合は、135 行目の fCharP に nullptr が入るため、136 行目のif 文に該当する。そうでない場合は、143 行目まで進んで文字列 (ファイル名) を返す。
• 161 行目では数学関数の pow を用いている。pow(a, b) は a の b 乗を返す関数なので、scaleRoller の値が実際には 2であれば 100(= 102 ) を、3 であれば 1000(= 103 ) を、-1 であれば 0.1(= 10−1 ) を返す。
• 314、315 行目では照明を Y 軸中心に少しだけ回転させている。形状を回転させずに立体感を出す方法として、このよ
うに照明を回転させることも有効な手段である。
• 318、319 行目で、GUI のスライダーやローラーで設定された値を実際に反映させている。
• 325 行目にて、ﬂ color chooser によってカラー設定ウィンドウを呼び出している。この関数の 2 〜 4 番目の引数は実は参照呼び出しになっている。従って、colorR, colorG, colorB の 3 変数は色設定の後に値がちゃんと反映されている。
ソースコード 13.3
1

# include < FK / FK .h >2# include < FL / Fl_V al u e_ Sl i de r .h >3# include < FL / Fl_Roller .h >119

GUI サンプル

4# include < FL / Fl_Group .h >5# include < FL / Fl_C he c k_ Bu t to n .h >6# include < FL / Fl_ C o l o r _ C h o o s e r .h >7# include < FL / Fl_F il e _C ho o se r .h >8# include < FL / fl_ask .h >910class GUISet {11

private :12
13

Fl_Va lu e_S li de r * headSlider ;// ヘ デ ィ ン グ 角 用 ス ラ イ ダ ー

14

Fl_Va lu e_S li de r * pitchSlider ;// ピ ッ チ 各 用 ス ラ イ ダ ー

15

Fl_Roller* scaleRoller ;// ス ケ ー リ ン グ 用 ロ ー ラ ー

17

Fl_Group* materialGroup ;// ラ ジ オ ボ タ ン の グ ル ー プ

18

Fl_Ch ec k_B ut to n * ambientButton ;// Ambient 用 ボ タ ン

19

Fl_Ch ec k_B ut to n * diffuseButton ;// Diffuse 用 ボ タ ン

20

Fl_Ch ec k_B ut to n * specu larBut ton ;// Specular 用 ボ タ ン

21

Fl_Ch ec k_B ut to n * emiss ionBut ton ;// Emission 用 ボ タ ン

22

Fl_Button* colorChooser ;// カ ラ ー 設 定 呼 び 出 し ボ タ ン

24

Fl_Button* fileO penBu tton ;// フ ァ イ ル 選 択 呼 び 出 し ボ タ ン

25

Fl_Button* exitButton ;// 終 了 ボ タ ン

16

23

26
27

public :28

GUISet ( void );// コ ン ス ト ラ ク タ

29

˜ GUISet ();// デ ス ト ラ ク タ

30
31

stringgetFileName ( void );// フ ァ イ ル 名 取 得

32doublegetHead ( void );// ヘ デ ィ ン グ 角 取 得

33doublegetPitch ( void );// ピ ッ チ 角 取 得

34doublegetScale ( void );// ス ケ ー ル 取 得

35

int

materi alSele ct ( void );// マ テ リ ア ル 種 類 取 得

36

bool

t o g g l e C o l o r C h o o s e r ( void ); // カ ラ ー 設 定 呼 出 判 定

37

bool

toggle FileOp en ( void );// フ ァ イ ル 取 得 呼 出 判 定

booltoggleExit ( void );// 終 了 判 定

38
39

};

40
41

GUISet :: GUISet ( void )

42

{

43

// ヘ デ ィ ン グ 角 用 ス ラ イ ダ ー 各 種 設 定

44headSlider = new Fl _V a lu e_ S li de r (130 , 330 , 180 , 20 , " Head ␣ Angle " );45

headSlider - > type ( F L _ H O R _ N I C E _ S L I D E R );// ナ イ ス な ス ラ イ ダ ー

46

headSlider - > minimum ( - FK_PI );// 最 小 値 は -3.14

47

headSlider - > maximum ( FK_PI );// 最 高 値 は 3.14

48

headSlider - > value (0.0);// 初 期 値 は 049

headSlider - > labelsize (12);// ラ ベ ル 文 字 の サ イ ズ を 12 pt に50

headSlider - > textsize (12);// カ ウ ン タ の サ イ ズ も 12 に

51
52// ピ ッ チ 角 用 ス ラ イ ダ ー 各 種 設 定

53pitchSlider = new Fl _V a lu e_ S li de r (130 , 370 , 180 , 20 , " Pitch ␣ Angle " );54

pitchSlider - > type ( F L _ H O R _ N I C E _ S L I D E R );55

pitchSlider - > minimum ( - FK_PI );56

pitchSlider - > maximum ( FK_PI );57

pitchSlider - > value (0.0);58

pitchSlider - > labelsize (12);59pitchSlider - > textsize (12);

60

120

// ス ケ ー リ ン グ 用 ロ ー ラ ー 各 種 設 定

61
62

scaleRoller = new Fl_Roller (160 , 410 , 120 , 20 , " Scale " );63

scaleRoller - > type ( FL_HORIZONTAL );64

scaleRoller - > minimum ( -100.0);

65

scaleRoller - > maximum (100.0);

66

scaleRoller - > value (0.0);

67

scaleRoller - > labelsize (12);// ロ ー ラ ー は 横 向 き

6869// 左 側 の マ テ リ ア ル 関 係 の ボ タ ン を グ ル ー プ 化 し て お く 。

70materialGroup = new Fl_Group (10 , 320 , 100 , 170);71

materialGroup - > box ( F L _ T H I N _ U P _ F R A M E );// ち ょ っ と 盛 り 上 げ る

72// Ambient 用 ラ ジ オ ボ タ ン

73
74

ambientButton = new Fl _C h ec k_ B ut to n (10 , 320 , 100 , 30 , " Ambient " );75ambientButton - > type ( F L_ R AD IO _ BU T TO N );76

ambientButton - > down_box ( F L _ D I A M O N D _ D O W N _ B OX );77// Diffuse 用 ラ ジ オ ボ タ ン

78
79

diffuseButton = new Fl _C h ec k_ B ut to n (10 , 350 , 100 , 30 , " Diffuse " );80

diffuseButton - > type ( F L_ R AD IO _ BU T TO N );81

diffuseButton - > down_box ( F L _ D I A M O N D _ D O W N _ B OX );82// Specular 用 ラ ジ オ ボ タ ン

83
84

specul arButt on = new Fl _C h ec k_ B ut to n (10 , 380 , 100 , 30 , " Specular " );85

specularButton - > type ( FL _R A DI O_ B UT T ON );86

specularButton - > down_box ( F L _ D I A M O N D _ D O W N _ B O X );87// Emission 用 ラ ジ オ ボ タ ン

8889emissi onButt on = new Fl _C h ec k_ B ut to n (10 , 410 , 100 , 30 , " Emission " );90emissionButton - > type ( FL _R A DI O_ B UT T ON );91emissionButton - > down_box ( F L _ D I A M O N D _ D O W N _ B O X );92
93// カ ラ ー 設 定 呼 出 ボ タ ン

94

colorChooser = new Fl_Button (20 , 450 , 80 , 30 , " ColorChange " );95

colorChooser - > type (0);96colorChooser - > labelsize (12);// 普 通 の ボ タ ン と し て 扱 う

9798// MaterialGroup の グ ル ー プ の 終 了

99materialGroup - > end ();

100
101

// と り あ え ず Ambient に チ ェ ッ ク を 入 れ る 。

102

ambientButton - > value (1);

103
104

// フ ァ イ ル 取 得 呼 出 ボ タ ン

105

fileOp enButt on = new Fl_Button (120 , 460 , 90 , 30 , " File ␣ Open " );106

fileOpenButton - > type (0);107

fileOpenButton - > labelsize (12);108// 終 了 ボ タ ン

109
110

exitButton = new Fl_Button (220 , 460 , 90 , 30 , " Exit " );111

exitButton - > type (0);112exitButton - > labelsize (12);

113
114
115

return ;
}

116
117

GUISet ::˜ GUISet ()

121

118

{

119
120

return ;
}

121
122

string GUISet :: getFileName ( void )

123

{

124

Fl_Fi le _C hoo se r

* fc ;

125

const char

* fCharP ;126

stringfileName ;

127
128

fc = new Fl _F i le _C h oo se r ( " . " , " *. wrl " , Fl_File_Chooser :: SINGLE ,129" VRML ␣ File ␣ Select " );130

fc - > show ();131

while ( fc - > visible ()) {132

Fl :: wait ();

133

}

134
135

fCharP = fc - > value (1);136

if ( fCharP == nullptr ) {137

delete fc ;138

return fileName ;

139

}

140
141

fileName = fCharP ;142

delete fc ;

143
144

return fileName ;
}

145
146
147

double GUISet :: getHead ( void )

148

{

149
150

return headSlider - > value ();
}

151
152

double GUISet :: getPitch ( void )

153

{

154
155

return pitchSlider - > value ();
}

156
157

double GUISet :: getScale ( void )158{// 10 の ( scaleRoller - > value ()) 乗 を 返 す 。

159
160
161
162

return pow (10.0 , scaleRoller - > value ());
}

163
164

int GUISet :: mater ialSel ect ( void )165{// ど の ラ ジ オ ボ タ ン が 選 択 さ れ て い る か を 取 得

166
167
168

if ( ambientButton - > value () == 1) return 1;

169

if ( diffuseButton - > value () == 1) return 2;

170

if ( specularButton - > value () == 1) return 3;

171

if ( emissionButton - > value () == 1) return 4;

172
173
174

return -1;
}

122

175
176

bool GUISet :: t o g g l e C o l o r C h o o s e r ( void )

177

{

178

if ( colorChooser - > value () == 1) return true ;

179
180

return false ;
}

181
182

bool GUISet :: toggl eFileO pen ( void )

183

{

184

if ( fileOpenButton - > value () == 1) {185

fileOpenButton - > value (0);186

return true ;

187

}

188
189

return false ;
}

190
191

bool GUISet :: toggleExit ( void )

192

{

193

if ( exitButton - > value () == 1) return true ;

194
195

return false ;
}

196
197

void getMaterial ( int index , fk_Material * mat , double *r , double *g , double * b )198{// Index に よ っ て マ テ リ ア ル の 各 種 属 性 を (R , G , B ) に 代 入

199
200
201

switch ( index ) {202case 1:203* r = mat - > getAmbient () - > getR ();204* g = mat - > getAmbient () - > getG ();205* b = mat - > getAmbient () - > getB ();206

break ;

207

case 2:208* r = mat - > getDiffuse () - > getR ();209* g = mat - > getDiffuse () - > getG ();210* b = mat - > getDiffuse () - > getB ();211

break ;

212

case 3:213* r = mat - > getSpecular () - > getR ();214* g = mat - > getSpecular () - > getG ();215* b = mat - > getSpecular () - > getB ();216

break ;

217

case 4:218* r = mat - > getEmission () - > getR ();219* g = mat - > getEmission () - > getG ();220* b = mat - > getEmission () - > getB ();221

break ;

222

default :223

break ;

224

}

225

return ;

226

}

227
228

void setMaterial ( int index , fk_Material * mat , double r , double g , double b )

229

{

230

// Index に よ っ て (R , G , B ) を 各 種 マ テ リ ア ル に 設 定

231

123

232

switch ( index ) {233

case 1:234

mat - > setAmbient (r , g , b );235

break ;

236

case 2:237

mat - > setDiffuse (r , g , b );238

break ;

239

case 3:240

mat - > setSpecular (r , g , b );241

break ;

242

case 4:243

mat - > setEmission (r , g , b );244

break ;

245

default :246

break ;

247

}

248
249
250

return ;
}

251
252

int main ( int argc , char * argv [])

253

{

254

doublecolorR , colorG , colorB ;255

fk_Materialmaterial ;256

fk_In de xF ace Se t shape ;257

fk_Light

light ;

258

fk_ModelshapeModel , lightModel [2] , camera ;259

fk_Scenescene ;

260

stringfileName ;

261
262

Fl_Window

mainWindow (320 , 500 , " VRML ␣ Viewer " );263

fk_Window

viewWin (10 , 10 , 300 , 300);264

GUISet

gui ;

265
266

mainWindow . end ();

267
268

fk_Material :: initDefault ();

269
270

// 光 源 の 設 定 。 今 回 は 2 つ の 平 行 光 源

271

lightModel [0]. setShape (& light );272

lightModel [1]. setShape (& light );273

lightModel [0]. setMaterial ( White );274

lightModel [1]. setMaterial ( White );275

lightModel [0]. glFocus ( -1.0 , -1.0 , 0.0);276

lightModel [1]. glFocus (1.0 , -1.0 , 0.0);

277
278

// ソ リ ッ ド モ デ ル の 初 期 マ テ リ ア ル に Yellow を 用 い る 。

279

material = Yellow ;280

shapeModel . setShape (& shape );281

shapeModel . setMaterial ( material );

282
283

// 視 点 の 設 定

284

camera . glTranslate (0.0 , 100.0 , 1000.0);

285

camera . glFocus (0.0 , 0.0 , 0.0);

286

camera . glUpvec (0.0 , 1.0 , 0.0);

287
288

// デ ィ ス プ レ イ リ ス ト へ ソ リ ッ ド 、 光 源 、 視 点 を 登 録

124

289

scene . entryModel (& shapeModel );290

scene . entryModel (& lightModel [0]);291scene . entryModel (& lightModel [1]);292scene . entryCamera (& camera );

293
294

// ViewWin に デ ィ ス プ レ イ リ ス ト を 登 録

295

viewWin . setScene (& scene );

296
297

mainWindow . show ();298

viewWin . show ();

299
300

while ( true ) {

301
302

if ( mainWindow . visible () == 0) {303

if ( Fl :: wait () == 0) {304

break ;

305

} else {306

continue ;

307

}

308

}

309

if ( viewWin . drawWindow () == 0) break ;310

if ( Fl :: check () == 0) break ;311

if ( viewWin . winOpenStatus () == false ) continue ;

312
313

// ラ イ ト を Y 軸 中 心 に 回 転

314

lightModel [0]. g lR o ta te W it hV e c (0.0 , 0.0 , 0.0 , fk_Y , FK_PI /100.0);

315

lightModel [1]. g lR o ta te W it hV e c (0.0 , 0.0 , 0.0 , fk_Y , FK_PI /100.0);

316
317

// ス ラ イ ダ ー や ロ ー ラ ー に 従 っ て ソ リ ッ ド モ デ ル の 姿 勢 と 大 き さ を 決 定

318

shapeModel . glAngle ( gui . getHead () , gui . getPitch () , 0.0);319

shapeModel . setScale ( gui . getScale ());

320
321

if ( gui . t o g g l e C o l o r C h o o s e r () == true ) {322// カ ラ ー 設 定 モ ー ド

323

getMaterial ( gui . m ateria lSelect () , & material ,324& colorR , & colorG , & colorB );325

fl_ c o l o r _ c h o o s e r ( " COLOR ␣ SET " , colorR , colorG , colorB );326

setMaterial ( gui . m ateria lSelect () , & material ,327

colorR , colorG , colorB );

328
329

shapeModel . setMaterial ( material );
}

330
331

if ( gui . togg leFile Open () == true ) {332// フ ァ イ ル 取 得 モ ー ド

333

fileName = gui . getFileName ();334

if ( fileName != " " ) {335// キ ャ ン セ ル を 押 さ れ た の で な い な ら

336

if ( shape . readVRMLFile ( fileName , true , false ) == false ) {// VRML フ ァ イ ル で は な か っ た ら

337
338

fl_alert ( " ␣ is ␣ not ␣ VRML2 .0 ␣ file . " , fileName . c_str ());

339

} else {340

shapeModel . s e tM at e ri alMode ( FK_PARENT_MODE );

341

}

342
343

}
}

344
345

if ( gui . toggleExit () == true ) {125

// 終 了 ボ タ ン が 押 さ れ た ら while ル ー プ を 抜 け る 。

346
347

break ;

348

}

349

}

350
351
352

return 0;}

13.4 マルチウィンドウ
次のサンプルは、マルチウィンドウを用いて複数の視点を同時に表示するプログラムである。このプログラムは、同一シーンで異なる 3 つのウィンドウが表示される。それぞれの視点は左から順に、車の運転者視点、建物から見た視点、車を斜め後ろから見下ろした鳥瞰視点を表している。
このプログラムでは、116 〜 126 行目で 3 つのウィンドウを同時に処理している以外は、特に目新しい部分はない。このように、特別な処理を施す必要なくマルチウィンドウプログラムを作成することが可能である。
ソースコード 13.4 マルチウィンドウサンプル
1# include < FK / FK .h >2
= 25.0;// 建 物 幅 の 基 本 単 位

SPEED= 2.0;// 車 の ス ピ ー ド

CIRCUITX= 150.0;// コ ー ス の X 方 向 幅

const double

CIRCUITY= 250.0;// コ ー ス の Y 方 向 幅

const double

EPS= 0.001;// 誤 差 判 定 用 数 値

3

const double

BUILDWIDTH

4

const double

5

const double

6
7
8
9

class Car {10

private :11

fk_ModelcarModel ;// 車 全 体 モ デ ル

12

fk_ModelbodyModel ;// 車 体 モ デ ル

13

fk_ModeltireModel [4];// 各 タ イ ヤ モ デ ル

14

fk_ModeldriverModel [2]; // 運 転 者 モ デ ル

15

fk_ModelbirdModel ;// 鳥 瞰 視 点 モ デ ル

17

fk_Block

body ;// 車 体 形 状

18

fk_Circle

tire ;// タ イ ヤ 形 状

19

fk_Sphere

driver ;// 運 転 者 形 状

16

20
21

public :22

Car ( void ) {23

init ();

24// コ ン ス ト ラ ク タ

}

25
26

void

init ( void );

27

void

entryScene ( fk_Scene * , bool );28

fk_Vector

getC arPosi tion ( void );29

fk_Model *getBirdModel ( void );

30

void

forward ( void );

31

void

rotate ( fk_Model * , fk_Vector , fk_Vector );

32

};

33
34

class World {35

private :36

fk_ModelbuildModel [6] , groundModel , lightModel [2];37

fk_BlockbuildShape , groundShape ;
126

38

fk_Light

lightShape ;

40

void

defLight ( int , double , double , double );

41

void

makeBuild ( int , double , double , double , fk_Material *);

39

42
43

public :44

World ( void ) {45

init ();

46

}

47
48

void

init ( void );

49

void

entryScene ( fk_Scene * , bool );

50

};

51
52

int main ( int argc , char * argv [])

53

{

54

Car

carObj ;

55

World

worldObj ;56

Fl_Window

mainWindow (940 , 320 , " FK ␣ TEST " );57

fk_Scene

carViewScene , buildViewScene , birdViewScene ;58

fk_Window

carViewWindow (10 , 10 , 300 , 300);

59

fk_Window

bu il d Vi ew W in do w (320 , 10 , 300 , 300);

60

fk_Window

birdVi ewWind ow (630 , 10 , 300 , 300);

61

fk_Color

bgColor (0.2 , 0.7 , 1.0);

fk_ModelbuildViewModel , birdViewModel ;

62
63
64
65

mainWindow . end ();66

fk_Material :: initDefault ();67
68

carObj . init ();69

worldObj . init ();

70
71
72

// 各 ウ ィ ン ド ウ に バ ッ ク グ ラ ウ ン ド カ ラ ー 設 定

73carViewScene . setBGColor ( bgColor );74

buildV iewSce ne . setBGColor ( bgColor );75

birdViewScene . setBGColor ( bgColor );

76
77
78

// 各 モ デ ル を デ ィ ス プ レ イ リ ス ト に 登 録

79

worldObj . entryScene (& carViewScene , false );80

worldObj . entryScene (& buildViewScene , true );81

worldObj . entryScene (& birdViewScene , false );82
83

carObj . entryScene (& carViewScene , false );84

carObj . entryScene (& buildViewScene , true );85

carObj . entryScene (& birdViewScene , true );86
87// 建 物 ウ ィ ン ド ウ の 視 点 設 定

88buildV iewMod el . glMoveTo ( -250.0 , 100.0 , 100.0);

89

buildV iewMod el . glFocus ( carObj . ge tCarPosition ());

90

buildV iewMod el . glUpvec (0.0 , 0.0 , 1.0);

91

buildV iewSce ne . entryCamera (& build ViewModel );92
93// 鳥 瞰 ウ ィ ン ド ウ の 視 点 設 定

94

birdViewScene . entryCamera ( carObj . getBirdModel ());

127

95
96

// ウ ィ ン ド ウ へ デ ィ ス プ レ イ リ ス ト を 登 録

97

carViewWindow . setScene (& carViewScene );98

build Vi ewW in do w . setScene (& build ViewSc ene );99

birdVi ewWind ow . setScene (& birdViewScene );

100
101

mainWindow . show ();102

carViewWindow . show ();103

build Vi ew Win do w . show ();104

birdVi ewWind ow . show ();

105
106

while ( true ) {

107
108

if ( mainWindow . visible () == 0) {109

if ( Fl :: wait () == 0) {110

break ;

111

} else {112

continue ;

113

}

114

}

115
116

if ( carViewWindow . drawWindow () == 0 ||117

bu il d Vi ew W in do w . drawWindow () == 0 ||118

birdVi ewWind ow . drawWindow () == 0) break ;

119
120

if ( Fl :: check () == 0) break ;

121
122

if ( carViewWindow . winOpenStatus () == false ||123

bu il d Vi ew W in do w . winOpenStatus () == false ||124

birdVi ewWind ow . winOpenStatus () == false ) {125

continue ;

126

}

127
128

carObj . forward ();129

buildViewMod el . glFocus ( carObj . ge tCarPosition ());

130

buildViewMod el . glUpvec (0.0 , 0.0 , 1.0);

131

}

132
133
134

return 0;
}

135
136

void Car :: init ( void )

137

{

138

int

i;

139
140

body . setSize (7.0 , 6.0 , 20.0);

141

tire . setRadius (2.0);

142

tire . setDivide (2);143

driver . setRadius (2.0);

144

driver . setDivide (2);

145
146

bodyModel . setShape (& body );147

bodyModel . glMoveTo (0.0 , 5.0 , 0.0);148

bodyModel . setMaterial ( Yellow );149

bodyModel . setParent (& carModel );

150
151

128

152

tireModel [0]. glMoveTo ( -4.0 , 1.0 , -8.0);

153

tireModel [0]. glVec (1.0 , 0.0 , 0.0);

154

tireModel [1]. glMoveTo (4.0 , 1.0 , -8.0);

155

tireModel [1]. glVec ( -1.0 , 0.0 , 0.0);

156

tireModel [2]. glMoveTo ( -4.0 , 1.0 , 8.0);

157

tireModel [2]. glVec (1.0 , 0.0 , 0.0);

158

tireModel [3]. glMoveTo (4.0 , 1.0 , 8.0);

159

tireModel [3]. glVec ( -1.0 , 0.0 , 0.0);

160
161

for ( i = 0; i < 4; i ++) {162

tireModel [ i ]. setShape (& tire );163

tireModel [ i ]. setMaterial ( Gray2 );164

tireModel [ i ]. setParent (& carModel );

165

}

166
167

driverModel [0]. setShape (& driver );168

driverModel [1]. setShape (& driver );169

driverModel [0]. glMoveTo ( -2.0 , 10.0 , 0.0);170

driverModel [1]. glMoveTo (2.0 , 10.0 , 0.0);

171

driverModel [0]. setMaterial ( Cream );172

driverModel [1]. setMaterial ( Cream );173

driverModel [0]. setSmoothMode ( true );174

driverModel [1]. setSmoothMode ( true );175

driverModel [0]. setParent (& carModel );176

driverModel [1]. setParent (& carModel );

177
178

birdModel . glMoveTo (0.0 , 100.0 , 200.0);

179

birdModel . glFocus (0.0 , 5.0 , 0.0);

180

birdModel . glUpvec (0.0 , 1.0 , 0.0);

181

birdModel . setParent (& carModel );

182
183

carModel . glMoveTo ( CIRCUITX , CIRCUITY , 0.0);184

carModel . glVec (0.0 , -1.0 , 0.0);

185

carModel . glUpvec (0.0 , 0.0 , 1.0);

186
187
188

return ;
}

189
190

void Car :: entryScene ( fk_Scene * scene , bool viewFlag )

191

{

192

int

i;

193
194

scene - > entryModel (& bodyModel );

195
196

for ( i = 0; i < 4; i ++) {197

scene - > entryModel (& tireModel [ i ]);

198

}

199
200

scene - > entryModel (& driverModel [0]);201

if ( viewFlag == true ) {202

scene - > entryModel (& driverModel [1]);203} else {204

scene - > entryCamera (& driverModel [1]);

205

}

206
207
208

return ;
}

129

209
210

fk_Vector Car :: get CarPo sition ( void )

211

{

212
213

return carModel . getPosition ();
}

214
215

fk_Model * Car :: getBirdModel ( void )

216

{

217
218

return & birdModel ;
}

219
220

void Car :: forward ( void )

221

{

222

fk_Vector

carPosition , carVelocity ;223

fk_Vector

Xplus (1.0 , 0.0 , 0.0) , Xminus ( -1.0 , 0.0 , 0.0);

224

fk_Vector

Yplus (0.0 , 1.0 , 0.0) , Yminus (0.0 , -1.0 , 0.0);

225

double

X = CIRCUITX ;226

doubleY = CIRCUITY ;

227
228

carPosition = carModel . getPosition ();229

carVelocity = carModel . getVec ();230

carModel . loTranslate (0.0 , 0.0 , - SPEED ); // 前 進

231
232

// サ ー キ ッ ト の 外 に で た 場 合 、 回 転 す る 。

233

if ( carPosition . x > X ) {234

rotate (& carModel , carVelocity , Xplus );

235

}

236

if ( carPosition . x < -X ) {237

rotate (& carModel , carVelocity , Xminus );

238

}

239

if ( carPosition . y > Y ) {240

rotate (& carModel , carVelocity , Yplus );

241

}

242

if ( carPosition . y < -Y ) {243

rotate (& carModel , carVelocity , Yminus );

244

}

245
246
247

return ;
}

248
249

void Car :: rotate ( fk_Model * model , fk_Vector velocity , fk_Vector orgVec )

250

{

251

// velocity と orgVec の 内 積 値 が 正 、 つ ま り 角 度 が 90 度 以 内 の 場 合 回 転252

if ( velocity * orgVec > EPS ) {253

model - > loAngle ( FK_PI /100.0 , 0.0 , 0.0);

254

}

255
256

return ;
}

257
258

void World :: defLight ( int lightID , double x , double y , double z )

259

{

260

lightModel [ lightID ]. setShape (& lightShape );261

lightModel [ lightID ]. setMaterial ( White );262

lightModel [ lightID ]. glTranslate (0.0 , 0.0 , 0.0);

263

lightModel [ lightID ]. glFocus (x , y , z );

264
265

return ;

130

266

}

267
268

void World :: makeBuild ( int buildID , double x , double y ,

269
270

double heightScale , fk_Material * buildMat ){271

buildModel [ buildID ]. setShape (& buildShape );272

buildModel [ buildID ]. setScale ( heightScale , fk_Z );273

buildModel [ buildID ]. glMoveTo (x , y , ( BUILDWIDTH * heightScale )/2.0);

274

buildModel [ buildID ]. setMaterial (* buildMat );

275
276
277

return ;
}

278
279

void World :: init ( void )

280

{

281

// 照 明 の 設 定

282

defLight (0 , 1.0 , 1.0 , -1.0);283

defLight (1 , -1.0 , -1.0 , -1.0);284// 建 物 の 設 定

285
286

buildShape . setSize ( BUILDWIDTH , BUILDWIDTH , BUILDWIDTH );287

makeBuild (0 , -250.0 , 100.0 , 5.0 , & Red );288

makeBuild (1 , -150.0 , 400.0 , 2.0 , & DimYellow );289

makeBuild (2 , 50.0 , 250.0 , 4.0 , & Blue );290

makeBuild (3 , 300.0 , 200.0 , 3.0 , & Gray1 );291

makeBuild (4 , 250.0 , -250.0 , 0.5 , & Green );292

makeBuild (5 , -50.0 , -350.0 , 6.0 , & Orange );

293
294

// 地 面 の 設 定

295

groundShape . setSize (1000 , 1000 , 2.0);

296

groundModel . setShape (& groundShape );297

groundModel . glTranslate (0.0 , 0.0 , -1.0);

298

groundModel . setMaterial ( Brown );

299
300
301

return ;
}

302
303

void World :: entryScene ( fk_Scene * scene , bool buildFlag )

304

{

305

int

i;

306
307

scene - > entryModel (& groundModel );

308
309

for ( i = 0; i < 2; i ++) {310

scene - > entryModel (& lightModel [ i ]);

311

}

312
313

for ( i = 0; i < 6; i ++) {314

scene - > entryModel (& buildModel [ i ]);

315

}

316
317

if ( buildFlag == true ) scene - > removeModel (& buildModel [0]);

318
319

return ;}131

13.5 形状の簡易表示とアニメーション
次のサンプルは、fk ShapeViewer クラスの典型的な利用法を示したものである。
• まず、13 〜 19 行目で 11 行 11 列の行列として並んでいる状態の座標を計算している。その際、z =x2 − y 2として z10成分は計算されている。
• 次に、22 〜 29 行目でインデックスフェースセットを表す配列を作成している。インデックスフェースセットに関しては、第 5.1 節を参照すること。
• 32 行目で実際に形状を生成する。この部分の解説も、第 5.1 節に記述がある。
• 35 行目では、作成した形状を描画形状として登録している。ID には 0 が選択されているが、特にどのような整数値でも構わない。
• 38,39 行目では、表裏の両面及び稜線や頂点を描画するように設定している。
• 42 行目では while ループ中で描画が行われるよう記述されている。これにより、43 〜 56 行目が実行される度に描画処理が行われるようになる。
• 46,47 行 目 で は、ア ニ メ ー シ ョ ン の 際 の 頂 点 移 動 量 が 計 算 さ れ て い る。移 動 は z 方 向 の み 行 わ れ、移 動 量 はである。counter はループの度に 56 行目で 10 ずつ追加されているので、描画の度に移動量が異なるこ
sin counter+10j
5πとになる。
• 50 行目で初期位置に移動量が足され、53 行目で実際に各頂点を移動している。
ソースコード 13.5
1

fk ShapeViewer サンプル

# include < FK / FK .h >2
3

int main ()

4

{

5

fk_ShapeView erviewer (300 , 360);

6

fk_In de xF ace Se t

shape ;

7

fk_Vector

pos [121] , moveVec , movePos ;8

int

IFSet [4*100];9int

i , j , counter ;10

double

x, y;

11
12

// 各 頂 点 位 置 の 設 定

13for ( i = 0; i <= 10; i ++) {14

for ( j = 0; j <= 10; j ++) {15

x = double ( i - 5);16

y = double ( j - 5);17

pos [ i *11 + j ]. set (x , y , ( x * x - y * y )/10.0);

18
19

}
}

20
21

// イ ン デ ッ ク ス フ ェ ー ス セ ッ ト の 生 成

22

for ( i = 0; i < 10; i ++) {23

for ( j = 0; j < 10; j ++) {24

IFSet [( i *10 + j )*4 + 0] = i *11 + j ;25

IFSet [( i *10 + j )*4 + 1] = ( i +1)*11 + j ;26

IFSet [( i *10 + j )*4 + 2] = ( i +1)*11 + j +1;

27

IFSet [( i *10 + j )*4 + 3] = i *11 + j +1;

28
29

}
}

30

132

31

// 形 状 の 生 成

32shape . makeIFSet (100 , 4 , IFSet , 121 , pos );33
34// fk_ShapeView er へ 形 状 を 設 定

35viewer . setShape (0 , & shape );3637// 各 種 設 定

38viewer . setDrawMode ( F K _ F R O N T B A C K _ P O L Y M O D E | FK_LINEMODE );39

viewer . setScale (10.0);4041counter = 0;42while ( viewer . draw () == true ) {43

for ( i = 0; i <= 10; i ++) {44

for ( j = 0; j <= 10; j ++) {45// 各 頂 点 の 移 動 量 計 算

46

moveVec . set (0.0 , 0.0 ,47

sin ( double ( counter + j *40)*0.05/ FK_PI ));48
49// 各 頂 点 を 移 動 場 所 を 計 算

50

movePos = moveVec + pos [ i *11 + j ];51// 各 頂 点 を 実 際 に 移 動

5253

shape . moveVPosition ( i *11 + j , movePos );

54

}

55

}

56

counter += 10;

57

}

58
59
60

return 0;}

13.6 パーティクルアニメーション
パーティクルアニメーションとは、粒子の移動によって気流や水流などを表現する手法である。FK システムでは、パーティクルアニメーションを作成するためのクラスとして fk Particle 及び fk ParticleSet クラスを用意している。これらの細かな仕様に関しては 4.13 節に記述してあるが、ここではサンプルプログラムを用いておおまかな利用法を説明する。
fk ParticleSet クラスは、これまで紹介したクラスとはやや利用手法が異なっている。まず、fk ParticleSet クラスを継承したクラスを作成し、いくつかの仮想関数に対して再定義を行う。あとは、getShape() 関数を利用して fk Model に形状として設定したり、fk ShapeViewer を利用して描画することができる。
ここでは、サンプルとして円柱の周囲を流れる水流のシミュレーションの様子を描画するプログラムを紹介する。
• 8 〜 15 行目は、fk ParticleSet クラスを継承した「MyParticle」というクラスを定義している。定義の際、仮想関数である genMethod()、allMethod()、indivMethod() の各関数の宣言を必ず行う。
• 19 〜 29 行目は MyParticle クラスのコンストラクタである。ここで、パーティクルの初期設定を行う。
• 21 行目の setMaxSize() 関数は fk ParticleSet クラスのメンバ関数でパーティクル個数の最大値を設定する。もしパーティクルの個数がこの値と等しくなったとき、newParticle() メソッドを呼んでもパーティクルは新たに生成されなくなる。
• 23,24 行目はそれぞれ個別処理、全体処理に対するモード設定である。ここで true に設定しない場合、allMethod() や
indivMethod() の記述は無視される。
• 27 行目は、パーティクル全体の色パレットを設定しているものである。ここでは緑色を ID 1 として登録している。

133

• 33 〜 40 行目では、新たにパーティクルが生成された際の処理を記述する。引数の p に新パーティクルのオブジェク
トが入っており、これに対して様々な設定を行う。36 行目では初期位置を、38 行目では色 ID を設定している。
• 43 〜 54 行目では、allMethod() メンバ関数を再定義している。allMethod() 関数には、パーティクル集合全体に対しての処理を記述する。ここではランダムにパーティクルの生成を行っているだけであるが、パーティクル全体に対して一括の処理を記述することもできる。
• 57 〜 82 行目では、indivMethod() メンバ関数を再定義している。indivMethod 関数には、個別のパーティクルに対する処理を記述する。
• indivMethod() 中では、65 〜 74 行目で速度ベクトルの入力を行っている。中心が原点で、z 軸に平行な半径 R の円柱の周囲を速度 (−Vx , 0, 0) の水流が流れているとする。このとき、各地点 (x, y, z) での水流を表す偏微分方程式は以下のようなものである。
R3
∂
P=V+
∂t
2

(

3V · P
V
−
P
r3
r5

)

ただし、
V = (−Vx , 0, 0),

P = (x, y, 0),

r = |P|

今回は、Vx = 0.2(60 行目の「water」変数)、R = 15(61 行目の「R」変数) として算出している。この式から、各パー
ティクルの速度ベクトルを算出し、74 行目で設定している。
• 77 〜 79 行目でパーティクル削除判定を行っている。パーティクルが x = −50 よりも左へ流れてしまった場合には 78
行目で削除を行っている。
• 93 行目では、パーティクル集合を fk ShapeViewer で表示するために getShape() 関数を用いている。
• 98 行目にあるように、handle() メンバ関数を用いることでパーティクル全体に 1 ステップ処理が行われる。その際には、設定した速度や加速度にしたがって各パーティクルが移動する。特に再設定しない限り、加速度は処理終了後も保存される。
ソースコード 13.6 パーティクルサンプル
1# include < FK / FK .h >2
3

double myRandom ( void )

4

{

5
6

return double ( rand ())/ double ( RAND_MAX );}7
8class MyParticle : public fk_Par ticleS et {9protected :

10

void

genMethod ( fk_Particle *);

11

void

allMethod ( void );

12

void

indivMethod ( fk_Particle *);13

public :1415

MyParticle ( void );
};

16
17

// コ ン ス ト ラ ク タ 。

18// こ こ に 、 様 々 な 設 定 を 記 述 し て お く 。

19

MyParticle :: MyParticle ( void )

20

{

21

setMaxSize (1000);// パ ー テ ィ ク ル の 最 大 数 設 定 。

22

srand ( time (0));// 乱 数 の 初 期 化 。

23

setIndivMode ( true ); // 個 別 処 理 ( indivMethod ) を ON に し て お く 。

24

setAllMode ( true );// 全 体 処 理 ( allMethod ) を ON に し て お く 。

25
26// パ レ ッ ト に 色 を 設 定 し て お く 。
134

27

setCo lo rPa le tt e (1 , 0.0 , 1.0 , 0.6);

28

return ;

29

}

30
31

// こ こ に パ ー テ ィ ク ル 生 成 時 の 処 理 を 記 述 す る 。

32// p に 新 た な パ ー テ ィ ク ル 要 素 が 入 っ て い る 。

33void MyParticle :: genMethod ( fk_Particle * p )

34

{

35

// 生 成 時 の 位 置 を 設 定

36

p - > setPosition (50.0 , myRandom ()*50.0 - 25.0 , myRandom ()*50.0 - 25.0);

37// パ ー テ ィ ク ル の 色 ID を 設 定

38

p - > setColorID (1);39
40

return ;}41
42// こ こ に 毎 ル ー プ 時 の 全 体 処 理 処 理 を 記 述 す る

43void MyParticle :: allMethod ( void )

44

{

45

for ( int i = 0; i < 5; i ++) {46

if ( myRandom () < 0.3) {47// 新 た な パ ー テ ィ ク ル を 生 成 。

48// 生 成 時 に genMethod () が 呼 ば れ る 。

49

newParticle ();

50

}

51

}

52
53
54

return ;}55
56// こ こ に 毎 ル ー プ 時 の 各 パ ー テ ィ ク ル へ の 処 理 を 記 述 す る 。

57void MyParticle :: indivMethod ( fk_Particle * p )

58

{

59

fk_Vector

pos , vec , tmp1 , tmp2 ;60

fk_Vector

water ( -0.2 , 0.0 , 0.0);

61

doubleR = 15.0;

62

doubler;

63
64// パ ー テ ィ ク ル の 位 置 を 取 得 。

65

pos = p - > getPosition ();66

pos . z = 0.0;67

r = pos . dist (); // | p | を r に 代 入 。

68
69// パ ー テ ィ ク ル の 速 度 ベ ク ト ル を 計 算

70

tmp1 = water /( r * r * r );71

tmp2 = ((3.0 * ( water * pos ))/( r * r * r * r * r )) * pos ;72

vec = water + (( R * R * R )/2.0) * ( tmp1 - tmp2 );73// パ ー テ ィ ク ル の 速 度 ベ ク ト ル を 代 入

74

p - > setVelocity ( vec );75
76// パ ー テ ィ ク ル の x 成 分 が -50 以 下 に な っ た ら 消 去 。

77

if ( pos . x < -50.0) {78

removePartic le ( p );

79

}

80
81
82

return ;
}

83

135

84

int main ()

85

{

86

fk_Sha peView erviewer (600 , 600);87

MyParticle

particle ;88

fk_Prismprism (40 , 15.0 , 15.0 , 50.0);89
90viewer . setShape (3 , & prism );91

viewer . setPosition (3 , 0.0 , 0.0 , 25.0);

92

viewer . setDrawMode (3 , FK_POLYMODE );93

viewer . setShape (2 , particle . getShape ());

94

viewer . setDrawMode (2 , FK_POINTMODE );95

viewer . setScale (10.0);

9697while ( viewer . draw () == true ) {particle . handle (); // パ ー テ ィ ク ル を 1 ス テ ッ プ 実 行 す る 。

98
99

}

100
101

return 0;}136

付録 A マテリアル一覧
表 A.1 FK システム中のデフォルトマテリアル一覧
色名

環境反射係数拡散反射係数

鏡面反射係数ハイライト

AshGray(0.2, 0.2, 0.2)

(0.4, 0.4, 0.4)

(0.01, 0.01, 0.01)(10.0)(0.15, 0.28, 0.23)(0.23, 0.47, 0.19)(0.37, 0.68, 0.28)(20.0)

Blue(0.3, 0.3, 0.3)

(0.0, 0.0, 0.7)

(1.0, 1.0, 1.0)

(30.0)

Brown(0.2, 0.1, 0.0)

(0.35, 0.15, 0.0)

(1.0, 1.0, 1.0)

(30.0)

BurntTitan(0.1, 0.07, 0.07)(0.44, 0.17, 0.1)

(0.6, 0.39, 0.1)

(16.0)

Coral(0.5, 0.3, 0.4)

(0.9, 0.5, 0.7)

(1.0, 1.0, 1.0)

(30.0)

Cream(0.3, 0.3, 0.3)

(0.8, 0.7, 0.6)

(1.0, 1.0, 1.0)

(30.0)

Cyan(0.3, 0.3, 0.3)

(0.0, 0.6, 0.6)

(1.0, 1.0, 1.0)

(30.0)

BambooGreen

DarkBlue(0.1, 0.1, 0.4)

(0.0, 0.0, 0.25)(1.0, 1.0, 1.0)

(30.0)

DarkGreen(0.1, 0.4, 0.1)

(0.0, 0.2, 0.0)

(1.0, 1.0, 1.0)

(30.0)

DarkPurple(0.3, 0.1, 0.3)

(0.3, 0.0, 0.3)

(1.0, 1.0, 1.0)

(30.0)

DarkRed(0.2, 0.0, 0.0)

(0.4, 0.0, 0.0)

(1.0, 1.0, 1.0)

(30.0)

DarkYellow(0.3, 0.3, 0.3)

(0.4, 0.3, 0.0)

(1.0, 1.0, 1.0)

(30.0)

DimYellow(0.18, 0.14, 0.0)

(0.84, 0.86, 0.07)(0.92, 0.82, 0.49)(30.0)

Flesh(0.3, 0.3, 0.3)

(0.8, 0.6, 0.4)

(1.0, 1.0, 1.0)

(30.0)

GlossBlack(0.0, 0.0, 0.0)

(0.04, 0.04, 0.04)(1.0, 1.0, 1.0)

(30.0)

GrassGreen(0.0, 0.1, 0.0)

(0.0, 0.7, 0.0)

(0.47, 0.98, 0.49)(30.0)

Gray1(0.3, 0.3, 0.3)

(0.6, 0.6, 0.6)

(0.1, 0.1, 0.1)

(30.0)

Gray2(0.3, 0.3, 0.3)

(0.2, 0.2, 0.2)

(0.1, 0.1, 0.1)

(30.0)

Green(0.3, 0.3, 0.3)

(0.0, 0.5, 0.0)

(1.0, 1.0, 1.0)

(30.0)

HolidaySkyBlue(0.01, 0.22, 0.4)

(0.2, 0.66, 0.92)(0.47, 0.74, 0.74)(30.0)

IridescentGreen(0.04, 0.11, 0.07)(0.09, 0.39, 0.18)(0.08, 0.67, 0.1)

(14.0)

Ivory(0.36, 0.28, 0.18)(0.56, 0.52, 0.29)(0.72, 0.45, 0.4)

(33.0)

LavaRed(0.14, 0.0, 0.0)

(0.62, 0.0, 0.0)

(1.0, 0.46, 0.46)(18.0)

LightBlue(0.3, 0.3, 0.3)

(0.4, 0.4, 0.9)

(1.0, 1.0, 1.0)

(30.0)

LightCyan(0.1, 0.2, 0.2)

(0.0, 0.5, 0.5)

(0.2, 0.2, 0.2)

(60.0)

LightGreen(0.3, 0.3, 0.3)

(0.5, 0.7, 0.3)

(1.0, 1.0, 1.0)

(30.0)

LightViolet(0.3, 0.3, 0.3)

(0.5, 0.4, 0.9)

(1.0, 1.0, 1.0)

(30.0)(0.21, 0.09, 0.23)(0.64, 0.54, 0.6)

(0.4, 0.26, 0.37)(15.0)

MatBlack(0.0, 0.0, 0.0)

(0.0, 0.0, 0.0)

(0.0, 0.0, 0.0)

(30.0)

Orange(0.3, 0.3, 0.3)

(0.8, 0.3, 0.0)

(0.2, 0.2, 0.2)

(30.0)

PaleBlue(0.3, 0.3, 0.3)

(0.5, 0.7, 0.7)

(1.0, 1.0, 1.0)

(30.0)

PearWhite(0.32, 0.29, 0.18)(0.64, 0.61, 0.5)

(0.4, 0.29, 0.17)(15.0)

Pink(0.6, 0.2, 0.3)

(0.9, 0.55, 0.55)(1.0, 1.0, 1.0)

(30.0)

Purple(0.3, 0.3, 0.3)

(0.7, 0.0, 0.7)

(1.0, 1.0, 1.0)

(30.0)

Raw(1.0, 1.0, 1.0)

(1.0, 1.0, 1.0)

(0.0, 0.0, 0.0)

(0.0)

Red

(0.3, 0.3, 0.3)

(0.7, 0.0, 0.0)

(1.0, 1.0, 1.0)

(30.0)

TrueWhite(1.0, 1.0, 1.0)

(1.0, 1.0, 1.0)

(0.0, 0.0, 0.0)

(0.0)

UltraMarine(0.01, 0.03, 0.21)(0.07, 0.12, 0.49)(0.53, 0.52, 0.91)(11.0)

Lilac

Violet(0.3, 0.3, 0.3)

(0.4, 0.0, 0.8)

(1.0, 1.0, 1.0)

(30.0)

White(0.3, 0.3, 0.3)

(1.0, 1.0, 1.0)

(0.1, 0.1, 0.1)

(30.0)WhiteLight(0.0, 0.0, 0.0)

(1.0, 1.0, 1.0)

(1.0, 1.0, 1.0)

(30.0)

Yellow(0.3, 0.3, 0.3)

(0.8, 0.6, 0.0)

(1.0, 1.0, 1.0)

(30.0)

137