Skip to content
Snippets Groups Projects
camera3d.cpp 8.16 KiB
Newer Older
#include "camera3d.hpp"
#include "../modules/camera.hpp"

#include <loguru.hpp>

using ftl::gui2::CameraView3D;

// =============================================================================

static Eigen::Affine3d create_rotation_matrix(float ax, float ay, float az) {
	Eigen::Affine3d rx =
		Eigen::Affine3d(Eigen::AngleAxisd(ax, Eigen::Vector3d(1, 0, 0)));
	Eigen::Affine3d ry =
		Eigen::Affine3d(Eigen::AngleAxisd(ay, Eigen::Vector3d(0, 1, 0)));
	Eigen::Affine3d rz =
		Eigen::Affine3d(Eigen::AngleAxisd(az, Eigen::Vector3d(0, 0, 1)));
	return rz * rx * ry;
}

// ==== CameraView3D ===========================================================

CameraView3D::CameraView3D(ftl::gui2::Screen *parent, ftl::gui2::Camera *ctrl) :
		CameraView(parent, ctrl) {

	eye_ = Eigen::Vector3d::Zero();
	neye_ = Eigen::Vector4d::Zero();
	rotmat_.setIdentity();

	rx_ = 0.0;
	ry_ = 0.0;

	ftime_ = 0.0;
	delta_ = 0.0;
	lerp_speed_ = 0.999f;

	pose_up_to_date_.test_and_set();

	tools_->setAvailable({
		Tools::SELECT_POINT,
		Tools::MOVEMENT,
		Tools::OVERLAY,
		Tools::INSPECT_POINT,
		Tools::CLIPPING,
		Tools::MOVE_CURSOR,
		Tools::ORIGIN_TO_CURSOR,
		Tools::RESET_ORIGIN,
		Tools::SAVE_CURSOR,
		Tools::ROTATE_X,
		Tools::ROTATE_Y,
		Tools::ROTATE_Z,
		Tools::TRANSLATE_X,
		Tools::TRANSLATE_Y,
		Tools::TRANSLATE_Z
	setZoom(false);
	setPan(false);

	tools_->setTool(Tools::MOVEMENT);

	tools_->addCallback([this](ftl::gui2::Tools tool) {
		if (tool == Tools::ORIGIN_TO_CURSOR) {
			ctrl_->setOriginToCursor();
			tools_->setTool(Tools::MOVEMENT);
			return true;
		} else if (tool == Tools::RESET_ORIGIN) {
			ctrl_->resetOrigin();
			tools_->setTool(Tools::MOVEMENT);
			return true;
		} else if (tool == Tools::SAVE_CURSOR) {
			ctrl_->saveCursorToPoser();
			tools_->setTool(Tools::MOVEMENT);
			return true;
		} else if (tool == Tools::ROTATE_X || tool == Tools::ROTATE_Y || tool == Tools::ROTATE_Z ||
					tool == Tools::TRANSLATE_X || tool == Tools::TRANSLATE_Y || tool == Tools::TRANSLATE_Z) {
			LOG(INFO) << "Loading cache pose";
			cache_pose_ = ctrl_->getActivePose();
			cache_screen_ = ctrl_->getActivePoseScreenCoord();
		}
		return false;
	});
}

bool CameraView3D::keyboardEvent(int key, int scancode, int action, int modifiers) {
	if (key == 263 || key == 262) {
		float mag = (modifiers & 0x1) ? 0.01f : 0.1f;
		float scalar = (key == 263) ? -mag : mag;
		neye_ += rotmat_*Eigen::Vector4d(scalar, 0.0, 0.0, 1.0);
		pose_up_to_date_.clear();
	}
	else if (key == 264 || key == 265) {
		float mag = (modifiers & 0x1) ? 0.01f : 0.1f;
		float scalar = (key == 264) ? -mag : mag;
		neye_ += rotmat_*Eigen::Vector4d(0.0, 0.0, scalar, 1.0);
		pose_up_to_date_.clear();
	}
	else if (key == 266 || key == 267) {
		float mag = (modifiers & 0x1) ? 0.01f : 0.1f;
		float scalar = (key == 266) ? -mag : mag;
		neye_ += rotmat_*Eigen::Vector4d(0.0, scalar, 0.0, 1.0);
		pose_up_to_date_.clear();
	}
	else if (key >= '0' && key <= '5' && modifiers == 2) {  // Ctrl+NUMBER
	}

	return true;
}

bool CameraView3D::mouseButtonEvent(const Eigen::Vector2i &p, int button, bool down, int modifiers) {
	if (button == 0 && !down) {
		if (tools_->isActive(Tools::MOVE_CURSOR)) {
			auto mouse = screen()->mousePos();
			auto pos = imview_->imageCoordinateAt((mouse - mPos).cast<float>());
			//Eigen::Vector3f world = ctrl_->worldAt(pos.x(), pos.y());

			ctrl_->setCursor(pos.x(), pos.y());
			tools_->setTool(Tools::ROTATE_CURSOR);
			return true;
		} else if (tools_->isActive(Tools::ROTATE_CURSOR)) {
			tools_->setTool(Tools::MOVEMENT);
		} else if (tools_->isActive(Tools::ROTATE_X) || tools_->isActive(Tools::ROTATE_Y) || tools_->isActive(Tools::ROTATE_Z) ||
					tools_->isActive(Tools::TRANSLATE_X) || tools_->isActive(Tools::TRANSLATE_Y) || tools_->isActive(Tools::TRANSLATE_Z)) {
			tools_->setTool(Tools::MOVEMENT);
	return CameraView::mouseButtonEvent(p, button, down, modifiers);
}

bool CameraView3D::mouseMotionEvent(const Eigen::Vector2i &p, const Eigen::Vector2i &rel, int button, int modifiers) {
	//if (button != 1) {
	//	return true;
	//}

	if (button == 1 && tools_->isActive(Tools::MOVEMENT)) {
		rx_ += rel[0];
		ry_ += rel[1];
		pose_up_to_date_.clear();
		return true;
	} else if (tools_->isActive(Tools::ROTATE_CURSOR)) {
		auto mouse = screen()->mousePos();
		auto pos = imview_->imageCoordinateAt((mouse - mPos).cast<float>());
		Eigen::Vector3f world = ctrl_->worldAt(pos.x(), pos.y());
		ctrl_->setCursorTarget(world);
		return true;
	} else if (tools_->isActive(Tools::ROTATE_X)) {
		auto screen_origin = ctrl_->getActivePoseScreenCoord();
		double angle = atan2(float(screen_origin[1] - p[1]), float(screen_origin[0] - p[0]));
		Eigen::Affine3d rx = Eigen::Affine3d(Eigen::AngleAxisd(angle, Eigen::Vector3d(1, 0, 0)));
		ctrl_->setActivePose(rx.matrix() * cache_pose_);
	} else if (tools_->isActive(Tools::ROTATE_Y)) {
		auto screen_origin = ctrl_->getActivePoseScreenCoord();
		double angle = -atan2(float(screen_origin[1] - p[1]), float(screen_origin[0] - p[0]));
		Eigen::Affine3d ry = Eigen::Affine3d(Eigen::AngleAxisd(angle, Eigen::Vector3d(0, 1, 0)));
		ctrl_->setActivePose(ry.matrix() * cache_pose_);
	} else if (tools_->isActive(Tools::ROTATE_Z)) {
		auto screen_origin = ctrl_->getActivePoseScreenCoord();
		double angle = atan2(float(screen_origin[1] - p[1]), float(screen_origin[0] - p[0]));
		Eigen::Affine3d rz = Eigen::Affine3d(Eigen::AngleAxisd(angle, Eigen::Vector3d(0, 0, 1)));
		ctrl_->setActivePose(rz.matrix() * cache_pose_);
	} else if (tools_->isActive(Tools::TRANSLATE_X)) {
		auto mouse = screen()->mousePos();
		auto pos = imview_->imageCoordinateAt((mouse - mPos).cast<float>());
		double dx = pos[0] - double(cache_screen_[0]);
		//double dy = pos[1] - double(cache_screen_[1]);
		double dist = dx; //(std::abs(dx) > std::abs(dy)) ? dx : dy;
		Eigen::Affine3d rx = Eigen::Affine3d(Eigen::Translation3d(dist*0.001, 0.0, 0.0));
		ctrl_->setActivePose(rx.matrix() * cache_pose_);
	} else if (tools_->isActive(Tools::TRANSLATE_Y)) {
		auto mouse = screen()->mousePos();
		auto pos = imview_->imageCoordinateAt((mouse - mPos).cast<float>());
		double dx = pos[0] - double(cache_screen_[0]);
		//double dy = pos[1] - double(cache_screen_[1]);
		double dist = dx; //(std::abs(dx) > std::abs(dy)) ? dx : dy;
		Eigen::Affine3d rx = Eigen::Affine3d(Eigen::Translation3d(0.0, dist*0.001, 0.0));
		ctrl_->setActivePose(rx.matrix() * cache_pose_);
	} else if (tools_->isActive(Tools::TRANSLATE_Z)) {
		auto mouse = screen()->mousePos();
		auto pos = imview_->imageCoordinateAt((mouse - mPos).cast<float>());
		double dx = pos[0] - double(cache_screen_[0]);
		//double dy = pos[1] - double(cache_screen_[1]);
		double dist = dx; //(std::abs(dx) > std::abs(dy)) ? dx : dy;
		Eigen::Affine3d rx = Eigen::Affine3d(Eigen::Translation3d(0.0, 0.0, dist*0.001));
		ctrl_->setActivePose(rx.matrix() * cache_pose_);

	//LOG(INFO) << "New pose: \n" << getUpdatedPose();
	//ctrl_->sendPose(getUpdatedPose());
	return false;
}

bool CameraView3D::scrollEvent(const Eigen::Vector2i &p, const Eigen::Vector2f &rel) {
	return true;
}

bool CameraView3D::keyboardCharacterEvent(unsigned int codepoint) {
	LOG(INFO) << "keyboardCharacterEvent: " << codepoint;
	return false;
}

Eigen::Matrix4d CameraView3D::getUpdatedPose() {
	float mspeed = ctrl_->value("mouse_speed", 0.2f);
	float rrx = ((float)ry_ * mspeed * delta_);
	float rry = (float)rx_ * mspeed * delta_;
	float rrz = 0.0;

	Eigen::Affine3d r = create_rotation_matrix(rrx, -rry, rrz);
	rotmat_ = rotmat_ * r.matrix();

	rx_ = 0;
	ry_ = 0;

	eye_[0] += (neye_[0] - eye_[0]) * lerp_speed_ * delta_;
	eye_[1] += (neye_[1] - eye_[1]) * lerp_speed_ * delta_;
	eye_[2] += (neye_[2] - eye_[2]) * lerp_speed_ * delta_;

	Eigen::Translation3d trans(eye_);
	Eigen::Affine3d t(trans);
	return t.matrix() * rotmat_;
}

void CameraView3D::processAnimation() {
	Eigen::Vector3d diff;
	diff[0] = neye_[0] - eye_[0];
	diff[1] = neye_[1] - eye_[1];
	diff[2] = neye_[2] - eye_[2];

	// Only update pose if there is enough motion
	if (diff.norm() > 0.01) {
		pose_up_to_date_.clear();
	}
}

void CameraView3D::draw(NVGcontext* ctx) {
	double now = glfwGetTime();
	delta_ = now - ftime_;
	ftime_ = now;

	processAnimation();

	// poll from ctrl_ or send on event instead?
	if (!pose_up_to_date_.test_and_set()) {
		ctrl_->sendPose(getUpdatedPose());
	}
	CameraView::draw(ctx);
}