[Atelier Blue アトリエブルー]HomeプログラミングManaged DirectX3D>直感に反しない回転(MatrixとQuaternion)

直感に反しない回転(MatrixとQuaternion)

回転だけなら意外と簡単です。とりあえず、MatrixとQuaternionの両方を切り替えられます。

ソースコード

操作「上」「下」「右」「左」「Shift+右」「Shift+左」で回転。「Q」で切り替えです。

using System;
using System.Drawing;
using System.Windows.Forms;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;

namespace Project2
{
    /// <summary>
    /// md3d2 の概要の説明です。
    /// </summary>
    public class md3d2:Form
    {
        //回転系
        private Quaternion qua=Quaternion.Identity;
        private Matrix rotationMatrix = Matrix.Identity;
        private bool isMatrix=true;

        public md3d2():base()
        {

            this.MinimumSize=new Size(80,60);
            this.ClientSize = new Size(300,300);
            this.Text ="Direct3D-My";
            this.KeyDown+=new KeyEventHandler(md3d2_KeyDown);
            this.KeyUp+=new KeyEventHandler(md3d2_KeyUp);
        }

        private Device device_;
        private PresentParameters presentParam_;


        /// <summary>
        /// 立方体メッシュ
        /// </summary>
        private Mesh teaMesh_;

        /// <summary>
        /// Direct3Dの初期化を行います。
        /// </summary>
        /// <returns>初期化が成功したかどうか</returns>
        public bool DXInitialize()
        {
            try
            {
                presentParam_ = new PresentParameters();

                presentParam_.Windowed =true;
                presentParam_.SwapEffect = SwapEffect.Discard;
                presentParam_.AutoDepthStencilFormat =DepthFormat.D16;
                presentParam_.EnableAutoDepthStencil=true;


                device_ = new Device(0,DeviceType.Hardware,this
                    ,CreateFlags.HardwareVertexProcessing,presentParam_);

                device_.RenderState.ZBufferEnable=true;
                device_.RenderState.ZBufferWriteEnable=true;


                //メッシュを作成
                creatMesh();

                return true;
            }
            catch
            {
                return false;
            }

        }


        /// <summary>
        /// メッシュを作成する
        /// </summary>
        private void creatMesh()
        {
            teaMesh_ = Mesh.Teapot(device_);
        }

        public void Render()
        {
            if(device_==null)return;
            if(this.WindowState ==FormWindowState.Minimized)return;

            //回転計算
            rotation();

            //回転を行う
            if(isMatrix)
            {
                device_.Transform.World = rotationMatrix;
                this.Text="Matrix";
            }
            else
            {
                device_.Transform.World =
                Matrix.RotationQuaternion(qua);
                this.Text="Quaternion";
            }

            //カメラの設定を行う
            device_.Transform.View = Matrix.LookAtLH(
                new Vector3( 5.0f, 0.0f, 0.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,
                (float)this.ClientSize.Width / (float)this.ClientSize.Height,
                3.0f, 15.0f);

            //device_.RenderState.Lighting =false;
            //ライトの設定
            device_.Lights[0].Direction =Vector3.Normalize(new Vector3(-1,-2,-3));
            device_.Lights[0].Type = LightType.Directional;

            device_.Lights[0].Diffuse = Color.White;
            device_.Lights[0].Ambient = Color.FromArgb(255,40,40,40);

            device_.Lights[0].Enabled =true;
            device_.Lights[0].Update();



            //マテリアルの設定
            Material mat =new Material();
            mat.AmbientColor = new ColorValue(1.0f,1.0f,0.1f);
            mat.DiffuseColor = new ColorValue(1.0f,1.0f,0.1f);

            device_.Material = mat;

            device_.Clear(ClearFlags.Target|ClearFlags.ZBuffer,Color.Blue,1.0f,0);

            device_.BeginScene();

            teaMesh_.DrawSubset(0);

            device_.EndScene();

            try
            {
                //更新
                device_.Present();
            }
            catch(DeviceLostException)
            {
                resetDevice();

            }

        }



        /// <summary>
        /// デバイスのリセットを行う
        /// </summary>
        private void resetDevice()
        {
            int result;
            if(!device_.CheckCooperativeLevel(out result))
            {
                if(result ==(int)ResultCode.DeviceLost)
                {
                    //ちょっと待つ
                    System.Threading.Thread.Sleep(10);
                }
                else if(result ==(int)ResultCode.DeviceNotReset)
                {
                    device_.Reset(presentParam_);
                }
            }
        }

        //キー入力
        private bool up,down,left,right,shift;

        private void md3d2_KeyDown(object sender, KeyEventArgs e)
        {
            switch(e.KeyCode)
            {
            case Keys.Down:
                down = true;
                break;
            case Keys.Up:
                up = true;
                break;
            case Keys.Right:
                right = true;
                break;
            case Keys.Left:
                left = true;
                break;
            case Keys.Q:
                isMatrix = !isMatrix;
                break;
            }
            shift = e.Shift;
        }

        private void md3d2_KeyUp(object sender, KeyEventArgs e)
        {
            switch(e.KeyCode)
            {
            case Keys.Down:
                down = false;
                break;
            case Keys.Up:
                up = false;
                break;
            case Keys.Right:
                right = false;
                break;
            case Keys.Left:
                left = false;
                break;
            case Keys.Shift:
                shift=false;
                break;
            }
            shift = e.Shift;
        }

        private void rotation()
        {
            float x=0,y=0,z=0;
            if(down)
            {
                z-=(float)Math.PI/90f;
            }
            if(up)
            {
                z+=(float)Math.PI/90f;
            }
            if(right)
            {
                if(shift)
                {
                    x+=(float)Math.PI/90f;
                }
                else
                {
                    y-=(float)Math.PI/90f;
                }
            }
            if(left)
            {
                if(shift)
                {
                    x-=(float)Math.PI/90f;
                }
                else
                {
                    y+=(float)Math.PI/90f;
                }
            }
            qua.Multiply(Quaternion.RotationYawPitchRoll(y,x,z));
            rotationMatrix.Multiply(Matrix.RotationYawPitchRoll(y,x,z));
        }
    }

    /// <summary>
    /// エントリクラス
    /// </summary>
    class Program
    {
        public static void Main()
        {
            using(md3d2 dxform =new md3d2())
            {
                if(!dxform.DXInitialize())
                {
                    MessageBox.Show("Diret3Dの初期化に失敗しました。"
                        ,"初期化の失敗");
                    return;
                }
                dxform.Show();

                while(dxform.Created)
                {
                    dxform.Render();
                    Application.DoEvents();
                }
            }
        }
    }
}

説明

「Quaternion」も「Matrix」も掛け算をして直感に反しない回転を実現しています。つまり、回転の角度を記録しておくわけではなく、現在の傾きに新たな傾きを掛け合わせているのです。

qua.Multiply(Quaternion.RotationYawPitchRoll(y,x,z));
rotationMatrix.Multiply(Matrix.RotationYawPitchRoll(y,x,z));

ここの部分です。「*=」も使えるような気がしますが、メソッド呼び出しにしてみました。


ページの一番上へ
前のページへ 一覧に戻る 次のページへ
初版2006-4-2 最終更新2006-5-14
[Atelier Blue アトリエブルー]HomeプログラミングManaged DirectX3D>直感に反しない回転(MatrixとQuaternion)