diff --git a/applications/gui/src/main.cpp b/applications/gui/src/main.cpp index 220dd20ce55e33e8f409435baedaab3aca338f30..47c1aa7c5b5025f2824ce4822c6e1d8aa16858fb 100644 --- a/applications/gui/src/main.cpp +++ b/applications/gui/src/main.cpp @@ -29,6 +29,88 @@ using ftl::rgbd::Source; };*/ +namespace { + constexpr char const *const defaultImageViewVertexShader = + R"(#version 330 + uniform vec2 scaleFactor; + uniform vec2 position; + in vec2 vertex; + out vec2 uv; + void main() { + uv = vertex; + vec2 scaledVertex = (vertex * scaleFactor) + position; + gl_Position = vec4(2.0*scaledVertex.x - 1.0, + 1.0 - 2.0*scaledVertex.y, + 0.0, 1.0); + })"; + + constexpr char const *const defaultImageViewFragmentShader = + R"(#version 330 + uniform sampler2D image; + out vec4 color; + in vec2 uv; + void main() { + color = texture(image, uv); + })"; + + + 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 FTLApplication : public nanogui::Screen { public: @@ -37,22 +119,154 @@ class FTLApplication : public nanogui::Screen { net_ = net; auto cwindow = new ftl::gui::ControlWindow(this, controller); - auto swindow = new ftl::gui::SourceWindow(this, controller); + swindow_ = new ftl::gui::SourceWindow(this, controller); + + //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; + depth_ = false; + + mShader.init("RGBDShader", defaultImageViewVertexShader, + defaultImageViewFragmentShader); + + MatrixXu indices(3, 2); + indices.col(0) << 0, 1, 2; + indices.col(1) << 2, 3, 1; + + MatrixXf vertices(2, 4); + vertices.col(0) << 0, 0; + vertices.col(1) << 1, 0; + vertices.col(2) << 0, 1; + vertices.col(3) << 1, 1; + + mShader.bind(); + mShader.uploadIndices(indices); + mShader.uploadAttrib("vertex", vertices); setVisible(true); performLayout(); } + ~FTLApplication() { + mShader.free(); + } + + bool mouseButtonEvent(const nanogui::Vector2i &p, int button, bool down, int modifiers) { + if (Screen::mouseButtonEvent(p, button, down, modifiers)) { + return true; + } else { + auto src_ = swindow_->getSource(); + if (src_ && src_->isReady() && 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]; + return true; + } + return false; + } + } + + bool keyboardEvent(int key, int scancode, int action, int modifiers) { + if (Screen::keyboardEvent(key, scancode, action, modifiers)) { + return true; + } else { + 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_; + return true; + } else if (key == 84 || key == 82) { + float scalar = (key == 84) ? 0.99f : 1.01f; + eye_ = ((eye_ - centre_) * scalar) + centre_; + return true; + } + return false; + } + } + virtual void draw(NVGcontext *ctx) { - nvgText(ctx, 10, 10, "FT-Lab Remote Presence System", NULL); + using namespace Eigen; + + auto src_ = swindow_->getSource(); + Vector2f imageSize(0, 0); + + 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_->getFrames(rgb, depth); + + if (swindow_->getDepth()) { + if (depth.rows > 0) { + imageSize = Vector2f(depth.cols,depth.rows); + cv::Mat idepth; + depth.convertTo(idepth, CV_8U, 255.0f / 10.0f); // TODO(nick) + applyColorMap(idepth, idepth, cv::COLORMAP_JET); + texture_.update(idepth); + mImageID = texture_.texture(); + } + } else { + if (rgb.rows > 0) { + imageSize = Vector2f(rgb.cols,rgb.rows); + texture_.update(rgb); + mImageID = texture_.texture(); + } + } + } + + if (imageSize[0] > 0) { + Vector2f screenSize = size().cast<float>(); + auto mScale = (screenSize.cwiseQuotient(imageSize).minCoeff()); + Vector2f scaleFactor = mScale * imageSize.cwiseQuotient(screenSize); + Vector2f positionInScreen(0.0f, 0.0f); + auto mOffset = (screenSize - (screenSize.cwiseProduct(scaleFactor))) / 2; + Vector2f positionAfterOffset = positionInScreen + mOffset; + Vector2f imagePosition = positionAfterOffset.cwiseQuotient(screenSize); + //glEnable(GL_SCISSOR_TEST); + //float r = screen->pixelRatio(); + /* glScissor(positionInScreen.x() * r, + (screenSize.y() - positionInScreen.y() - size().y()) * r, + size().x() * r, size().y() * r);*/ + mShader.bind(); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, mImageID); + mShader.setUniform("image", 0); + mShader.setUniform("scaleFactor", scaleFactor); + mShader.setUniform("position", imagePosition); + mShader.drawIndexed(GL_TRIANGLES, 0, 2); + //glDisable(GL_SCISSOR_TEST); + } + + nvgText(ctx, 10, 20, "FT-Lab Remote Presence System", NULL); /* Draw the user interface */ + screen()->performLayout(ctx); Screen::draw(ctx); } private: + ftl::gui::SourceWindow *swindow_; //std::vector<SourceViews> sources_; ftl::net::Universe *net_; + nanogui::GLShader mShader; + GLuint mImageID; + //Source *src_; + GLTexture texture_; + Eigen::Vector3f eye_; + Eigen::Vector3f centre_; + Eigen::Vector3f up_; + Eigen::Vector3f lookPoint_; + float lerpSpeed_; + bool depth_; }; int main(int argc, char **argv) { diff --git a/applications/gui/src/src_window.cpp b/applications/gui/src/src_window.cpp index 15b8c5439234b87ba6e414209f644718ed3626a9..98b760676a06099b7994ccbb0dc2db5d67b48783 100644 --- a/applications/gui/src/src_window.cpp +++ b/applications/gui/src/src_window.cpp @@ -159,15 +159,16 @@ SourceWindow::SourceWindow(nanogui::Widget *parent, ftl::ctrl::Master *ctrl) using namespace nanogui; + depth_ = false; src_ = ftl::create<Source>(ctrl->getRoot(), "source", ctrl->getNet()); - Widget *tools = new Widget(this); - tools->setLayout(new BoxLayout(Orientation::Horizontal, - Alignment::Middle, 0, 6)); + //Widget *tools = new Widget(this); + // tools->setLayout(new BoxLayout(Orientation::Horizontal, + // Alignment::Middle, 0, 6)); - new Label(tools, "Select source","sans-bold"); + new Label(this, "Select source","sans-bold"); available_ = ctrl->getNet()->findAll<string>("list_streams"); - auto select = new ComboBox(tools, available_); + auto select = new ComboBox(this, available_); select->setCallback([this,select](int ix) { LOG(INFO) << "Change source: " << ix; src_->set("uri", available_[ix]); @@ -178,10 +179,11 @@ SourceWindow::SourceWindow(nanogui::Widget *parent, ftl::ctrl::Master *ctrl) select->setItems(available_); }); - auto depth = new Button(tools, "Depth"); + auto depth = new Button(this, "Depth"); depth->setFlags(Button::ToggleButton); depth->setChangeCallback([this](bool state) { - image_->setDepth(state); + //image_->setDepth(state); + depth_ = state; }); #ifdef HAVE_LIBARCHIVE @@ -210,11 +212,11 @@ SourceWindow::SourceWindow(nanogui::Widget *parent, ftl::ctrl::Master *ctrl) }); #endif - auto imageView = new VirtualCameraView(this); + //auto imageView = new VirtualCameraView(this); //cam.view = imageView; - imageView->setGridThreshold(20); - imageView->setSource(src_); - image_ = imageView; + //imageView->setGridThreshold(20); + //imageView->setSource(src_); + //image_ = imageView; } SourceWindow::~SourceWindow() { diff --git a/applications/gui/src/src_window.hpp b/applications/gui/src/src_window.hpp index 4b92f2e1f66b10046207063d3b3745d3b7dd9b28..6258267c62fcec81c688a89a9361c4c58d166938 100644 --- a/applications/gui/src/src_window.hpp +++ b/applications/gui/src/src_window.hpp @@ -21,9 +21,13 @@ class SourceWindow : public nanogui::Window { SourceWindow(nanogui::Widget *parent, ftl::ctrl::Master *ctrl); ~SourceWindow(); + ftl::rgbd::Source *getSource() const { return src_; } + bool getDepth() const { return depth_; } + private: ftl::ctrl::Master *ctrl_; ftl::rgbd::Source *src_; + bool depth_; VirtualCameraView *image_; std::vector<std::string> available_;