openFrameworksで3Dモデルを扱う~ofxAssimpleModelLoader編~

##はじめに

この記事はopenFrameworksで3dモデルを扱いたいんだけどどうすればいいの？
openFrameworks初めて触って、3dモデルを扱いたいのですが！
みたいな方に向けて書いてる記事です。(最近そういう質問が多かったので記事にしました / OSX向けです)
[ofxAssimpleModelLoader](http://openframeworks.cc/documentation/ofxAssimpModelLoader/ofxAssimpModelLoader/)という3Dモデルを扱えるAddonがありますがaddonにsampleプロジェクトがないので今回作りました。

##下準備
openFrameworksをダウンロードすると
http://openframeworks.cc/download/

標準でaddonsディレクトリに
![17555002_1317846104964234_684798948_n.png](https://qiita-image-store.s3.amazonaws.com/0/56510/86cdf885-7144-b9dc-2750-9b7d6cae0906.png)

ofxAssimpleModelLoaderという、3dモデルを扱うAddonが含まれています。

ProjectGeneratorから

![Screen Shot 2017-03-29 at 17.59.48.png](https://qiita-image-store.s3.amazonaws.com/0/56510/3b9247b2-6214-1423-2541-b16d3d6e5316.png)

AddonにofxAssimpleModelLoaderを追加して、Generateをします。


作成した、プロジェクトの`bin/data`ディレクトリに適当な3Dのモデルを配置します。

![Screen Shot 2017-03-29 at 21.10.53.png](https://qiita-image-store.s3.amazonaws.com/0/56510/4d1d7495-e493-2fe2-deb7-644b70a4378e.png)

この時モデルファイルの拡張子は

> 3DS, ASE. DXF, HMP, MD2, MD3, MD5, MDC, MDL, NFF, PLY
, STL, X, LWO, OBJ, SMD, Collada, LWO, Ogre XML, partly LWS

のいずれかである必要があります。

##実装

ヘッダファイルで`ofxAssimpModelLoader.h`をincludeします。
その後に、ofxAssimpModelLoaderの変数、ofCameraの変数を宣言します。

```ofApp.h
#pragma once

#include "ofMain.h"
#include "ofxAssimpModelLoader.h"
class ofApp : public ofBaseApp{

	public:
		void setup();
		void update();
		void draw();

		void keyPressed(int key);
		void keyReleased(int key);
		void mouseMoved(int x, int y );
		void mouseDragged(int x, int y, int button);
		void mousePressed(int x, int y, int button);
		void mouseReleased(int x, int y, int button);
		void mouseEntered(int x, int y);
		void mouseExited(int x, int y);
		void windowResized(int w, int h);
		void dragEvent(ofDragInfo dragInfo);
		void gotMessage(ofMessage msg);

    ofCamera camera;
    ofxAssimpModelLoader mAssimpModelLoader;
    string model_path;
};
```

次に実装ファイルに関してです。

```ofApp.cpp
//--------------------------------------------------------------
void ofApp::setup(){
    model_path = "model_sample.stl";
    mAssimpModelLoader.setScale(0.5, 0.5, 0.5);
    mAssimpModelLoader.loadModel(model_path);
}

//--------------------------------------------------------------
void ofApp::update(){
    mAssimpModelLoader.update();
    camera.lookAt(ofVec3f(0,0,0));
    camera.setPosition(500*cos(ofGetElapsedTimef()/10), 200, 500*sin(ofGetElapsedTimef()/10));
}

//--------------------------------------------------------------
void ofApp::draw(){
    ofBackground(0);
    ofPushMatrix();
    ofTranslate(ofGetWidth()/2,ofGetHeight()/2);
    camera.begin();
    ofRotateX(-90);
    ofSetLineWidth(1);
    mAssimpModelLoader.drawWireframe();  //モデルを描画
    camera.end();
    ofPopMatrix();
}
```

構成は単純で、`setup()`でモデルの大きさ(scale)をどうするかを設定
また、モデル自体のロードを行います

`update()`はofVideoPlayerなど同様にAssimpModelLoaderのインスタンスのupdate()を呼びます。
今回はサンプルとして3Dモデルの周りをカメラが回ってる感じを出すために、その更新も書いています。

`draw()`は位置や座標系を調整して、`mAssimpModelLoader.drawWireframe()`でモデルをwirefranmeとして描画しています。

##結果

![gif](https://media.giphy.com/media/xUA7b6HAP8Y9SQsko0/giphy.gif)

このような感じで3Dモデルが、描画されます

##発展

3Dモデルを面を描画する方法について、以下のように、実装ファイルを変更します。

```ofApp.cpp
//--------------------------------------------------------------
void ofApp::setup(){
    model_path = "model_sample.stl";
    mAssimpModelLoader.setScale(0.5, 0.5, 0.5);
    mAssimpModelLoader.loadModel(model_path);
}

//--------------------------------------------------------------
void ofApp::update(){
    mAssimpModelLoader.update();
    camera.lookAt(ofVec3f(0,0,0));
    camera.setPosition(500*cos(ofGetElapsedTimef()/10), 200, 500*sin(ofGetElapsedTimef()/10));
}

//--------------------------------------------------------------
void ofApp::draw(){
    ofBackground(0);
    ofSetColor(255,30);
    ofPushMatrix();
    ofTranslate(ofGetWidth()/2,ofGetHeight()/2);
    camera.begin();
    ofRotateX(-90);
    ofSetLineWidth(1);
    mAssimpModelLoader.drawVertices(); //モデルの頂点を描画
    mAssimpModelLoader.drawFaces();  //モデルの面を描画
    camera.end();
    ofPopMatrix();
}
```

このまま実行すると以下のようになります。
![gif](https://media.giphy.com/media/xUA7aMW3cxiIkSb6Te/giphy.gif)

`drawFace()`を用いると面が描画され、`drawVertices()`で頂点が描画されています。

次の例では、`drawWireframe()`で先ほど描画した3Dモデルの辺を点線として描画する方法を紹介します。
辺を点線として扱うには

`glEnable();`と｀glLinStipple();`を使います。後者の関数に関しては、こちらに詳細がまとまっており参考にさせていただきました。

http://wisdom.sakura.ne.jp/system/opengl/gl6.html

```ofApp.cpp
#include "ofApp.h"

//--------------------------------------------------------------
void ofApp::setup(){
    model_path = "model_sample.stl";
    mAssimpModelLoader.setScale(0.5, 0.5, 0.5);
    mAssimpModelLoader.loadModel(model_path);
}

//--------------------------------------------------------------
void ofApp::update(){
    mAssimpModelLoader.update();
    camera.lookAt(ofVec3f(0,0,0));
    camera.setPosition(500*cos(ofGetElapsedTimef()/10), 200, 500*sin(ofGetElapsedTimef()/10));
}

//--------------------------------------------------------------
void ofApp::draw(){
    ofSetColor(255,30);
    ofBackground(0);
    ofPushMatrix();
    ofTranslate(ofGetWidth()/2,ofGetHeight()/2);
    camera.begin();
    ofRotateX(-90);
    ofSetLineWidth(1);
    glEnable(GL_LINE_STIPPLE);
    glLineStipple(0.1 , 0x0101);
    mAssimpModelLoader.drawWireframe();  //モデルを描画
    glDisable(GL_LINE_STIPPLE);
    camera.end();
    ofPopMatrix();
}
```

結果は以下の通りです

![gif](https://media.giphy.com/media/l0Iykb3SIRupeHmKs/giphy.gif)

また、随時更新していきます。