おまけ:RICOH THETAのJPG画像を平面-球変換してOpenGLで表示する(その7)
全天球画像をSONY HMZ-TシリーズなどのHMDで楽しみたい場合、前回紹介した汎用IMUを取り付けると上手く動かすことができます。前回紹介したSerialThread.cppとSerialThread.hおよび(その6)で紹介したricohThetaExampleを使って、HMDで見られるように改造します。
SerialThread.hを読み込んで、ricohThetaExampleクラス内でインスタンスを定義します。
ricohThetaExample.h
#include "SerialThread.h"
クラス定義の最後の方に、
// IMU
SerialThread *serial;
floatroll, pitch, yaw;
を追加してください。
シリアルデータの読み込み部分をcppに追加します。setup,update,drawの関数を置き換えてください。視線方向ではなくて、IMUのroll, pitch, yawに応じてモデルを回転させます。HMD酔いを起こしてしまうとき、draw関数内の次の行をコメントアウトしてください。draw関数内のRoll, Pitch, Yawの符号は、センサーの取り付け方向に応じて適当に調整してください。
ofMultMatrix(mRoll); // cancel
ricohThetaExample.cpp
//--------------------------------------------------------------
void ricohThetaExample::setup() {
get_angles(filename.data());
ofSetVerticalSync(true);
// load an image from disk
img.loadImage(filename);
// loop through the image in the x and y axes
float r = 10.0f; // radius
ofEnableDepthTest();
ofEnableNormalizedTexCoords();
glEnable(GL_POINT_SMOOTH); // use circular points instead of square points
glPointSize(3); // make the points bigger
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
cam.setAutoDistance(false);
cam.setPosition(0,0,0);
cam.lookAt(ofVec3f(-r,0,0),ofVec3f(0,0,1));
cam.setDistance(1.0);
roll = pitch = yaw = 0.0f;
serial = new SerialThread();
serial->init();
serial->connect(0, 57600);
serial->startThread(true, false);
}
//--------------------------------------------------------------
void ricohThetaExample::update() {
if(!serial->serial.isInitialized() ){
cam.enableMouseInput();
// keep horizontal
ofVec3f updir = cam.getUpDir();
ofVec3f lookdir = cam.getLookAtDir();
ofVec3f hordir = updir.getCrossed(lookdir);
ofQuaternion quat;
quat.makeRotate(ofVec3f(hordir.x,hordir.y,0).normalized(),hordir.normalized());
ofMatrix4x4 upmat(quat);
ofVec3f new_updir = upmat*updir;
cam.lookAt(lookdir, new_updir);
}
else{
cam.disableMouseInput();
if(serial->is_updated()){
serial->getYPR(yaw, pitch, roll);
}
}
}
//--------------------------------------------------------------
void ricohThetaExample::draw() {
ofBackgroundGradient(ofColor::gray, ofColor::black, OF_GRADIENT_CIRCULAR);
ofQuaternion qRoll, qPitch, qYaw;
qRoll.set(sin(roll/2*M_PI/180.0),0,0,cos(roll/2*M_PI/180.0));
qPitch.set(0,sin(-pitch/2*M_PI/180.0),0,cos(-pitch/2*M_PI/180.0));
qYaw.set(0,0,sin(yaw/2*M_PI/180.0),cos(yaw/2*M_PI/180.0));
ofMatrix4x4 mRoll(qRoll), mPitch(qPitch), mYaw(qYaw);
// keep zenith
ofQuaternion qzx, qzy, qzz;
qzx.set(sin(zenith_x/2*M_PI/180.0),0,0,cos(zenith_x/2*M_PI/180.0));
qzy.set(0,sin(-zenith_y/2*M_PI/180.0),0,cos(-zenith_y/2*M_PI/180.0));
qzz.set(0,0,sin(compass/2*M_PI/180.0),cos(compass/2*M_PI/180.0));
ofMatrix4x4 m44(qzx*qzy);
ofMatrix4x4 m_yaw(qzz);
// even points can overlap with each other, let's avoid that
cam.begin();
ofPushMatrix();
ofMultMatrix(mRoll); // cancel
ofMultMatrix(mPitch);
ofMultMatrix(mYaw);
//ofMultMatrix(m_yaw); // adjust north
ofMultMatrix(m44.getInverse());
ofMatrix4x4 rot(0,-1,0,0,
0,0,-1,0,
1,0,0,0,
0,0,0,1);
ofMultMatrix(rot);
img.bind();
ofSphere(10);
img.unbind();
ofPopMatrix();
cam.end();
}