2012年4月28日土曜日

Win32++ (1) ボタン

main.cpp

// ボタンのテスト
#include <stdcontrols.h>
#include <controls.h>
#include <cstring.h>
#pragma comment(lib, "comctl32.lib")

// メッセージ
#define MY_MSG_001  (WM_APP + 1)

// ボタン
class MyButton : public CButton {
protected:
  int   m_nID;
public:
  MyButton(int id) : m_nID(id), CButton() {}
  virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam) {
    switch (HIWORD(wParam)) {
    case BN_CLICKED:
      {
        CWnd* pParent = this->GetParent();
        if(pParent) {
          this->SendMessage(pParent->GetHwnd(), MY_MSG_001, m_nID, 0);
        }
      }
      return TRUE;
    }
    return FALSE;
  }
};

// ビュー
class CView : public CWnd {
public:
  CView()       {}
  virtual ~CView()  {
    delete m_pButton1;
    delete m_pButton2;
  }

  virtual void OnCreate() {
    // ボタンを作成
    m_pButton1 = new MyButton(1);
    m_pButton1->Create(this);
    m_pButton1->MoveWindow(10, 10, 100, 24);
    m_pButton1->SetWindowTextW(L"ボタン1");

    m_pButton2 = new MyButton(2);
    m_pButton2->Create(this);
    m_pButton2->MoveWindow(10, 50, 100, 24);
    m_pButton2->SetWindowTextW(L"ボタン2");
  }
protected:
  virtual LRESULT WndProc(UINT uMsg, WPARAM wParam, LPARAM lParam);

  MyButton* m_pButton1;
  MyButton* m_pButton2;
};

LRESULT CView::WndProc(UINT uMsg, WPARAM wParam, LPARAM lParam) {
  CString str;

  switch (uMsg) {
  case MY_MSG_001:
    str.Format(L"%d %d\n", uMsg, wParam);
    TRACE(str);
    break;
  case WM_DESTROY:
    m_pButton1->Destroy();
    m_pButton2->Destroy();
    ::PostQuitMessage(0);
    break;
  }
  return WndProcDefault(uMsg, wParam, lParam);
}

class MyApp : public CWinApp {
public:
  MyApp() {}
    virtual ~MyApp() {}
  virtual BOOL InitInstance() {
    m_View.Create();
    return TRUE;
  }
private:
    CView m_View;
};

int APIENTRY WinMain(HINSTANCE, HINSTANCE, LPSTR, int) 
{
    MyApp MyApp;
    return MyApp.Run();
}

2012年4月27日金曜日

OpenCV 画像の90度回転

テキストをパースして、複数の画像を1枚にまとめてみる テスト

パース処理にboostのtokenizerを使用

貼り付ける画像が、貼り付け先の画像からはみ出るとエラーになるよ


テキストファイルの例

2                     <-- 貼り付ける画像数
1024 512              <-- 貼り付け先の画像の幅, 高さ
c:/temp/test1.bmp"    <-- 画像の絶対パス
0 0 0 200 200 0       <-- [画像のインデックス] [X座標] [Y座標] [幅] [高さ] [※回転]
c:/temp/test2.bmp"
1 210 0 32 128 1

main.cpp

#include <fstream>
#include <boost/lexical_cast.hpp>
#include <boost/tokenizer.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>

// 貼り付ける画像情報
struct ImageInfo {
  std::string   path;   // ファイルの絶対パス
  int       param[6]; // パラメータ
};

// src_imgをdst_imgのrect領域にコピー
void MatCopyPaste(const cv::Rect& rect, cv::Mat& dst_img, cv::Mat& src_img) {
  cv::Mat d = dst_img(rect);    // 貼り付け先のMatを取得
  src_img.copyTo(d);        // 貼り付け画像をコピーする
}

// 1画像の処理
void ImageProc(cv::Mat& dst_img, const ImageInfo& info) {
  // 貼り付ける画像を読み込む
  cv::Mat src_img = cv::imread(info.path, 1);
  assert(!src_img.empty());

  // 貼り付け先の画像のサイズ
  int imgWidth = info.param[3];
  int imgHeight = info.param[4];
  // 貼り付け位置
  int imgX = info.param[1];
  int imgY = info.param[2];

  // 貼り付け画像
  cv::Mat tmp_img(cv::Size(imgWidth, imgHeight), src_img.type());

  if(info.param[5] == 1) {
    // 画像を回転
    int src_w = src_img.size().width;
    int src_h = src_img.size().height;
    
    cv::Mat rot_img(cv::Size(src_h, src_w), src_img.type(), cv::Scalar(0, 0, 0));
    cv::transpose(src_img, rot_img);  // 転置 左回り 反時計回りに90度回転 
    cv::flip(rot_img, rot_img, 1);    // 左右反転 時計回りに90度回転

    cv::resize(rot_img, tmp_img, tmp_img.size(), cv::INTER_CUBIC);
  } else {
    // サイズ変更
    cv::resize(src_img, tmp_img, tmp_img.size(), cv::INTER_CUBIC);
  }

  // 指定した矩形に画像を貼り付ける
  cv::Rect rect(imgX, imgY, imgWidth, imgHeight); 
  MatCopyPaste(rect, dst_img, tmp_img);
}

int main( int argc, char **argv ) {
  int count = 0;
  std::string str;
  // 貼り付ける画像を記述したテキストファイルを読み込む
  std::ifstream ifs( "./data/ImgList.txt" );

  typedef boost::char_separator char_sep;
  typedef boost::tokenizer tokenizer;
  char_sep sep(" ");    // スペース区切り

  int imgNum, imgWidth, imgHeight;
  std::vector  imgDataArray;
  ImageInfo imgData;

  while(std::getline(ifs, str)) {
    std::vector tokenArray;

    // 読み込んだ文字列をトークンに分割して配列に格納
    tokenizer tok(str, sep);
    for (tokenizer::iterator tok_iter = tok.begin(); tok_iter != tok.end(); ++tok_iter) {
      tokenArray.push_back( *tok_iter );
    }

    switch(count) {
    case 0:  // 画像数
      // int にキャスト
      imgNum = boost::lexical_cast(tokenArray[0]);
      break;
    case 1:  // 画像の幅と高さ
      imgWidth = boost::lexical_cast(tokenArray[0]);
      imgHeight = boost::lexical_cast(tokenArray[1]);
      break;
    default: // 画像のパラメータ
      if(0 == count % 2) {
        // ファイルパス
        imgData.path = tokenArray[0];
      } else {
        // パラメータ
        for(int i = 0; i < 6; i++) {
          imgData.param[i] = boost::lexical_cast(tokenArray[i]);
        }
        imgDataArray.push_back(imgData);
      }
      break;
    }
    count++;
  }

  // 貼り付け先の画像
  cv::Mat dstImg(cv::Size(imgWidth, imgHeight), CV_8UC3, cv::Scalar(0, 0, 0));

  // 画像処理
  for(size_t i = 0; i < imgDataArray.size(); i++) {
    ImageProc(dstImg, imgDataArray[i]);
  }

  // 結果を保存
//  cv::imwrite("./data/result.png", dstImg);

  // 結果を表示
  cv::namedWindow("Result", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
  cv::imshow("Result", dstImg);
  cv::waitKey(0);
  return 0;
}

2012年4月23日月曜日

MAXScript マテリアル毎にノードをまとめる

FBXをインポートしてみたら、ノード数が多いので

sceneMaterialsを使って、マテリアル毎にまとめるテスト

MergeNodeTest.ms

g_NodeNameArray = #() -- シーン中のノード名の配列

-- 再帰関数でノードを列挙する
fn EnumNode n =
(
  for n in n.children do
  (
    -- 配列にノード名を追加
    append g_NodeNameArray n.name
    -- 再帰処理
    EnumNode n
  )
)

-- 文字列の配列から一致する文字列のインデックスを取得
fn GetNameIndex strArray str = (
  for i = 1 to strArray.count do (
    if strArray[i] == str do (  
      return i
    )
  )
  -1  -- 一致する文字列が無い
)

-- マテリアル毎にノードをまとめるテスト
fn MergeNodePerStdMtl = (
  -- シーン内のノード数
  format "Scene Node Num = %\n" g_NodeNameArray.count
  
  -- シーン内のマテリアル数を出力
  format "sceneMaterials.count %\n" sceneMaterials.count
  
  -- シーン内のスタンダードマテリアル名の配列
  strStdMtlNameArray = #()
  
  for mtl in sceneMaterials do (
    -- マテリアルのクラス名を取得
    c = classof mtl
--    format "mtl.name = % class = %\n" mtl.name c

    if c == Standardmaterial do (
      -- スタンダードなマテリアル名を追加
      append strStdMtlNameArray mtl.name  
    )
    
    if c == Multimaterial do (
      nSubMtl = getNumSubMtls mtl
--      format "nSubMtl = %\n" nSubMtl    -- サブマテリアル数
    )
  )
  
  -- strStdMtlNameArrayが空なので抜ける
  if strStdMtlNameArray.count == 0 do return 1

  -- スタンダードマテリアルを使用するノードの配列を作成
  stdMtlNodeArray = #()
  stdMtlNodeArray.count = strStdMtlNameArray.count
  
  -- シーン内のノードをスタンダードマテリアル毎にマージする
  for i = 1 to g_NodeNameArray.count do (
    -- ノードを名前から取得
    n = getNodeByName g_NodeNameArray[i]
    if undefined == n do continue
    
    -- ノードのマテリアルを取得
    mtl = n.material
    if undefined == mtl do continue
    
    c = classof mtl -- マテリアルのクラス
    if c == Standardmaterial do (
      -- 配列のインデックスを取得
      idx = GetNameIndex strStdMtlNameArray mtl.name
      if -1 == idx do continue
      
      if undefined == stdMtlNodeArray[ idx ] then (
        -- 空なので、ノードをセット
        stdMtlNodeArray[idx] = n
      )
      else (
        -- ノードをマージする
        stdMtlNodeArray[idx] = stdMtlNodeArray[idx] + n
        -- マージしたノードは削除
        delete n
      )
    )
  )
)

rn = rootNode       -- ルートノードを取得する
EnumNode rn         -- シーン内のノードを列挙
MergeNodePerStdMtl()  -- スタンダードなマテリアル毎にノードをマージ

2012年4月21日土曜日

Unity アニメーションイベント

マテリアルのシェーダーは Transparent/Diffuse を使用

TestGL2_b.cs

using UnityEngine;
//using System.Collections;
 
//[RequireComponent(typeof(Animation))]
public class TestGL2_b : MonoBehaviour {
    Vector3[]       m_VtxArray;                 // 頂点の配列
    Vector2[]       m_UvArray;                  // UVの配列
    Vector2         m_uvOffset = Vector2.zero;  // UVのオフセット

    public Material m_Mtl;                      // マテリアル

    AnimationCurve  m_animCurve;                // アニメカーブ

    AnimationEvent  m_animEvtStart;             // 開始イベント
    AnimationEvent  m_animEvtEnd;               // 終了イベント

    AnimationClip   m_animClip;                 // アニメクリップ
    string          m_animName = "anim1";       // アニメ名

    int             m_loopCount = 0;            // ループ回数

    public void Start () {
        // アニメコンポーネントを追加
        Animation anim = GetComponent();
        if(null == anim) anim = gameObject.AddComponent();
        
        // 頂点とUVの作成
        m_VtxArray = new Vector3[4];
        m_UvArray = new Vector2[4];

        m_VtxArray[0] = new Vector3(0.0f, 0.0f, 0.0f);  // BottomLeft
        m_VtxArray[1] = new Vector3(0.0f, 1.0f, 0.0f);  // TopLeft
        m_VtxArray[2] = new Vector3(1.0f, 0.85f, 0.0f); // TopRight
        m_VtxArray[3] = new Vector3(1.0f, 0.0f, 0.0f);  // BottomRight

        m_UvArray[0] = new Vector2(0.0f, 0.0f);
        m_UvArray[1] = new Vector2(0.0f, 1.0f);
        m_UvArray[2] = new Vector2(1.0f, 1.0f);
        m_UvArray[3] = new Vector2(1.0f, 0.0f);

        CreateAnim();
    }

    void OnRenderObject() {
        // フェードイン、アウトをループさせるテスト
        // マテリアルの色変更
        Color color = m_Mtl.GetColor("_Color");
        // アニメの時間取得
        float time = animation[m_animName].time;    // アニメーションの経過時間になる
        
        time -= m_loopCount * animation[m_animName].length;
        // アニメカーブの値を取得
        float value = m_animCurve.Evaluate(time);
        color.a = value;

        // マテリアルの色セット
        m_Mtl.SetColor("_Color", color);

        // 四角形を描画
        DrawQuad();
    }

    // アニメーションを作成
    void CreateAnim() {
        float startTime = 0.0f;
        float endTime = 5.0f;
        float midTime = (endTime - startTime) * 0.5f;

        // アニメカーブを作成
        m_animCurve = new AnimationCurve();

        // アルファ値のキーフレームを追加
        m_animCurve.AddKey( new Keyframe(startTime, 0.0f) );
        m_animCurve.AddKey( new Keyframe(midTime, 1.0f) );
        m_animCurve.AddKey( new Keyframe(endTime, 0.0f) );

        // アニメーションクリップを作成
        m_animClip = new AnimationClip();
        m_animClip.wrapMode = WrapMode.Loop;
        m_animClip.SetCurve("", typeof(Material), "_Color.a", m_animCurve);

        // アニメーションイベントを作成
        m_animEvtStart = new AnimationEvent();
        m_animEvtStart.time = startTime;
        m_animEvtStart.intParameter = 987;
        m_animEvtStart.stringParameter = "start";
        m_animEvtStart.functionName = "AnimEventStart";

        m_animEvtEnd = new AnimationEvent();
        m_animEvtEnd.time = endTime;
        m_animEvtEnd.intParameter = 123;
        m_animEvtEnd.stringParameter = "end";
        m_animEvtEnd.functionName = "AnimEventEnd";

        // クリップにイベントを追加
        m_animClip.AddEvent(m_animEvtStart);
        m_animClip.AddEvent(m_animEvtEnd);

        // アニメーションにクリップを追加
        animation.AddClip(m_animClip, m_animName);
        // アニメーションの再生開始
        animation.Play(m_animName);
    }

    // パラメータ数は 0個か1個 2個だと実行時にエラー
    void AnimEventStart(string s) {
        print("AnimEventStart() " + s);
    }

    void AnimEventEnd(string s) {
        m_loopCount++;
        print("AnimEventEnd() " + s);
    }

    void DrawQuad() {
        m_Mtl.SetPass(0);

        GL.PushMatrix();
        GL.LoadOrtho();
        // 左下が 0, 0, 0
        GL.Begin(GL.QUADS);
        for(int i = 0; i < 4; i++) {
            GL.TexCoord(m_UvArray[i] + m_uvOffset);
            GL.Vertex(m_VtxArray[i]);
        }
        GL.End();
        GL.PopMatrix();
        m_uvOffset.x -= 0.001f;
        m_uvOffset.y -= 0.001f;
    }
}

Unity GL (2) 四角形の描画

TestGL2.cs

using UnityEngine;
 
public class TestGL2 : MonoBehaviour {
    Vector3[]       m_VtxArray;                 // 頂点の配列
    Vector2[]       m_UvArray;                  // UVの配列
    Vector2         m_uvOffset = Vector2.zero;  // UVのオフセット

    public Material m_Mtl;                      // 使用するマテリアル

    public void Start () {
        // 頂点とUVの作成
        m_VtxArray = new Vector3[4];
        m_UvArray = new Vector2[4];

        // 時計回りだと描画される?
        m_VtxArray[0] = new Vector3(0.25f, 0.25f, 0.0f); // BottomLeft
        m_VtxArray[1] = new Vector3(0.25f, 0.95f, 0.0f); // TopLeft
        m_VtxArray[2] = new Vector3(0.75f, 0.75f, 0.0f); // TopRight
        m_VtxArray[3] = new Vector3(0.75f, 0.25f, 0.0f); // BottomRight

        m_UvArray[0] = new Vector2(0.0f, 0.0f);
        m_UvArray[1] = new Vector2(0.0f, 1.0f);
        m_UvArray[2] = new Vector2(1.0f, 1.0f);
        m_UvArray[3] = new Vector2(1.0f, 0.0f);
    }

    void OnRenderObject() {
        DrawQuad();
    }

    void DrawQuad() {
        m_Mtl.SetPass(0);

        GL.PushMatrix();
        GL.LoadOrtho();         // 正射影 左下が 0, 0, 0
        //GL.LoadIdentity();
        //GL.LoadProjectionMatrix(Matrix4x4.identity);
        
        GL.Begin(GL.QUADS);
#if false
        // 反時計回り 描画されない
        for(int i = 3; i >= 0; i--) {
            GL.TexCoord(m_UvArray[i] + m_uvOffset);
            GL.Vertex(m_VtxArray[i]);
        }
#else
        // 時計回り 描画された 
        for(int i = 0; i < 4; i++) {
            GL.TexCoord(m_UvArray[i] + m_uvOffset);
            GL.Vertex(m_VtxArray[i]);
        }
#endif
        GL.End();
        GL.PopMatrix();

        m_uvOffset.x -= 0.001f;
        m_uvOffset.y -= 0.001f;
    }
}

2012年4月12日木曜日

Unity GL (1) ライン描画

GL.LINESでライン描画

空オブジェクトにスクリプトを追加、インスペクター上で、描画するメッシュを選択

TestGL1.cs

using UnityEngine;
 
public class TestGL1 : MonoBehaviour {
    Material        m_Material;                 // マテリアル
    Vector3[]       m_lineVtxArray;             // ライン描画用の頂点の配列
    public Color    m_lineColor = Color.blue;   // 描画色
    public Mesh     m_Mesh;                     // メッシュ

    public void Start () {
        CreateMaterial();
        CreateVtxArray();
    }

    void OnRenderObject() {
        DrawLine();
    }

    // OnPostRender カメラにスクリプトをアタッチすると呼ばれた
    void OnPostRender() {
//        print("OnPostRender");
    }

    // マテリアルを作成
    void CreateMaterial() {
        m_Material = new Material( "Shader \"Lines/Color\" {" +
            "SubShader {" +
            "    Pass { " +
            "       Blend SrcAlpha OneMinusSrcAlpha" +  // αブレンド
//          "       Blend Off" +        // ブレンド オフ
            "       Cull Off" +         // カリング オフ
            "       ZWrite Off" +       // Z値の書き込み オフ
//          "       ZTest Always" +     // Zテスト 常に描画
            "       ZTest Less" +       // Zテスト Z値が小さいと描画
            "       Fog { Mode Off }" + // フォグ オフ
            "       BindChannels {" +
            "           Bind \"Vertex\", vertex" + 
            "           Bind \"Color\", color" +
            "       }" +
            "} } }" );
        m_Material.hideFlags = HideFlags.HideAndDontSave;
        m_Material.shader.hideFlags = HideFlags.HideAndDontSave;
    }

    // メッシュから頂点の配列を作成
    void CreateVtxArray() {
        if(null == m_Mesh) return;

        Vector3[] vertices = m_Mesh.vertices;
        int[] triangles = m_Mesh.triangles;

        print("triangles.Length = " + triangles.Length);
        m_lineVtxArray = new Vector3[triangles.Length];
        for (int i = 0; i < triangles.Length / 3; i++) {
            m_lineVtxArray[i * 3 + 0] = vertices[triangles[i * 3 + 0]];
            m_lineVtxArray[i * 3 + 1] = vertices[triangles[i * 3 + 1]];
            m_lineVtxArray[i * 3 + 2] = vertices[triangles[i * 3 + 2]];
        }
    }

    // 線分の描画
    void DrawLine() {
        Vector3 vPos = Vector3.zero;
        Quaternion qRot = Quaternion.identity;
        Matrix4x4 mtx = Matrix4x4.TRS(vPos, qRot, Vector3.one); 
        
        m_Material.SetPass(0);

        GL.PushMatrix();
        GL.MultMatrix(mtx);
        
        GL.Begin(GL.LINES);
        GL.Color(m_lineColor);

        for (int i = 0; i < m_lineVtxArray.Length / 3; i++) {
            GL.Vertex(m_lineVtxArray[i * 3]);
            GL.Vertex(m_lineVtxArray[i * 3 + 1]);

            GL.Vertex(m_lineVtxArray[i * 3 + 1]);
            GL.Vertex(m_lineVtxArray[i * 3 + 2]);

            GL.Vertex(m_lineVtxArray[i * 3 + 2]);
            GL.Vertex(m_lineVtxArray[i * 3]);
        }
        GL.End();
        GL.PopMatrix();
    }
}

2012年4月7日土曜日

Unity WWW (2)

UnityのWWWで、ローカルホストにアクセスするテスト

サーバーにAIMLを実装したJ-Aliceを使用


ボット同士の会話

TestWWW2.cs

using System;
using System.Collections.Generic;
using System.Collections;
using System.Text;
using UnityEngine;

// テキストフィールドに入力した文字列を、Sendボタンでサーバーに送信
public class TestWWW2 : MonoBehaviour {
    public int  m_nPortNo = 8000;       // ポート番号
    public TestWWW2     m_objTalkTo;    // 話し相手
    
    string      m_InputTxt = "";        // 聞いた内容
    string      m_OutputTxt = "";       // 話した内容
    GameObject  m_objCamera;            // 描画用のカメラ

    public void Start() {
        // カメラを取得
        m_objCamera = GameObject.Find("Main Camera");
    }

    // 話したテキストをセット 話し相手側から呼びだす
    public void SetTaledTxt(string txt) {
        m_InputTxt = txt;
        StartCoroutine( ConnectToLocal( m_InputTxt ) );
    }

    // ローカルサーバーのJ-Aliceに接続
    IEnumerator ConnectToLocal(string inputTxt) {
        // URL引数
        string arg = "?input=" + WWW.EscapeURL(inputTxt);
        // URL
//        string url = "http://localhost:" + m_nPortNo.ToString() + arg;
        string url = "http://127.0.0.1:" + m_nPortNo.ToString() + arg;

        // WWWでURLに接続
        WWW www = new WWW(url);
        yield return www;

        // ShiftJisのエンコーディングを取得
        int codepage = 932;
        Encoding enc = Encoding.GetEncoding( codepage );

        // WWWから文字列を取得
        string str = enc.GetString( www.bytes );

        // J-Aliceからの応答部分の文字列を切り取り 仮
        int nStartIndex = str.IndexOf(":");
        int nEndIndex = str.IndexOf("<p>");
        int nLen = nEndIndex - nStartIndex - 2;
        str = str.Substring(nStartIndex + 2, nLen);

        m_OutputTxt = str;          // 描画するテキストをセット

        // 表示フラグ変更用のウェイト開始
        StartCoroutine( WaitOutputTxt() ); 
    }

    IEnumerator WaitOutputTxt() {
        yield return new WaitForSeconds(3.0f);  // ウェイト

        if(null != m_objTalkTo) {
            // 話相手に話した内容をセット
            m_objTalkTo.SetTaledTxt( m_OutputTxt );
        }
    }

    void OnGUI () {
        // ワールド座標をスクリーン座標に変換
        Vector3 pos = transform.position;
        Vector3 screenPos = m_objCamera.camera.WorldToScreenPoint( pos );

        // 入力文字列編集用のテキストフィールド
        string outTxt = GUI.TextField( new Rect(screenPos.x - 64.0f, 10.0f, 128.0f, 18.0f), m_InputTxt);
        if(0 != outTxt.CompareTo(m_InputTxt)) {
            m_InputTxt = outTxt;
        }

        // 送信ボタン
        if(GUI.Button(new Rect(screenPos.x - 64.0f + 140.0f, 10.0f, 64.0f, 18.0f), "Send")) {
            // コルーチンの開始
            StartCoroutine( ConnectToLocal( m_InputTxt ) );
        }
        // 応答テキストを表示
        GUI.TextArea( new Rect(screenPos.x - 100.0f, 60.0f, 200.0f, 64.0f), m_OutputTxt);
    }
}