[PR]子育てママさんへ:3年毎に15万円うけとれる保険?

アニメーションしてみる

このページの概要

 今回は、少しこったものを作ってみたいと思います。親子関係(以後リレーションと呼ぶことにする(の定義で作ったクラスを使って、3つのオブジェクトを制御して、簡単なアームロボットのような物を作って見ます。

アームロボットについて

 イメージしやすいようにアームロボットのようなオブジェクトの図を以下に示します。

図1 アームロボット(Shadeで作成)

 赤色オブジェクトをA,青色オブジェクトをB、緑色オブジェクトをCとします。黄色の部分はかくリレーションの軸となるものです。各軸の回転角度を変えることでアーム(Cオブジェクト)を伸ばしたり縮めたりします。この機構により任意角度(0〜360°)の位置にアームを伸び縮みすることができるようになります。アームは伸び縮みする際は直線運動をするようにします。アームが直線運動するためには、各軸の回転角を制御してアームの姿勢を保ちます。それでは、姿勢を保つためには各軸の回転角をどのようにすればいいでしょうか。

各軸の回転角の算出方法

 上の図のモデルを単純化してみましょう。以下に単純化したモデルを示します。

図2 アームロボット(モデル)

実際にはアームは常にA方向へ向きます。回転角度はそれぞれ次のように定義します。

表1:回転角度の定義
θ 0〜2π
θ1 0〜2π
θ2 0〜2π
θ3 0〜π

詳しく説明すると、θ2はAオブジェクトからの回転角なので、Bの座標系で0〜2π、θ3も同様にBオブジェクトとリレーションの関係なので、Cの座標系で0〜πとなります。A,B,Cの座標は反時計まわりをプラスと定義します。θはアームロボット自体の回転角です。図1を見てもわかるように、三角形ABCは2等辺三角形をなし、三角形の内角AとCは等しく、θの関係はθ1=π+θ3となっていることがわかります。また、θ2は、-(θ1+θ3)つまり、-2×θ1となっていることがわかります。それでは、各θ間の関係を以下にまとめて見ます。

表2:各θ間の関係
θ1 θ3+θ1(Aオブジェクトの回転角下のθ1には関係しない)
θ2 -2×θ1
θ3 π+θ1

アニメーション(アームロボットの制御)
 
 さて、各物体間の関係がわかったところで、この関係を使いながらアームロボットを制御してみましょう。今回はアニメーションということで、次のような動作をさせてみたいと思います。以下に動作手順を示したフローチャートを示します。

図3 アニメーションフローチャート

オブジェクトへの適用

 では、オブジェクトを細かく定義してみます。今回のアニメーションでは図4、図5の二つの形状を用います。アームは図4、図5に軸と土台です。

図4 アームの定義

図4を基にした各アームの値を表3に示します。
 
表3 アームの定義
No radius1 radius2 width height
オブジェクトA 12 10 50 12
オブジェクトB 10 8 50 12
オブジェクトC 8 8 50 12

これはモデリングソフト上での定義の値で実際には1/10の値となっているので注意が必要です。

図5 軸の定義
 
図5を基にした各軸と土台の値を表3に示します。
表4 軸と土台の定義
No radius1 height
土台 30 12
軸A 10 12
軸B 8 12
軸C 6 12

この値もDirectXで用いるときには1/10倍して用います。軸Aが土台とアームAの軸、軸BがアームAとアームBの軸、軸CがアームBとアームCの軸となっています。

これでモデルの定義が終わりました。では、次にプログラムの実装をしてみたいと思います。

ソースコード
 
 今回はRelationModelとBaseModelのソースコードは省き、メインの部分のみ示しておきます。


オブジェクトの表示のソースコード
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
using System;
using System.Drawing;
using System.Windows.Forms;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using Direct3D=Microsoft.DirectX.Direct3D;
using D3DModel;
namespace ShowModel {
        enum ArmState { Straight, Rotate };
        public class Meshes : Form {

                Device device = null;
                RelationModel armA,armB,armC;
                RelationModel axisA,axisB,axisC;
                BaseModel mbase;
                PresentParameters presentParams = new PresentParameters();

                private float theta1,theta2,theta3,theta;
                private float direction,direction2;
                private float speed;
                private ArmState state;

                public Meshes() {
                        this.ClientSize = new System.Drawing.Size(500,400);
                        this.Text = "アニメーション";
                        armA = new RelationModel();
                        armB = new RelationModel();
                        armC = new RelationModel();
                        axisA = new RelationModel();
                        axisB = new RelationModel();
                        axisC = new RelationModel();
                        InitializeParameter();

                }
                void InitializeParameter() {
                        armA.parent = axisA;
                        axisB.parent= armA;
                        armB.parent = axisB;
                        axisC.parent = armB;
                        armC.parent = axisC;
                        mbase = new BaseModel();
                        theta1 =(float)Math.PI/3.0f;
                        theta = (float)Math.PI/2.0f;
                        theta2 =-2.0f*theta1;
                        theta3 = (float)Math.PI + theta1;
                        direction = 1.0f;
                        direction2 = -1.0f;
                        state = ArmState.Straight;      
                        speed = 0.05f;
                }
                bool InitializeGraphics() {
                        try {
                                presentParams.Windowed = true;
                                presentParams.SwapEffect = SwapEffect.Discard;
                                presentParams.EnableAutoDepthStencil = true;
                                presentParams.AutoDepthStencilFormat = DepthFormat.D16;
                                device = new Device(0, DeviceType.Hardware,
                                        this,
                                        CreateFlags.SoftwareVertexProcessing,
                                        presentParams);
                                device.DeviceReset += 
                                        new System.EventHandler(this.OnResetDevice);
                                this.OnResetDevice(device, null);
                        }
                        catch (DirectXException) {
                                return false;
                        }
                        return true;
                }
                public void OnResetDevice(object sender, EventArgs e) {
                        Device dev = (Device)sender;
                        dev.RenderState.ZBufferEnable = true;
                        armA.LoadMesh(device,"ArmA.x");
                        armB.LoadMesh(device,"ArmB.x");
                        armC.LoadMesh(device,"ArmC.x");
                        axisA.LoadMesh(device,"AxisA.x");
                        axisB.LoadMesh(device,"AxisB.x");
                        axisC.LoadMesh(device,"AxisC.x");
                        mbase.LoadMesh(device,"base.x");
                }
                private void SetupMatrices() {
                        device.Transform.View = 
                                Matrix.LookAtLH(new Vector3( 0.0f, 2.0f,-6.0f ), 
                                new Vector3( 0.0f, 0.0f, 0.0f ), 
                                new Vector3( 0.0f, 1.0f, 0.0f ) );
                        device.Transform.Projection = 
                                Matrix.PerspectiveFovLH(
                                (float)(Math.PI / 4),
                                device.Viewport.Width/(float)device.Viewport.Height,
                                1.0f,
                                100.0f );

                        switch(state) {
                                case ArmState.Straight: {
                                        theta1 +=speed*direction;
                                        theta2 = -2.0f*(theta1);
                                        theta3 = (float)Math.PI +theta1 ;
                                        if (Math.Floor(theta1/speed) ==
                                                Math.Floor(Math.PI/speed)) {
                                                direction = -1.0f;
                                        } else if(Math.Floor(theta1/speed) ==
                                                Math.Floor(Math.PI/(3.0*/speed))) {
                                                direction = 1.0f;
                                                state = ArmState.Rotate;        
                                        }
                                        break;
                                }
                                case ArmState.Rotate: {
                                        theta += speed*direction2;
                                        if (Math.Floor(theta/speed) ==
                                                Math.Floor(Math.PI/(2.0*speed))) {
                                                direction2 = -1.0f;
                                                state = ArmState.Straight;      
                                                break;
                                        }else if(Math.Floor(theta/speed) ==
                                                Math.Floor(0.00f)){ 
                                                direction2 = 1.0f;
                                                state = ArmState.Straight; 
                                                break;
                                        }
                                        break;
                                }
                        }
                        Matrix m = new Matrix();
                        m = Matrix.RotationY(-theta);
                        axisA.localMatrix = m;
                        armA.axis = new Vector3(0.0f,0.0f,0.0f);
                        armA.direction = new Vector3(0.0f,1.0f,0.0f);
                        armA.axis = new Vector3(0.0f,0.7f,0.0f);
                        armA.angle = theta1;
                        axisB.axis = new Vector3(5.0f,0.7f,0.0f);
                        armB.direction = new Vector3(0.0f,1.0f,0.0f);
                        armB.angle = theta2;
                        armB.axis = new Vector3(0.0f,0.7f,0.0f);
                        axisC.axis = new Vector3(5.0f,0.7f,0.0f);
                        armC.direction = new Vector3(0.0f,1.0f,0.0f);
                        armC.angle = theta3;
                        armC.axis = new Vector3(0.0f,0.7f,0.0f);
                }

                private void SetupLights() {
                        System.Drawing.Color col = System.Drawing.Color.White;
                        Direct3D.Material mtrl = new Direct3D.Material();
                        mtrl.Diffuse = col;
                        mtrl.Ambient = col;
                        device.Material = mtrl;
                        device.Lights[0].Type = LightType.Directional;
                        device.Lights[0].Diffuse =
                                System.Drawing.Color.DarkTurquoise;
                        device.Lights[0].Direction = new Vector3(0.0f, 1.0f, 1.0f);
                        device.Lights[0].Commit();
                        device.Lights[0].Enabled = true;
                        device.RenderState.Ambient = Color.FromArgb(10,10,10);
                }
                private void Render() {
                        if (device == null) 
                                return;
                        device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, 
                                System.Drawing.Color.DarkGray, 1.0f, 0);
                        device.BeginScene();
                        SetupMatrices();
                        SetupLights();
                        
                        Matrix world = new Matrix();
                        world = Matrix.Identity;
                        float sc = 0.3f;
                        world = Matrix.Scaling(sc,sc,sc);
                        mbase.localMatrix = Matrix.Translation(0.0f,-0.8f,0.0f);
                        mbase.DrawModel(device,world);
                        axisA.DrawModel(device,world);
                        armA.DrawModel(device,world);
                        axisB.DrawModel(device,world);
                        armB.DrawModel(device,world);
                        axisC.DrawModel(device,world);
                        armC.DrawModel(device,world);
                        
                        device.EndScene();
                        device.Present();
                }

                protected override void 
                        OnPaint(System.Windows.Forms.PaintEventArgs e) {
                        this.Render();
                }
                protected override void 
                        OnKeyPress(System.Windows.Forms.KeyPressEventArgs e) {
                        if ((int)(byte)e.KeyChar == 
                                (int)System.Windows.Forms.Keys.Escape)
                                this.Dispose();
                }
                static void Main() {
                        using (Meshes frm = new Meshes()) {
                                if (!frm.InitializeGraphics()) {
                                        MessageBox.Show("初期化できませんでした");
                                        return;
                                }
                                frm.Show();
                                while(frm.Created) {
                                        frm.Render();
                                        Application.DoEvents();
                                }
                        }
                }
        }
}

SetupMatrices()で実際のフローチャートに示したような処理を行っています。ここで110〜111行目を見ると、Math.Floorという数学関数を扱っています。これは、少数を丸めて比較するために用いています。speedで割っているのは有効桁数の影響を考慮しているためです。

実行結果

図 実行画面

 静止画像ではわかりにくいと思うので、動画バージョンも作りました。圧縮しすぎで、画面がかなり汚いですが、
これで、モデリングソフトで作ったモデルをDirectXで読み込んで表示することが出来るようになりました。実行結果のイメージは出来ると思います。

今回でDirectXの基礎的なソフトの作成は一応終了したいと思います。これからは力学についてのシュミレーションをいくつか作成していくことにします。

[PR]三井住友海上きらめき生命:医療保険のご案内と資料請求はこちらから