cvl-robot's diary

研究ノート メモメモ https://github.com/dotchang/

3DSMaxやMAYAの操作系を真似て作られたLibGizmoがよくて、それをopenFrameworksに持ってきたofxManipulatorが3次元操作のためにとても使いやすい。

proceduralさんのopenFramworks addonのofxManipulatorがすごく便利です。github.com

元々は、LibGizmoというフランスのskavenさんが作ったライブラリの一部なのだそうですが、
これも元々3dsmaxやmayaの操作系に触発されて作られたものです。
LibGizmo | Skaven's BBQ

どんな物かというと、
3次元オブジェクトの3次元空間中での

  • 拡大縮小、
  • 回転、
  • 並進移動

がマウス一つで簡単に操作できるようになるライブラリです。
openGLのカメラに基づいてピックが計算されるので、直感的に操作できます。

f:id:cvl-robot:20150917115543g:plain

作者のExampleでは、ofxFirstPersonCameraなる独自のopenGLカメラを使っていますが、
ofEasyCamのイベントを渡すようにちょっとだけ改造してやれば、こちらでも問題なく使えるようになります。

また、Manipulatorが操作されたことを通知するEventを発行してやるようにすると、とても綺麗にプログラムが書けるようになります。

ofxManipulator.hのclass ofxManipulatorに追加。

public:
	ofEvent<ofVec3f> onScaleChange;
	ofEvent<ofVec3f> onTranslateChange;
	ofEvent<ofQuaternion> onRotateChange;

ofxManipulator.cppのvoid ofxManipulator::mouseDragged(ofMouseEventArgs &mouse)のswitch文のそれぞれの最後にそれぞれ追加。

case (MANIPULATOR_SCALE):{
…
ofNotifyEvent(onScaleChange, m_scale);
}break;
case (MANIPULATOR_ROTATION):{
…
ofNotifyEvent(onRotateChange, m_rotation);
}break;
case (MANIPULATOR_TRANSLATION):{
…
ofNotifyEvent(onTranslateChange, m_translation);
}break;

使い方の例を適当に抜粋すると、

void myApp::setup(){
ofxManipulator *m_gizmo = new ofxManipulator();
ofAddListener(m_gizmo->onTranslateChange, this, &myApp::onTranslateChange);
}

void myApp::onTranslateChange(ofVec3f& t)
{
…
}

のように、です。

ofEasyCamに対応するときの、変更箇所のメモ(を忘れたので、適当に羅列)
作者のページにはcmakeを使えと書いてありますが、普通にprojectGeneratorで大丈夫。
ofApp.cppのvoid ofApp::draw()内

//m_model.getMesh("").drawFaces();
	for (int i = 0; i < m_model.getMeshCount(); i++) {
		m_model.getMesh(i).drawFaces();
	}

ofApp.cppのvoid ofApp::mousePressed(ofMouseEventArgs &mouse)内

	//case (OF_MOUSE_BUTTON_RIGHT) : m_cam.toggleControl(); break;
	case (OF_MOUSE_BUTTON_RIGHT) : {
		if(m_cam.getMouseInputEnabled()) m_cam.disableMouseInput();
		else m_cam.enableMouseInput();
	}

VisualStudio2015のof0.9RCではofRemoveListenerでセグメンテーションフォルトを起こす致命的なバグがあります。
ofEasyCamのofRemoveListenerをコメントアウトして、フラグでイベントを拾う拾わないを切り替えるなど場当たり的な解決が(今は)必要です。

さらにマウスだけで操作できるようにFocus機能を付けて便利にする

マウスカーソルが近づいたら勝手にマニピュレータが表示されて、遠くに外れたら勝手にマニピュレータが消される仕組みができたら操作が楽になりますね。
追加してみましょう。

class ofxManipulator
{
...省略
public:
	bool m_focus;
	bool m_auto_focus;
	MANIPULATOR_TYPE m_lastManipulator;
	bool getAutoFocus() { return m_auto_focus; }
	void setAutoFocus(bool on) { m_auto_focus = on; }

	float m_id;
	float getID() { return m_id; }
	void setID(float v) { m_id = v; }

	float m_onFocusThreshold;
	float getOnFocusThreshold() { return m_onFocusThreshold; }
	void setOnFocusThreshold(float t) { m_onFocusThreshold = t; }

	float m_offFocusThreshold;
	float getOffFocusThreshold() { return m_offFocusThreshold; }
	void setOffFocusThreshold(float t) { m_offFocusThreshold = t; }

	ofEvent<ofVec3f> onFocus, outFocus;
	ofEvent<ofVec3f> onScaleChange;
	ofEvent<ofVec3f> onTranslateChange;
	ofEvent<ofQuaternion> onRotateChange;
};

ofxManipulator.cpp

ofxManipulator::ofxManipulator()
	:m_currScale(SCALE_NONE)
	, m_currScalePredict(SCALE_NONE)
	, m_currRotation(ROTATION_NONE)
	, m_currRotationPredict(ROTATION_NONE)
	, m_currTranslation(TRANSLATION_NONE)
	, m_currTranslationPredict(TRANSLATION_NONE)
	, m_currManipulator(MANIPULATOR_NONE)
	, m_x_color(0xff, 0x41, 0x36)
	, m_y_color(0x2e, 0xcc, 0x40)
	, m_z_color(0x00, 0x74, 0xd9)
	, m_w_color(0xff, 0x4b, 0xff)
	, m_select_color(0xff, 0xdc, 0x00)
	, m_scale(1.0f, 1.0f, 1.0f)
	, m_manipulatorScale(1.0f)
	, m_screenFactor(0.0f)
	, m_angleRad(0.0f)
	, m_onFocusThreshold(100.0f) // added
	, m_offFocusThreshold(200.0f) // added
	, m_focus(false) // added
	, m_auto_focus(true) // added
	, m_id(0) // added
	, m_lastManipulator(MANIPULATOR_TRANSLATION) // added
{
...省略

void ofxManipulator::mouseMoved(ofMouseEventArgs &mouse)
{
	int x = mouse.x;
	int y = mouse.y;

	// Focus
	ofCamera cam;
	cam.setTransformMatrix(m_viewInverse);
	ofVec3f cur = cam.worldToScreen(m_translation);
	float dist = cur.distance(mouse);
	cur.x = m_id;
	cur.y = dist;
	// cur.z = cur.z;

	// Change to onFocus
	if (dist < m_onFocusThreshold * m_manipulatorScale) {
		m_focus = true;
		if (m_auto_focus) {
			if (m_currManipulator == MANIPULATOR_NONE) {
				m_currManipulator = m_lastManipulator;
			}
		}
	}
	// OnFocus process
	if (dist < m_offFocusThreshold * m_manipulatorScale) {
		if (m_focus) {
			ofNotifyEvent(onFocus, cur);
		}
	}
	// Change to outFocus
	else {
		if (m_focus) {
			ofNotifyEvent(outFocus, cur);
			m_focus = false;
			if (m_auto_focus) {
				m_currManipulator = MANIPULATOR_NONE;
			}
		}
	}
	if (m_currManipulator != MANIPULATOR_NONE) {
		m_lastManipulator = m_currManipulator;
	}

	switch (m_currManipulator)
	{
	case (MANIPULATOR_SCALE) :
		getCurrScale(m_currScalePredict, x, y);
		break;
	case (MANIPULATOR_ROTATION) :
		getCurrRotation(m_currRotationPredict, x, y);
		break;
	case (MANIPULATOR_TRANSLATION) :
		getCurrTranslation(m_currTranslationPredict, x, y);
		break;
	}
}

複数のマニピュレータを同時に扱うときは、notifyで送られてくるcurを自分で調べてどれを操作対象とするか自分で決める必要があります。
curは、xがid, yが距離, zが正規化された深さ情報です。

Autodesk AutoCAD LT 2016 Commercial New SLM

Autodesk AutoCAD LT 2016 Commercial New SLM

AutodeskもAdobeも使いにくいのはなんでだろう・・・?