元派遣プログラマの自称技術系ブログです。雑記とか自作のオープンソースプロジェクトの話とか。
Javaとか組込とかできます。お仕事ください。

あそんでみた

NyARToolkitCSでxファイル形式のミクを読み込んでみた。

を参考に、サンプルに少々書き足したらあっさり動きました。

調子に乗ってWindowsMobileでもやろうとしたら、見事に玉砕しましたけども。(メッシュが美味く読み込めてない?エラーは出なかったんだけど、なんでだろう?)

サンプルはこんな感じ

using System;
using System.IO;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Threading;
using System.Windows.Forms;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using NyARToolkitCSUtils.Capture;
using NyARToolkitCSUtils.Raster;
using NyARToolkitCSUtils.Direct3d;
using NyARToolkitCSUtils.NyAR;
using jp.nyatla.nyartoolkit.cs;
using jp.nyatla.nyartoolkit.cs.core;
using jp.nyatla.nyartoolkit.cs.raster;
using jp.nyatla.nyartoolkit.cs.detector;

namespace SimpleLiteDirect3d
{

    public partial class SimpleLiteD3d : IDisposable, CaptureListener
    {
        /// <summary>
        /// メッシュ
        /// </summary>
        private Mesh _mesh = null;

        /// <summary>
        /// マテリアル情報配列
        /// </summary>
        private ExtendedMaterial[] _materials = null;

        /// <summary>
        /// テクスチャー配列
        /// </summary>
        private Texture[] _textures = null;

        private const int SCREEN_WIDTH=320;
        private const int SCREEN_HEIGHT=240;
        private const String AR_CODE_FILE = "../../../../../data/patt.hiro";
        private const String AR_CAMERA_FILE = "../../../../../data/camera_para.dat";
        //DirectShowからのキャプチャ
        private CaptureDevice  m_cap;
        //NyAR
        private D3dSingleDetectMarker m_ar;
        private DsXRGB32Raster m_raster;
        //背景テクスチャ
        private NyARSurface_XRGB32 m_surface;
        /// Direct3D デバイス
        private Device _device = null;
        /* 非同期イベントハンドラ
         * CaptureDeviceからのイベントをハンドリングして、バッファとテクスチャを更新する。
         */
        public void OnBuffer(CaptureDevice i_sender, double i_sample_time, IntPtr i_buffer, int i_buffer_len)
        {
            int w = i_sender.video_width;
            int h = i_sender.video_height;
            int s = w * (i_sender.video_bit_count / 8);
            
            //テクスチャにRGBを取り込み()
            lock (this)
            {
                //カメラ映像をARのバッファにコピー
                this.m_raster.setBuffer(i_buffer);

                //テクスチャ内容を更新
                this.m_surface.CopyFromXRGB32(this.m_raster);
            }
            return;
        }
        /* キャプチャを開始する関数
         */
        public void StartCap()
        {
            this.m_cap.StartCapture();
        }
        /* キャプチャを停止する関数
         */
        public void StopCap()
        {
            this.m_cap.StopCapture();
        }


        /* Direct3Dデバイスを準備する関数
         */
        private Device PrepareD3dDevice(Control i_window)
        {
            PresentParameters pp = new PresentParameters();

            // ウインドウモードなら true、フルスクリーンモードなら false を指定
            pp.Windowed = true;
            // スワップとりあえずDiscardを指定。
            pp.SwapEffect = SwapEffect.Flip;
            pp.BackBufferFormat = Format.X8R8G8B8;
            pp.BackBufferCount = 1;
            pp.EnableAutoDepthStencil = true;
            pp.AutoDepthStencilFormat = DepthFormat.D16;
            CreateFlags fl_base = CreateFlags.FpuPreserve;

            try{
                return new Device(0, DeviceType.Hardware, i_window.Handle, fl_base|CreateFlags.HardwareVertexProcessing, pp);
            }catch (Exception ex1){
                Debug.WriteLine(ex1.ToString());
                try{
                    return new Device(0, DeviceType.Hardware, i_window.Handle, fl_base | CreateFlags.SoftwareVertexProcessing, pp);
                }catch (Exception ex2){
                    // 作成に失敗
                    Debug.WriteLine(ex2.ToString());
                    try{
                        return new Device(0, DeviceType.Reference, i_window.Handle, fl_base | CreateFlags.SoftwareVertexProcessing, pp);
                    }catch (Exception ex3){
                        throw ex3;
                    }
                }
            }
        }
        public bool InitializeApplication(Form1 topLevelForm,CaptureDevice i_cap_device)
        {
            //キャプチャを作る(QVGAでフレームレートは30)
            i_cap_device.SetCaptureListener(this);
            i_cap_device.PrepareCapture(SCREEN_WIDTH, SCREEN_HEIGHT, 30);
            this.m_cap = i_cap_device;
            
            //ARの設定

            //ARラスタを作る(DirectShowキャプチャ仕様)。
            this.m_raster = new DsXRGB32Raster(i_cap_device.video_width, i_cap_device.video_height, i_cap_device.video_width * i_cap_device.video_bit_count / 8);

            //AR用カメラパラメタファイルをロードして設定
            D3dARParam ap = new D3dARParam();
            ap.loadFromARFile(AR_CAMERA_FILE);
            ap.changeSize(SCREEN_WIDTH, SCREEN_HEIGHT);

            //AR用のパターンコードを読み出し	
            NyARCode code = new NyARCode(16, 16);
            code.loadFromARFile(AR_CODE_FILE);

            //1パターンのみを追跡するクラスを作成
            this.m_ar = new D3dSingleDetectMarker(ap, code, 80.0);

            //計算モードの設定
            this.m_ar.setContinueMode(false);


            //3dデバイスを準備する
            this._device = PrepareD3dDevice(topLevelForm);

            //カメラProjectionの設定
            this._device.Transform.Projection = ap.getCameraFrustumRH();

            // ビュー変換の設定(左手座標系ビュー行列で設定する)
            // 0,0,0から、Z+方向を向いて、上方向がY軸
            this._device.Transform.View = Matrix.LookAtLH(
                new Vector3(0.0f, 0.0f, 0.0f), new Vector3(0.0f, 0.0f, 1.0f), new Vector3(0.0f, 1.0f, 0.0f));

            // ライトを無効
            this._device.RenderState.Lighting = false;

            // カリングを無効にしてポリゴンの裏も描画する
            //this._device.RenderState.CullMode = Cull.None;

            //背景サーフェイスを作成
            this.m_surface = new NyARSurface_XRGB32(this._device, SCREEN_WIDTH, SCREEN_HEIGHT);

            // Xファイルを読み込んでメッシュを作成する
            try
            {
                this._mesh = Mesh.FromFile("miku.x",
                    MeshFlags.Managed, this._device, out this._materials);
            }
            catch (DirectXException ex)
            {
                // メッシュの作成に失敗した場合は例外が飛んでくる
                MessageBox.Show(ex.ToString(), "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return false;
            }

            // 法線情報がなければ計算して作成
            if ((this._mesh.VertexFormat & VertexFormats.Normal) == 0)
            {
                // 法線情報を加えたメッシュを複製する
                Mesh tempMesh = this._mesh.Clone(this._mesh.Options.Value,
                    this._mesh.VertexFormat | VertexFormats.Normal, this._device);

                // 法線を計算
                tempMesh.ComputeNormals();

                // 古いメッシュを破棄し、置き換える
                this._mesh.Dispose();
                this._mesh = tempMesh;
            }

            // テクスチャーがあれば読み込み
            if (this._materials.Length >= 1)
            {
                // テクスチャー用の配列を作成
                this._textures = new Texture[this._materials.Length];

                // 配列分テクスチャーの読み込みを試みる
                for (int i = 0; i < this._materials.Length; i++)
                {
                    // 必ず null で初期化する
                    this._textures[i] = null;

                    // テクスチャー名が登録されているか確認
                    if (this._materials[i].TextureFilename != null &&
                        this._materials[i].TextureFilename.Length >= 1)
                    {
                        try
                        {
                            // テクスチャーを読み込む
                            this._textures[i] = TextureLoader.FromFile(this._device,this._materials[i].TextureFilename);
                        }
                        catch (DirectXException ex)
                        {
                            // テクスチャーの作成に失敗した場合は例外が飛んでくる
                            MessageBox.Show(ex.ToString(), "エラー",
                                MessageBoxButtons.OK, MessageBoxIcon.Error);
                            return false;
                        }
                    }
                }
            }


            return true;
        }

        //メインループ処理
        public void MainLoop()
        {
            //ARの計算
            Matrix trans_matrix = new Matrix();
            bool is_marker_enable;
            lock (this)
            {
                this._device.Clear(ClearFlags.ZBuffer | ClearFlags.Target,Color.DarkBlue, 1.0f, 0);

                //マーカーは見つかったかな?
                is_marker_enable = this.m_ar.detectMarkerLite(this.m_raster, 110);
                if (is_marker_enable)
                {
                    //あればMatrixを計算
                    this.m_ar.getD3dMatrix(out trans_matrix);
                }

                // 背景サーフェイスを直接描画
                Surface dest_surface = this._device.GetBackBuffer(0, 0, BackBufferType.Mono);
                Rectangle src_dest_rect = new Rectangle(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
                this._device.StretchRectangle(this.m_surface.d3d_surface, src_dest_rect, dest_surface, src_dest_rect, TextureFilter.None);

                // 3Dオブジェクトの描画はここから
                this._device.BeginScene();

                //マーカーが見つかっていて、0.4より一致してたら描画する。
                if (is_marker_enable && this.m_ar.getConfidence()>0.4)
                {

                    // 描画する頂点のフォーマットをセット
                    this._device.VertexFormat = CustomVertex.PositionColored.Format;


                    //立方体を20mm上(マーカーの上)にずらしておく
                    Matrix transform_mat2 = Matrix.Translation(0,0,20.0f);
                    transform_mat2 *= Matrix.Scaling(new Vector3(0.2f, 0.2f, 0.2f));
                    transform_mat2 *= Matrix.RotationX((float)Math.PI*1/2);
                    //変換行列を掛ける
                    transform_mat2*=trans_matrix;

                    
                    // 計算したマトリックスで座標変換
                    this._device.SetTransform(TransformType.World, transform_mat2);

                    for (int i = 0; i < this._materials.Length; i++)
                    {
                        // テクスチャーのセット
                        this._device.SetTexture(0, this._textures[i]);

                        // マテリアルをセット
                        this._device.Material = this._materials[i].Material3D;

                        // 描画
                        this._mesh.DrawSubset(i);
                    }
                }

                // 描画はここまで
                this._device.EndScene();

                // 実際のディスプレイに描画
                this._device.Present();
            }
            
        }

        // リソースの破棄をするために呼ばれる
        public void Dispose()
        {
            // テクスチャーの解放
            if (this._textures != null)
            {
                foreach (Texture i in this._textures)
                {
                    if (i != null)
                    {
                        i.Dispose();
                    }
                }
            }

            // メッシュの解放
            if (this._mesh != null)
            {
                this._mesh.Dispose();
            }
            // Direct3D デバイスのリソース解放
            if (this._device != null)
            {
                this._device.Dispose();
            }
        }
    }
}

そのうち完全版を公開します。