1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
|
// ----------------------------------------------------------------------------
// - Open3D: www.open3d.org -
// ----------------------------------------------------------------------------
// Copyright (c) 2018-2024 www.open3d.org
// SPDX-License-Identifier: MIT
// ----------------------------------------------------------------------------
#include <atomic>
#include <chrono>
#include <mutex>
#include <thread>
#include "open3d/Open3D.h"
using namespace open3d;
using namespace open3d::visualization;
const int WIDTH = 1024;
const int HEIGHT = 768;
const Eigen::Vector3f CENTER_OFFSET(0.0f, 0.0f, -3.0f);
const std::string CLOUD_NAME = "points";
class MultipleWindowsApp {
public:
MultipleWindowsApp() {
is_done_ = false;
gui::Application::GetInstance().Initialize();
}
void Run() {
main_vis_ = std::make_shared<visualizer::O3DVisualizer>(
"Open3D - Multi-Window Demo", WIDTH, HEIGHT);
main_vis_->AddAction(
"Take snapshot in new window",
[this](visualizer::O3DVisualizer &) { this->OnSnapshot(); });
main_vis_->SetOnClose([this]() { return this->OnMainWindowClosing(); });
gui::Application::GetInstance().AddWindow(main_vis_);
auto r = main_vis_->GetOSFrame();
snapshot_pos_ = gui::Point(r.x, r.y);
std::thread read_thread([this]() { this->ReadThreadMain(); });
gui::Application::GetInstance().Run();
read_thread.join();
}
private:
void OnSnapshot() {
n_snapshots_ += 1;
snapshot_pos_ = gui::Point(snapshot_pos_.x + 50, snapshot_pos_.y + 50);
auto title = std::string("Open3D - Multi-Window Demo (Snapshot #") +
std::to_string(n_snapshots_) + ")";
auto new_vis = std::make_shared<visualizer::O3DVisualizer>(title, WIDTH,
HEIGHT);
geometry::AxisAlignedBoundingBox bounds;
{
std::lock_guard<std::mutex> lock(cloud_lock_);
auto mat = rendering::MaterialRecord();
mat.shader = "defaultUnlit";
new_vis->AddGeometry(
CLOUD_NAME + " #" + std::to_string(n_snapshots_), cloud_,
&mat);
bounds = cloud_->GetAxisAlignedBoundingBox();
}
new_vis->ResetCameraToDefault();
Eigen::Vector3f center = bounds.GetCenter().cast<float>();
new_vis->SetupCamera(60, center, center + CENTER_OFFSET,
{0.0f, -1.0f, 0.0f});
gui::Application::GetInstance().AddWindow(new_vis);
auto r = new_vis->GetOSFrame();
new_vis->SetOSFrame(
gui::Rect(snapshot_pos_.x, snapshot_pos_.y, r.width, r.height));
}
bool OnMainWindowClosing() {
// Ensure object is free so Filament can clean up without crashing.
// Also signals to the "reading" thread that it is finished.
main_vis_.reset();
return true; // false would cancel the close
}
private:
void ReadThreadMain() {
// This is NOT the UI thread, need to call PostToMainThread() to
// update the scene or any part of the UI.
data::DemoICPPointClouds demo_icp_pointclouds;
geometry::AxisAlignedBoundingBox bounds;
Eigen::Vector3d extent;
{
std::lock_guard<std::mutex> lock(cloud_lock_);
cloud_ = std::make_shared<geometry::PointCloud>();
io::ReadPointCloud(demo_icp_pointclouds.GetPaths(0), *cloud_);
bounds = cloud_->GetAxisAlignedBoundingBox();
extent = bounds.GetExtent();
}
auto mat = rendering::MaterialRecord();
mat.shader = "defaultUnlit";
gui::Application::GetInstance().PostToMainThread(
main_vis_.get(), [this, bounds, mat]() {
std::lock_guard<std::mutex> lock(cloud_lock_);
main_vis_->AddGeometry(CLOUD_NAME, cloud_, &mat);
main_vis_->ResetCameraToDefault();
Eigen::Vector3f center = bounds.GetCenter().cast<float>();
main_vis_->SetupCamera(60, center, center + CENTER_OFFSET,
{0.0f, -1.0f, 0.0f});
});
Eigen::Vector3d magnitude = 0.005 * extent;
utility::random::UniformRealGenerator<double> uniform_gen(-0.5, 0.5);
while (main_vis_) {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
// Perturb the cloud with a random walk to simulate an actual read
{
std::lock_guard<std::mutex> lock(cloud_lock_);
for (size_t i = 0; i < cloud_->points_.size(); ++i) {
Eigen::Vector3d perturb(magnitude[0] * uniform_gen(),
magnitude[1] * uniform_gen(),
magnitude[2] * uniform_gen());
cloud_->points_[i] += perturb;
}
}
if (!main_vis_) { // might have changed while sleeping
break;
}
gui::Application::GetInstance().PostToMainThread(
main_vis_.get(), [this, mat]() {
std::lock_guard<std::mutex> lock(cloud_lock_);
// Note: if the number of points is less than or equal
// to the number of points in the original object that
// was added, using Scene::UpdateGeometry() will be
// faster. Requires that the point cloud be a
// t::PointCloud.
main_vis_->RemoveGeometry(CLOUD_NAME);
main_vis_->AddGeometry(CLOUD_NAME, cloud_, &mat);
});
}
}
private:
std::mutex cloud_lock_;
std::shared_ptr<geometry::PointCloud> cloud_;
std::atomic<bool> is_done_;
std::shared_ptr<visualizer::O3DVisualizer> main_vis_;
int n_snapshots_ = 0;
gui::Point snapshot_pos_;
};
int main(int argc, char *argv[]) {
MultipleWindowsApp().Run();
return 0;
}
|