2012年4月30日月曜日

Bullet 衝突回数のカウント

衝突回数をカウントして、剛体を削除するテスト bullet-2.79

main.cpp

#include <crtdbg.h>
#include <btBulletCollisionCommon.h>
#include <btBulletDynamicsCommon.h>

#ifdef _DEBUG
#pragma comment(lib, "BulletCollision_debug.lib")
#pragma comment(lib, "BulletDynamics_debug.lib")
#pragma comment(lib, "LinearMath_debug.lib")
#else
#pragma comment(lib, "BulletCollision.lib")
#pragma comment(lib, "BulletDynamics.lib")
#pragma comment(lib, "LinearMath.lib")
#endif

// ↓コンタクトのコールバック
extern ContactProcessedCallback gContactProcessedCallback;

struct TestData {
  int count; // 衝突回数
  TestData() : count(0) {}
};

class TestBullet {
  btDiscreteDynamicsWorld*        m_pWorld;
  btVector3 m_vWorldSize;

    btDefaultCollisionConfiguration     m_config;
    btCollisionDispatcher         m_dispatcher;
    btAxisSweep3              m_broadphase;
    btSequentialImpulseConstraintSolver   m_solver;
  btAlignedObjectArray<btCollisionShape*> m_collisionShapes;

  btRigidBody*  m_Body1;
  TestData    m_BodyData1;
public:
  TestBullet() :
    m_dispatcher(&m_config),
    m_vWorldSize(1000.0f, 1000.0f, 1000.0f),
    m_broadphase(m_vWorldSize * -0.5f, m_vWorldSize * 0.5f, 1024),
    m_pWorld(0), m_Body1(0)
  {}
  ~TestBullet();

  void Init();
  void Update();
  void DeleteBody(btRigidBody** pBody);
  static bool HandleContactProcess(btManifoldPoint& p, void* a, void* b);
};

TestBullet::~TestBullet() {
  for (int i = m_pWorld->getNumCollisionObjects() - 1; i >= 0 ; i--) {
    btCollisionObject* obj = m_pWorld->getCollisionObjectArray()[i];
    btRigidBody* body = btRigidBody::upcast(obj);
    DeleteBody(&body);
  }
  for (int j = 0; j < m_collisionShapes.size(); j++) {
    btCollisionShape* shape = m_collisionShapes[j];
    m_collisionShapes[j] = 0;
    delete shape;
  }
  delete m_pWorld;
}

void TestBullet::Init() {
  m_pWorld = new btDiscreteDynamicsWorld(&m_dispatcher, &m_broadphase, &m_solver, &m_config);
  m_pWorld->setGravity(btVector3(0.0f, -9.8f * 1.0f, 0.0f));
  m_pWorld->getSolverInfo().m_numIterations = 2;

  // 地面の形状
  btCollisionShape* groundShape = new btBoxShape(btVector3(btScalar(50.),btScalar(50.),btScalar(50.)));
  m_collisionShapes.push_back(groundShape);

  btTransform groundTransform;
  groundTransform.setIdentity();
  groundTransform.setOrigin(btVector3(0.0f, -50.0f, 0.0f));

  // 地面の作成
  btScalar mass(0.0f);
  bool isDynamic = (mass != 0.f);

  btVector3 localInertia(0,0,0);
  if (isDynamic) groundShape->calculateLocalInertia(mass, localInertia);

  btDefaultMotionState* myMotionState = new btDefaultMotionState( groundTransform );
  btRigidBody::btRigidBodyConstructionInfo rbInfo0(mass, myMotionState, groundShape, localInertia);

  btRigidBody* pGroundBody = new btRigidBody(rbInfo0);
  m_pWorld->addRigidBody(pGroundBody);


  // 削除テスト用の剛体の作成
  btCollisionShape* colShape = new btSphereShape( 2.0f );
  m_collisionShapes.push_back(colShape);

  btTransform startTransform;
  startTransform.setIdentity();

  mass  = 100.0f;
  isDynamic = (mass != 0.f);
  if (isDynamic)  colShape->calculateLocalInertia(mass, localInertia);

  startTransform.setOrigin(btVector3(2, 5, 0));

  myMotionState = new btDefaultMotionState(startTransform);
  btRigidBody::btRigidBodyConstructionInfo rbInfo1(mass, myMotionState, colShape, localInertia);

  m_Body1 = new btRigidBody(rbInfo1);
  m_Body1->setUserPointer(&m_BodyData1);  // ユーザーデータをセット
  m_pWorld->addRigidBody(m_Body1);
}

void TestBullet::Update() {
  const btScalar dt = 1.0f / 30.0f;
  m_pWorld->stepSimulation(dt);

  btTransform trans;
  for (int i = m_pWorld->getNumCollisionObjects() - 1; i >= 0; i--) {
    btCollisionObject* obj = m_pWorld->getCollisionObjectArray()[i];
    btRigidBody* body = btRigidBody::upcast(obj);

    if(body && !body->isStaticObject()) {
      body->getMotionState()->getWorldTransform(trans);
      btVector3& pos = trans.getOrigin();

      printf("%f, %f, %f\n", pos.getX(), pos.getY(), pos.getZ());
    }
  }
  if(m_Body1 != NULL && m_BodyData1.count > 6) {
    DeleteBody(&m_Body1); // 削除テスト
  }
}

void TestBullet::DeleteBody(btRigidBody** ppBody) {
  btRigidBody* pBody = *ppBody;
  m_pWorld->removeRigidBody( pBody );
  btMotionState* pMotionState = pBody->getMotionState();
  if(pMotionState) {
    delete pMotionState;
  }
  delete pBody;
  *ppBody = NULL;
}

bool TestBullet::HandleContactProcess(btManifoldPoint& p, void* a, void* b) {
  btRigidBody* pBody0 = (btRigidBody*)a;
  btRigidBody* pBody1 = (btRigidBody*)b;

  TestData* pUserData0 = (TestData*)pBody0->getUserPointer();
  TestData* pUserData1 = (TestData*)pBody1->getUserPointer();

  // カウント
  if(pUserData0) pUserData0->count++;
  if(pUserData1) pUserData1->count++;
  return true;
}

int main() {
#if defined(DEBUG) | defined(_DEBUG)
    _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
#endif
  TestBullet test;
  test.Init();
  // 衝突のコールバック関数をセット
  gContactProcessedCallback = TestBullet::HandleContactProcess;

  for (int i = 0; i < 100; i++) {
    test.Update();
  }
  return 0;
}

2012年4月29日日曜日

dcollide Kdopのテスト

K-DOP (K discrete oriented polytope)

main.cpp

#define _USE_MATH_DEFINES
#include <d-collide/dcollide.h>
#include <d-collide/proxyfactory.h>
#include <d-collide/math/vector.h>
#include <d-collide/shapes/mesh/meshfactory.h>

int main()
{
  // ワールド
  dcollide::World world;

  // プロキシファクトリを取得
  dcollide::ProxyFactory* pPf = world.getProxyFactory();

  // プロキシを作成
  dcollide::Proxy* pProxy = pPf->createProxy();

  // メッシュの頂点
  dcollide::Vertex vtx_array[] = {
    dcollide::Vertex(0, 10.18f, 0),
    dcollide::Vertex(-10, 0, -10),
    dcollide::Vertex( 10, 0, -10),
    dcollide::Vertex(-10, 0,  10),
    dcollide::Vertex( 10, 0,  10),
  };

  std::vector vertices;
  vertices.push_back( &vtx_array[0] );
  vertices.push_back( &vtx_array[1] );
  vertices.push_back( &vtx_array[2] );
  vertices.push_back( &vtx_array[3] );
  vertices.push_back( &vtx_array[4] );

  // メッシュのインデックス
  std::vector indices;
  indices.push_back(1); indices.push_back(2); indices.push_back(0);
  indices.push_back(3); indices.push_back(4); indices.push_back(0);

  // メッシュ
  dcollide::Mesh mesh(vertices, indices);
  mesh.setProxy( pProxy );

  // メッシュファクトリ
  dcollide::MeshFactory mf;

  // メッシュファクトリから球のメッシュを作成
  dcollide::Mesh* pSphereMesh = mf.createSphere(4.56f, 5.0f);
  pSphereMesh->setProxy( pProxy );

  // kdopのk
  int k = 6;    // 6:AABB 14, 18, 26
  // kdop
  dcollide::Kdop kdop(k);
  kdop.adjustToShape(&mesh);    // プロキシが無いとerror
//  kdop.adjustToShape(pSphereMesh);

  // AABB
  dcollide::Vector3 vMin =  kdop.getSurroundingAabbMin();
  dcollide::Vector3 vMax = kdop.getSurroundingAabbMax();
 
  printf("k = %d\n", k);
  for(int i = 0; i < k; ++i) 
  {
    // 原点から平面までの距離
    dcollide::real dist = kdop.getDistanceOfPlaneToOrigin(i);
    // 平面の法線ベクトル
    dcollide::Vector3 vN = kdop.getPlaneNormal(i);
    printf("%d [%f %f %f %f]\n", i, vN.getX(), vN.getY(), vN.getZ(), dist);
  }
  return 0;
}

2012年4月28日土曜日

MAXScript テキストファイルに書き出す

シーン内で使用しているテクスチャ画像の絶対パスをファイルに書き出す テスト

WriteDiffuseTexFilePath.ms

-- マテリアルのDiffuseテクスチャ画像の絶対パスをファイルに出力
fn WriteMtlInfo fs mtl = (
  d = mtl.DiffuseMap
  if undefined == d do return -2
  
  bm = d.bitmap
  if undefined == bm do return -3
  
  filePath = d.fileName
  
  -- ファイルの絶対パスを取得
  FileResolutionManager.getFullFilePath &filePath #Bitmap

  -- ¥ を /に変換
  strArray = filterString filePath "¥¥"
  filePath = ""
  for i = 1 to strArray.count do (
    filePath += strArray[i]
    if i < strArray.count do (
      filePath += "/"
    )
  )
  -- 画像の幅、高さ
  w = bm.width
  h = bm.height
  format "%¥n" filePath to:fs -- 絶対パスをファイルに出力
)

-- シーン内のマテリアル情報の取得
fn WriteDiffuseTexFilePath fileName = (
  if 0 == sceneMaterials.count do (
    format "sceneMaterials.count is 0¥n"
    return -1
  )

  -- ファイル出力先のディレクトリ
  fPath =  GetDir #export
  fPath += "¥¥" + fileName

  -- ファイルオープン
  fs = openFile fPath mode:"wt"
  if undefined  == fs do return -1
  
  for i = 1 to sceneMaterials.count do (
    mtl = sceneMaterials[ i ]   -- マテリアルを取得
    c = classof mtl     -- マテリアルのクラス名を取得

    if c == Standardmaterial do (
      WriteMtlInfo fs mtl
    )
    if c == Multimaterial do (
      nSubMtl = getNumSubMtls mtl -- マテリアルのサブマテリアル数を取得
      for j = 1 to nSubMtl do (
        subMtl = getSubMtl mtl j
        WriteMtlInfo fs subMtl
      )
    )
  )
  -- ファイルクローズ
  close fs  
)
-- exportフォルダにTexFilePath.txtが作成される
WriteDiffuseTexFilePath "TexFilePath.txt"

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);
    }
}

2012年4月1日日曜日

Unity ギズモの描画と編集

空のゲームオブジェクトにTestGizmo.csを追加

ギズモを選択、編集タイプに応じて移動、サイズ変更がシーンウィンドウで行える

Assets/Script/TestGizmo.cs

using UnityEngine;

// ギズモのテスト用クラス
public class TestGizmo : MonoBehaviour {
  // 編集タイプの種類
  public enum EditType {
    Invalid,
    BoundCenter,
    BoundSize
  }

  // 編集タイプ
  public EditType editType = EditType.Invalid;

  // ギズモの形状
  public Bounds   bound = new Bounds(Vector3.zero, Vector3.one);

  // ギズモの描画
  void OnDrawGizmos () {
    Gizmos.color = Color.magenta;
    Gizmos.DrawWireCube(bound.center, bound.size);
  }
}

Assets/Editor/EditorTestGizmo.cs

using UnityEngine;
using UnityEditor;

// TestGizmoの編集用クラス
[CustomEditor( typeof(TestGizmo) )]     // ←編集対象のクラスを指定
public class EditorTestGizmo : Editor {

  // Editor.OnSceneGUI シーンウィンドウ上のGUI処理
  void OnSceneGUI () {
    // 編集対象を取得
    TestGizmo t = target as TestGizmo;

      switch(t.editType) {
      case TestGizmo.EditType.BoundCenter:
          // 位置の変更
          t.bound.center = Handles.PositionHandle(t.bound.center, 
                                      Quaternion.identity);
          break;
      case TestGizmo.EditType.BoundSize:
          // サイズの変更
          t.bound.size = Handles.ScaleHandle(t.bound.size, 
                           t.bound.center, Quaternion.identity, 5.0f);
          break;
      }
      if (GUI.changed) {
//          Debug.Log("GUI.changed");
      }
  }
}