はじめての「Arduino」「Processing」環境で「3軸加速度センサモジュール」「MqoViewer Library for Processing」を使ってメタセコイアのモデルデータを表示・操作するまでの手順

0. 概要

1. Arduino環境の構築

ハードウェアの準備

 

ソフトウェアの準備

パソコンにArduino UNOを認識させる

サンプルプログラムを書き込み動作を確認する

  1. Arduino UNOをUSBで接続する。
  2. Arduino IDEを起動する。
  3. File → Examples → 1.Basics → Blink を選択する。(=サンプルスケッチを選択)
  4. Tools → Board → Arduino Uno を選択する。(=ボードの指定)
  5. Tools → Serial Port → COM●(←ドライバインストール時に割り当てられたCOMポート番号)を選択する。
  6. (メニューアイコンの右から2個目の)Uploadを押す。
  7. 基板上のLEDが点滅すればOKです。

2. Processing環境の構築

Processingのインストール

3. Arduinoの実装

必要な部品

配線図

Arduinoのスケッチ

//初期化処理
void setup(){
  //シリアル通信の転送速度は9600bps
  Serial.begin(9600);
}

//ループ処理
void loop(){
  
  //Processingからデータを受信するまで待機
  if(Serial.available()>0){
    
    //加速度センサの値(0~1023)を4で割ることにより0~255の範囲で送信する
    Serial.print(analogRead(2)/4,BYTE); //アナログ入力の0番ピン(に入力されたX軸加速度)の値を送信
    Serial.print(analogRead(1)/4,BYTE); //アナログ入力の1番ピン(に入力されたY軸加速度)の値を送信
    Serial.print(analogRead(0)/4,BYTE); //アナログ入力の2番ピン(に入力されたZ軸加速度)の値を送信
    
    //Processingのデータを受信
    Serial.read();
  }
}

メモ

4. Processingの実装

準備

Processingのスケッチ

//----------------------------------------------------------------------------------------------------
// ライブラリのインポート
//----------------------------------------------------------------------------------------------------

//OpenGLライブラリ
import javax.media.opengl.*; 
import processing.opengl.*;
//MqoViewer Library for Processingライブラリ
import jp.nyatla.kGLModel.*;
import jp.nyatla.kGLModel.contentprovider.*;
//シリアル通信ライブラリ
import processing.serial.*;


//----------------------------------------------------------------------------------------------------
// 変数
//----------------------------------------------------------------------------------------------------

//モデルデータ
KGLModelData object_data; //モデル本体
KGLModelData shadow_data; //モデルの影
KGLModelData racket_data; //卓球ラケット
//読み込み用
ContentProvider content_provider_object;
ContentProvider content_provider_shadow;
ContentProvider content_provider_racket;

//シリアル通信変数
Serial myPort;
//フォント変数
PFont myFont;

//カメラ回転角
float cameraThe;
float cameraPhi;
//カメラ座標
float cameraXpos;
float cameraYpos;
float cameraZpos;
//光源の回転角
float lightThe;
float lightPhi;
//光源の座標
float lightXpos;
float lightYpos;
float lightZpos;

int aaa;
int bbb;

//----------------------------------------------------------------------------------------------------
// 初期化処理
//----------------------------------------------------------------------------------------------------
void setup() {
  //ウィンドウの初期化
  size(800, 600, OPENGL);
  
  //モデルデータの読み込み(mqoファイルをフルパス指定(HTTP指定やZIP指定も別メソッドを使えば可能)
  content_provider_object = new LocalContentProvider(this, "C:\\Users\\yuichiro\\Documents\\Processing\\mikukurukuru\\model\\miku.mqo");
  content_provider_shadow = new LocalContentProvider(this, "C:\\Users\\yuichiro\\Documents\\Processing\\mikukurukuru\\model\\shadow.mqo");
  content_provider_racket = new LocalContentProvider(this, "C:\\Users\\yuichiro\\Documents\\Processing\\mikukurukuru\\model\\racket.mqo");
  //OpenGLハンドルの取得
  PGraphicsOpenGL pgl = (PGraphicsOpenGL) g;
  GL gl = pgl.beginGL();
  //モデルデータに記述したOpenGLハンドルを与える(最後部の引数でモデルの座標軸を指定(true:左手系(Processing標準))
  object_data = KGLModelData.createGLModelPs(this, gl,null,this.content_provider_object,0.015f, KGLExtensionCheck.IsExtensionSupported(gl,"GL_ARB_vertex_buffer_object"),true);
  shadow_data = KGLModelData.createGLModelPs(this, gl,null,this.content_provider_shadow,0.015f, KGLExtensionCheck.IsExtensionSupported(gl,"GL_ARB_vertex_buffer_object"),true);
  racket_data = KGLModelData.createGLModelPs(this, gl,null,this.content_provider_racket,0.015f, KGLExtensionCheck.IsExtensionSupported(gl,"GL_ARB_vertex_buffer_object"),true);
  pgl.endGL();
  
  //シリアル通信の開始
  myPort = new Serial(this,Serial.list()[0], 9600);
  
  //フォントデータの読み込み
  myFont = loadFont("CarbonBlock-48.vlw");
  
  //変数の初期化
  cameraThe = 90 * PI/180;
  cameraPhi = 30 * PI/180;
  lightThe = 0;
  lightPhi = 100;
}


//----------------------------------------------------------------------------------------------------
// シリアル通信発生イベント
//----------------------------------------------------------------------------------------------------
void serialEvent(Serial p){
  //Arduino側からの受信が完了するまで待機
  if(myPort.available()>2){
    cameraThe = ((myPort.read() + (90-((1024/2)*(1.65/2.5))/4)) * PI/180); //加速度センサのX軸加速度の値でカメラ回転角シータを変更(0Gで90度になるように調整)
    cameraPhi = ((myPort.read() - (((1024/2)*(1.65/2.5))/4-45)) * PI/180); //加速度センサのY軸加速度の値でカメラ回転角ファイを変更(0Gで45度になるように調整)
    int temp = myPort.read(); //今回は加速度センサのZ軸加速度の値は使わないため受け流す
    myPort.write(100); //受信が完了したことをArduinoに通知する
  }
}

//----------------------------------------------------------------------------------------------------
// マウスが押されたら通信開始
//----------------------------------------------------------------------------------------------------
void mousePressed(){
  //バッファを空に
  myPort.clear();
  //通信開始の合図を送信
  myPort.write(100);
}

//----------------------------------------------------------------------------------------------------
// マウスドラッグイベント(開発時のテスト用)
//----------------------------------------------------------------------------------------------------
//void mouseDragged()
//{
//  cameraThe += (mouseX - pmouseX) * 0.01;
//  cameraPhi += (mouseY - pmouseY) * 0.01;
//}

//----------------------------------------------------------------------------------------------------
// 描写処理
//----------------------------------------------------------------------------------------------------
void draw() {
  
  //背景色
  background(24, 66, 90); //※メタセコイアの背景色と同じ色
  
  //カメラ回転角からカメラ座標を算出し,カメラを設定
  int cameraRadius = 450;
  cameraXpos = cameraRadius * cos(cameraThe) * cos(cameraPhi);
  cameraYpos = cameraRadius * sin(cameraPhi);
  cameraZpos = cameraRadius * sin(cameraThe) * cos(cameraPhi);
  camera(cameraXpos,cameraYpos,cameraZpos, 0,200,0, 0,-1,0); //視点,注視点,天井方向の定義
  
  //カメラ座標表示ボードの描写
  fill(0,0,0); //背景色
  stroke(255,255,255); //枠色
  strokeWeight(4); //枠の太さ
  rect(-290,100,200,100); //描写
  noStroke();
  
  //テキスト(カメラ座標,モデル達の注釈)の描写
  translate(0,0,1); //1ピクセル前面にずらした位置に描写
  rotateZ(PI); //座標系がずれるので調整
  fill(255,255,255); //文字色
  textAlign(LEFT); //左揃え
  textFont(myFont, 28); //フォントサイズ28
  text("cameraXpos: ", 100, -170, 0);
  text("cameraYpos: ", 100, -140, 0);
  text("cameraZpos: ", 100, -110, 0);
  textFont(myFont, 32);
  text("Miku Hatsune", -200, -325, 0);
  textFont(myFont, 12);
  text("(C) innoce.nobody.jp", -200, -310, 0);
  textFont(myFont, 16);
  text("Penholder L8efty", 145, -293, 15);
  textFont(myFont, 8);
  text("(C) www003.upp.so-net.ne.jp/kakomiki", 145, -283, 15);
  textAlign(RIGHT); //右揃え
  textFont(myFont, 28);
  text((int)cameraXpos, 280, -170, 0);
  text((int)cameraYpos, 280, -140, 0);
  text((int)cameraZpos, 280, -110, 0);
  rotateZ(-PI); //ずらした座標系を元に戻す
  translate(0,0,-1); //ずらした座標を元に戻す
  
  //モデル達の注釈矢印の描写
  stroke(255,255,255); //線の色
  strokeWeight(2); //線の太さ
  line(50,320,0, 205,320,0);
  line(30,300,0, 50,320,0);
  translate(30,300,0);
  sphere(2); //矢印先端の球
  translate(-30,-300,0);
  
  stroke(255,255,255); //線の色
  strokeWeight(2); //線の太さ
  line(-140,290,15, -260,290,15);
  line(-125,270,15, -140,290,15);
  translate(-125,270,15);
  sphere(2); //矢印先端の球
  translate(125,-270,-15);

  
  //OpenGLハンドルの取得
  PGraphicsOpenGL pgl = (PGraphicsOpenGL) g;
  GL gl = pgl.beginGL();
  
  //gl.glTexEnvf(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE, GL.GL_MODULATE); //テクスチャ設定
  //gl.glEnable(GL.GL_CULL_FACE);
  //gl.glCullFace(GL.GL_FRONT);

  //フォグ設定
  float fog[]={0.022f*5, 0.066f*5, 0.090f*5, 0.5f};
  //float fog[]={0.022f*5, 0.066f*5, 0.090f*5, 0.5f};
  gl.glFogfv(GL.GL_FOG_COLOR,fog,0);
  gl.glFogi(GL.GL_FOG_MODE,GL.GL_LINEAR);
  gl.glFogf(GL.GL_FOG_DENSITY,0.003f);
  gl.glFogf(GL.GL_FOG_START,500f);
  gl.glFogf(GL.GL_FOG_END,1000f);
  gl.glEnable(GL.GL_FOG);
  
  //--------------------------------------------------
  // フィールドの描写
  //--------------------------------------------------
  gl.glPushMatrix();
  
  //グリッド(床と重ならないように2ピクセル浮かして描写)
  gl.glLineWidth(1f);
  gl.glEnable(GL.GL_LINE_STIPPLE); //破線モードを有効に
  gl.glLineStipple(1,(short)0xF0F0); //破線パターンの設定
  gl.glBegin(GL.GL_LINES);
  gl.glColor4f(1f,1f,1f,0.2f); //色
  int gridNumber = 20;
  int gridInterval = 50;
  int gridArea = gridNumber * gridInterval;
  for(int i=-gridNumber;i<gridNumber;i++){
    gl.glVertex3f(-gridArea, 2 ,i*gridInterval);
    gl.glVertex3f(+gridArea, 2 ,i*gridInterval);
    gl.glVertex3f(i*gridInterval, 2 ,-gridArea);
    gl.glVertex3f(i*gridInterval, 2 ,+gridArea);
  }
  gl.glEnd();
  gl.glDisable(GL.GL_LINE_STIPPLE); //破線モードを無効に
  
  //床
  gl.glEnable(GL.GL_CULL_FACE);//裏面のみを描くように変更
  gl.glCullFace(GL.GL_FRONT);
  gl.glBegin(GL.GL_QUADS);
  gl.glColor4f(1f,1f,1f,0.1f); //色
  //gl.glColor4f(1 , 1 , 0, 1);
  gl.glVertex3f(+gridArea, 0, +gridArea);
  //gl.glColor4f(1 , 0 , 0, 1);
  gl.glVertex3f(-gridArea, 0, +gridArea);
  //gl.glColor4f(0 , 1 , 0, 1);
  gl.glVertex3f(-gridArea, 0, -gridArea);
  //gl.glColor4f(0 , 0 , 1, 1);
  gl.glVertex3f(+gridArea, 0, -gridArea);
  gl.glEnd();
  gl.glDisable(GL.GL_CULL_FACE); //両面を描くように元に戻す
 
  gl.glPopMatrix();
  //--------------------------------------------------
  // モデル本体の描写
  //--------------------------------------------------
  gl.glPushMatrix();

  gl.glRotatef(180, 0, 1, 0); //座標系の関係でモデルが後ろを向くので回転
  gl.glTranslatef(0, 273, 0); //地面に立つように移動
  //裏面のみを描くように変更
  gl.glEnable(GL.GL_CULL_FACE);
  gl.glCullFace(GL.GL_FRONT);
  
  //モデルデータ
  object_data.enables(30.0f); //スケール
  object_data.draw(); //描写
  object_data.disables(); //破棄
  gl.glDisable(GL.GL_CULL_FACE); //両面を描くように元に戻す
 
  gl.glPopMatrix();
  //--------------------------------------------------
  // モデルの影の描写
  //--------------------------------------------------
  gl.glPushMatrix();
  
  //影行列
  float matrix[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
  
  //カメラ回転角からカメラ座標を算出し,カメラを設定
  int lightRadius = 500;
  lightXpos = lightRadius * cos(lightThe) * cos(lightPhi);
  lightYpos = lightRadius * sin(lightPhi);
  lightZpos = lightRadius * sin(lightThe) * cos(lightPhi);
  
  //影行列の計算
  matrix[0]  = 1;
  matrix[5]  = 0;
  matrix[4]  = -lightXpos/lightYpos;
  matrix[6]  = -lightZpos/lightYpos;
  matrix[10] = 1;
  matrix[15] = 1;

  gl.glRotatef(180, 0, 1, 0); //座標系の関係でモデルが後ろを向くので回転
  gl.glTranslatef(0, 1, 0); //1ピクセル浮かした位置に描写
  
  gl.glMultMatrixf(matrix, 0); //影行列を適用
  gl.glTranslatef(0, 273, 0); //本体の足元から伸びるように移動
  
  //モデルデータ
  shadow_data.enables(30.0f); //スケール
  shadow_data.draw(); //描写
  shadow_data.disables() ; //破棄
  
  gl.glPopMatrix();
  //--------------------------------------------------
  // 卓球ラケットの描写
  //--------------------------------------------------
  gl.glPushMatrix();

  gl.glRotatef(180, 0, 1, 1); //回転
  gl.glTranslatef(125, 0, 260); //移動
  racket_data.enables(140.0f); //スケール
  racket_data.draw(); //描写
  racket_data.disables(); //破棄

  gl.glPopMatrix();


  //--------------------------------------------------
  // 描写終了
  //--------------------------------------------------
  pgl.endGL();
  
  //--------------------------------------------------
  // 変数の更新
  //--------------------------------------------------
  
  lightThe += 0.005; //光源の移動
}

5. 動作確認

の動画を載せようかと思ったのですがそれほど面白い動きではなかったので割愛です…。

6. おまけ(3Dプログラミング技術メモ)

カメラの回転

影行列

7. 参考サイト

Copyright c 2006- "ysdu16.com" Some right reserved.