diff --git a/applications/gui/CMakeLists.txt b/applications/gui/CMakeLists.txt index c4ea2272fd92e76ccf0aa6b4b9154d06aaea8485..ac9ae7ec20881ab9c4112d9baf0696ce87693536 100644 --- a/applications/gui/CMakeLists.txt +++ b/applications/gui/CMakeLists.txt @@ -5,6 +5,7 @@ set(GUISRC src/main.cpp src/ctrl_window.cpp + src/src_window.cpp ) add_executable(ftl-gui ${GUISRC}) diff --git a/applications/gui/src/main.cpp b/applications/gui/src/main.cpp index a12ff59055e2aca407f3b0032850bce52163cc07..f0cd4c325894c5708be23bee6a02d578c726c2d5 100644 --- a/applications/gui/src/main.cpp +++ b/applications/gui/src/main.cpp @@ -17,137 +17,18 @@ #include <nanogui/label.h> #include "ctrl_window.hpp" +#include "src_window.hpp" using std::string; using ftl::rgbd::RGBDSource; -class GLTexture { - public: - GLTexture() { - glGenTextures(1, &glid_); - glBindTexture(GL_TEXTURE_2D, glid_); - cv::Mat m(cv::Size(100,100), CV_8UC3); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, m.cols, m.rows, 0, GL_RGB, GL_UNSIGNED_BYTE, m.data); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - } - ~GLTexture() { - glDeleteTextures(1, &glid_); - } - - void update(cv::Mat &m) { - if (m.rows == 0) return; - glBindTexture(GL_TEXTURE_2D, glid_); - // TODO Allow for other formats - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, m.cols, m.rows, 0, GL_BGR, GL_UNSIGNED_BYTE, m.data); - auto err = glGetError(); - if (err != 0) LOG(ERROR) << "OpenGL Texture error: " << err; - } - - unsigned int texture() const { return glid_; } - - private: - unsigned int glid_; -}; - /*struct SourceViews { ftl::rgbd::RGBDSource *source; GLTexture texture; nanogui::ImageView *view; };*/ -template<class T> -Eigen::Matrix<T,4,4> lookAt -( - Eigen::Matrix<T,3,1> const & eye, - Eigen::Matrix<T,3,1> const & center, - Eigen::Matrix<T,3,1> const & up -) -{ - typedef Eigen::Matrix<T,4,4> Matrix4; - typedef Eigen::Matrix<T,3,1> Vector3; - - Vector3 f = (center - eye).normalized(); - Vector3 u = up.normalized(); - Vector3 s = f.cross(u).normalized(); - u = s.cross(f); - - Matrix4 res; - res << s.x(),s.y(),s.z(),-s.dot(eye), - u.x(),u.y(),u.z(),-u.dot(eye), - -f.x(),-f.y(),-f.z(),f.dot(eye), - 0,0,0,1; - - return res; -} - -class VirtualCameraView : public nanogui::ImageView { - public: - VirtualCameraView(nanogui::Widget *parent) : nanogui::ImageView(parent, 0) { - src_ = nullptr; - eye_ = Eigen::Vector3f(0.0f, 0.0f, 0.0f); - centre_ = Eigen::Vector3f(0.0f, 0.0f, -4.0f); - up_ = Eigen::Vector3f(0,1.0f,0); - lookPoint_ = Eigen::Vector3f(0.0f,0.0f,-4.0f); - lerpSpeed_ = 0.4f; - } - void setSource(RGBDSource *src) { src_ = src; } - - bool mouseButtonEvent(const nanogui::Vector2i &p, int button, bool down, int modifiers) { - //LOG(INFO) << "Mouse move: " << p[0]; - if (src_ && down) { - Eigen::Vector4f camPos = src_->point(p[0],p[1]); - camPos *= -1.0f; - Eigen::Vector4f worldPos = src_->getPose() * camPos; - lookPoint_ = Eigen::Vector3f(worldPos[0],worldPos[1],worldPos[2]); - LOG(INFO) << "Depth at click = " << -camPos[2]; - } - } - - bool keyboardEvent(int key, int scancode, int action, int modifiers) { - LOG(INFO) << "Key press" << key << " - " << action; - if (key == 81 || key == 83) { - // TODO Should rotate around lookAt object, but requires correct depth - Eigen::Quaternion<float> q; q = Eigen::AngleAxis<float>((key == 81) ? 0.01f : -0.01f, up_); - eye_ = (q * (eye_ - centre_)) + centre_; - } else if (key == 84 || key == 82) { - float scalar = (key == 84) ? 0.99f : 1.01f; - eye_ = ((eye_ - centre_) * scalar) + centre_; - } - } - - void draw(NVGcontext *ctx) { - //net_->broadcast("grab"); - if (src_) { - cv::Mat rgb, depth; - centre_ += (lookPoint_ - centre_) * (lerpSpeed_ * 0.1f); - Eigen::Matrix4f viewPose = lookAt<float>(eye_,centre_,up_).inverse(); - - src_->setPose(viewPose); - src_->grab(); - src_->getRGBD(rgb, depth); - if (rgb.rows > 0) { - texture_.update(rgb); - bindImage(texture_.texture()); - } - - screen()->performLayout(ctx); - } - ImageView::draw(ctx); - } - - private: - RGBDSource *src_; - GLTexture texture_; - Eigen::Vector3f eye_; - Eigen::Vector3f centre_; - Eigen::Vector3f up_; - Eigen::Vector3f lookPoint_; - float lerpSpeed_; -}; class FTLApplication : public nanogui::Screen { public: @@ -155,7 +36,7 @@ class FTLApplication : public nanogui::Screen { using namespace nanogui; net_ = net; - for (auto &src : root->getConfig()["sources"]) { + /*for (auto &src : root->getConfig()["sources"]) { RGBDSource *in = ftl::rgbd::RGBDSource::create(src, net); //new ftl::rgbd::NetSource(src, &net); if (!in) { LOG(ERROR) << "Unrecognised source: " << src; @@ -185,9 +66,10 @@ class FTLApplication : public nanogui::Screen { //displays.emplace_back(config["display"], src["uri"]); LOG(INFO) << (string)src["uri"] << " loaded "; } - } + }*/ - auto window = new ftl::gui::ControlWindow(this, controller); + auto cwindow = new ftl::gui::ControlWindow(this, controller); + auto swindow = new ftl::gui::SourceWindow(this, controller); setVisible(true); performLayout(); diff --git a/applications/gui/src/src_window.cpp b/applications/gui/src/src_window.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5bbb12c83ad9aa7b05af146eadcd077f698e6c2b --- /dev/null +++ b/applications/gui/src/src_window.cpp @@ -0,0 +1,161 @@ +#include "src_window.hpp" + +#include <nanogui/imageview.h> +#include <nanogui/combobox.h> +#include <nanogui/label.h> +#include <nanogui/opengl.h> +#include <nanogui/glutil.h> +#include <nanogui/screen.h> +#include <nanogui/layout.h> + +using ftl::gui::SourceWindow; +using ftl::rgbd::RGBDSource; +using std::string; + +class GLTexture { + public: + GLTexture() { + glGenTextures(1, &glid_); + glBindTexture(GL_TEXTURE_2D, glid_); + cv::Mat m(cv::Size(100,100), CV_8UC3); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, m.cols, m.rows, 0, GL_RGB, GL_UNSIGNED_BYTE, m.data); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + } + ~GLTexture() { + glDeleteTextures(1, &glid_); + } + + void update(cv::Mat &m) { + if (m.rows == 0) return; + glBindTexture(GL_TEXTURE_2D, glid_); + // TODO Allow for other formats + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, m.cols, m.rows, 0, GL_BGR, GL_UNSIGNED_BYTE, m.data); + auto err = glGetError(); + if (err != 0) LOG(ERROR) << "OpenGL Texture error: " << err; + } + + unsigned int texture() const { return glid_; } + + private: + unsigned int glid_; +}; + +template<class T> +Eigen::Matrix<T,4,4> lookAt +( + Eigen::Matrix<T,3,1> const & eye, + Eigen::Matrix<T,3,1> const & center, + Eigen::Matrix<T,3,1> const & up +) +{ + typedef Eigen::Matrix<T,4,4> Matrix4; + typedef Eigen::Matrix<T,3,1> Vector3; + + Vector3 f = (center - eye).normalized(); + Vector3 u = up.normalized(); + Vector3 s = f.cross(u).normalized(); + u = s.cross(f); + + Matrix4 res; + res << s.x(),s.y(),s.z(),-s.dot(eye), + u.x(),u.y(),u.z(),-u.dot(eye), + -f.x(),-f.y(),-f.z(),f.dot(eye), + 0,0,0,1; + + return res; +} + +class VirtualCameraView : public nanogui::ImageView { + public: + VirtualCameraView(nanogui::Widget *parent) : nanogui::ImageView(parent, 0) { + src_ = nullptr; + eye_ = Eigen::Vector3f(0.0f, 0.0f, 0.0f); + centre_ = Eigen::Vector3f(0.0f, 0.0f, -4.0f); + up_ = Eigen::Vector3f(0,1.0f,0); + lookPoint_ = Eigen::Vector3f(0.0f,0.0f,-4.0f); + lerpSpeed_ = 0.4f; + } + + void setSource(RGBDSource *src) { src_ = src; } + + bool mouseButtonEvent(const nanogui::Vector2i &p, int button, bool down, int modifiers) { + //LOG(INFO) << "Mouse move: " << p[0]; + if (src_ && down) { + Eigen::Vector4f camPos = src_->point(p[0],p[1]); + camPos *= -1.0f; + Eigen::Vector4f worldPos = src_->getPose() * camPos; + lookPoint_ = Eigen::Vector3f(worldPos[0],worldPos[1],worldPos[2]); + LOG(INFO) << "Depth at click = " << -camPos[2]; + } + } + + bool keyboardEvent(int key, int scancode, int action, int modifiers) { + LOG(INFO) << "Key press" << key << " - " << action; + if (key == 81 || key == 83) { + // TODO Should rotate around lookAt object, but requires correct depth + Eigen::Quaternion<float> q; q = Eigen::AngleAxis<float>((key == 81) ? 0.01f : -0.01f, up_); + eye_ = (q * (eye_ - centre_)) + centre_; + } else if (key == 84 || key == 82) { + float scalar = (key == 84) ? 0.99f : 1.01f; + eye_ = ((eye_ - centre_) * scalar) + centre_; + } + } + + void draw(NVGcontext *ctx) { + //net_->broadcast("grab"); + if (src_) { + cv::Mat rgb, depth; + centre_ += (lookPoint_ - centre_) * (lerpSpeed_ * 0.1f); + Eigen::Matrix4f viewPose = lookAt<float>(eye_,centre_,up_).inverse(); + + src_->setPose(viewPose); + src_->grab(); + src_->getRGBD(rgb, depth); + if (rgb.rows > 0) { + texture_.update(rgb); + bindImage(texture_.texture()); + } + + screen()->performLayout(ctx); + } + ImageView::draw(ctx); + } + + private: + RGBDSource *src_; + GLTexture texture_; + Eigen::Vector3f eye_; + Eigen::Vector3f centre_; + Eigen::Vector3f up_; + Eigen::Vector3f lookPoint_; + float lerpSpeed_; +}; + +SourceWindow::SourceWindow(nanogui::Widget *parent, ftl::ctrl::Master *ctrl) + : nanogui::Window(parent, "Source View"), ctrl_(ctrl) { + setLayout(new nanogui::GroupLayout()); + + using namespace nanogui; + + src_ = ftl::create<ftl::rgbd::NetSource>(ctrl->getRoot(), "source", ctrl->getNet()); + + new Label(this, "Select source","sans-bold"); + auto available = ctrl->getNet()->findAll<string>("list_streams"); + auto select = new ComboBox(this, available); + select->setCallback([this,available](int ix) { + LOG(INFO) << "Change source: " << ix; + src_->set("uri", available[ix]); + }); + + auto imageView = new VirtualCameraView(this); + //cam.view = imageView; + imageView->setGridThreshold(20); + imageView->setSource(src_); +} + +SourceWindow::~SourceWindow() { + +} diff --git a/applications/gui/src/src_window.hpp b/applications/gui/src/src_window.hpp new file mode 100644 index 0000000000000000000000000000000000000000..b52ae8034ff6733d2582c33dd689e3a7c937f7c9 --- /dev/null +++ b/applications/gui/src/src_window.hpp @@ -0,0 +1,29 @@ +#ifndef _FTL_GUI_SRCWINDOW_HPP_ +#define _FTL_GUI_SRCWINDOW_HPP_ + +#include <nanogui/window.h> +#include <ftl/master.hpp> +#include <ftl/uuid.hpp> +#include <ftl/net_source.hpp> + +namespace ftl { +namespace gui { + +/** + * Manage connected nodes and add new connections. + */ +class SourceWindow : public nanogui::Window { + public: + SourceWindow(nanogui::Widget *parent, ftl::ctrl::Master *ctrl); + ~SourceWindow(); + + private: + ftl::ctrl::Master *ctrl_; + ftl::rgbd::NetSource *src_; + +}; + +} +} + +#endif // _FTL_GUI_SRCWINDOW_HPP_ diff --git a/components/common/cpp/include/ftl/configuration.hpp b/components/common/cpp/include/ftl/configuration.hpp index 046b58a07bf536a691a970b3a9d93bf4b5ab119b..d38472746254587e4f495547ba48e095f750ed12 100644 --- a/components/common/cpp/include/ftl/configuration.hpp +++ b/components/common/cpp/include/ftl/configuration.hpp @@ -126,15 +126,23 @@ T *ftl::config::create(ftl::Configurable *parent, const std::string &name, ARGS entity["$id"] = id_str + std::string("#") + name; } } - } /*else { - nlohmann::json &res = resolve(entity); - if (!res["uri"].is_string()) { - res["uri"] = *parent->get<std::string>("uri") + std::string("/") + name; - LOG(WARNING) << "Creating false URI!!! - " << res["uri"].get<std::string>(); + + return create<T>(entity, args...); + } else if (entity.is_null()) { + // Must create the object from scratch... + std::string id_str = *parent->get<std::string>("$id"); + if (id_str.find('#') != std::string::npos) { + id_str = id_str + std::string("/") + name; + } else { + id_str = id_str + std::string("#") + name; } - }*/ + parent->getConfig()[name] = { + {"$id", id_str} + }; - return create<T>(entity, args...); + nlohmann::json &entity2 = parent->getConfig()[name]; + return create<T>(entity2, args...); + } } #endif // _FTL_COMMON_CONFIGURATION_HPP_ diff --git a/components/control/cpp/include/ftl/master.hpp b/components/control/cpp/include/ftl/master.hpp index 2a961af8de19a97ace119a70dd2870e993733dc9..82b68cf525de5e425d2bc705e599028995798dea 100644 --- a/components/control/cpp/include/ftl/master.hpp +++ b/components/control/cpp/include/ftl/master.hpp @@ -55,6 +55,9 @@ class Master { //void onStatus(); // void onChange(); + ftl::net::Universe *getNet() { return net_; } + ftl::Configurable *getRoot() { return root_; } + private: std::vector<std::function<void(const LogEvent&)>> log_handlers_; ftl::Configurable *root_; diff --git a/components/rgbd-sources/include/ftl/net_source.hpp b/components/rgbd-sources/include/ftl/net_source.hpp index c95437da81a75a11084a853221218cbced87e05d..2693128d275e3e4f7ad317e8d93b68dd5f096aa1 100644 --- a/components/rgbd-sources/include/ftl/net_source.hpp +++ b/components/rgbd-sources/include/ftl/net_source.hpp @@ -29,15 +29,17 @@ class NetSource : public RGBDSource { } void setPose(const Eigen::Matrix4f &pose); - void setURI(const std::string &uri); private: bool has_calibration_; ftl::UUID peer_; int N_; + bool active_; + std::string uri_; bool _getCalibration(ftl::net::Universe &net, const ftl::UUID &peer, const std::string &src, ftl::rgbd::CameraParameters &p); void _recv(const std::vector<unsigned char> &jpg, const std::vector<unsigned char> &d); + void _updateURI(); }; } diff --git a/components/rgbd-sources/src/net_source.cpp b/components/rgbd-sources/src/net_source.cpp index a658be388e09d603f18d9062290982cf39185796..8c0187f039e2a60d2a0b5eb041d3efd1464a99f5 100644 --- a/components/rgbd-sources/src/net_source.cpp +++ b/components/rgbd-sources/src/net_source.cpp @@ -45,43 +45,18 @@ NetSource::NetSource(nlohmann::json &config) : RGBDSource(config) { } NetSource::NetSource(nlohmann::json &config, ftl::net::Universe *net) - : RGBDSource(config, net) { + : RGBDSource(config, net), active_(false) { - auto uri = get<string>("uri"); - if (!uri) { - LOG(ERROR) << "NetSource does not have a URI"; - return; - } - auto p = net->findOne<ftl::UUID>("find_stream", *uri); - if (!p) { - LOG(ERROR) << "Could not find stream: " << *uri; - return; - } - peer_ = *p; - - has_calibration_ = _getCalibration(*net, peer_, *uri, params_); - - net->bind(*uri, [this](const vector<unsigned char> &jpg, const vector<unsigned char> &d) { - unique_lock<mutex> lk(mutex_); - _recv(jpg, d); + on("uri", [this](const config::Event &e) { + _updateURI(); }); - N_ = 10; - - // Initiate stream with request for first 10 frames - try { - net->send(peer_, "get_stream", *uri, 10, 0, net->id(), *uri); - } catch(...) { - LOG(ERROR) << "Could not connect to stream " << *uri; - } + _updateURI(); } NetSource::~NetSource() { - auto uri = get<string>("uri"); - - // TODO(Nick) If URI changes then must unbind + rebind. - if (uri) { - net_->unbind(*uri); + if (uri_.size() > 0) { + net_->unbind(uri_); } } @@ -99,6 +74,8 @@ void NetSource::_recv(const vector<unsigned char> &jpg, const vector<unsigned ch } void NetSource::setPose(const Eigen::Matrix4f &pose) { + if (!active_) return; + vector<unsigned char> vec((unsigned char*)pose.data(), (unsigned char*)(pose.data()+(pose.size()))); try { net_->send(peer_, "set_pose", *get<string>("uri"), vec); @@ -108,10 +85,45 @@ void NetSource::setPose(const Eigen::Matrix4f &pose) { RGBDSource::setPose(pose); } -void NetSource::setURI(const std::string &uri) { - //RGBDSource::setURI(uri); - set("uri", uri); - has_calibration_ = _getCalibration(*net_, peer_, *get<string>("uri"), params_); +void NetSource::_updateURI() { + active_ = false; + auto uri = get<string>("uri"); + + // TODO(Nick) If URI changes then must unbind + rebind. + if (uri_.size() > 0) { + net_->unbind(uri_); + } + + if (uri) { + auto p = net_->findOne<ftl::UUID>("find_stream", *uri); + if (!p) { + LOG(ERROR) << "Could not find stream: " << *uri; + return; + } + peer_ = *p; + + has_calibration_ = _getCalibration(*net_, peer_, *uri, params_); + + net_->bind(*uri, [this](const vector<unsigned char> &jpg, const vector<unsigned char> &d) { + unique_lock<mutex> lk(mutex_); + _recv(jpg, d); + }); + + N_ = 10; + + // Initiate stream with request for first 10 frames + try { + net_->send(peer_, "get_stream", *uri, 10, 0, net_->id(), *uri); + } catch(...) { + LOG(ERROR) << "Could not connect to stream " << *uri; + } + + uri_ = *uri; + active_ = true; + } else { + uri_ = ""; + LOG(WARNING) << "NetSource URI is missing"; + } } void NetSource::grab() {