This example shows how to create a simple viewer using wxWidgets.
33#error "OpenGL required: set wxUSE_GLCANVAS to 1 and rebuild the library"
37#include <easy3d/core/model.h>
38#include <easy3d/core/surface_mesh.h>
39#include <easy3d/core/graph.h>
40#include <easy3d/core/point_cloud.h>
41#include <easy3d/core/poly_mesh.h>
42#include <easy3d/renderer/opengl.h>
43#include <easy3d/renderer/opengl_util.h>
44#include <easy3d/renderer/opengl_error.h>
45#include <easy3d/renderer/renderer.h>
46#include <easy3d/renderer/shader_program.h>
47#include <easy3d/renderer/shader_manager.h>
48#include <easy3d/renderer/transform.h>
49#include <easy3d/renderer/shape.h>
50#include <easy3d/renderer/camera.h>
51#include <easy3d/renderer/manipulated_camera_frame.h>
52#include <easy3d/renderer/drawable_points.h>
53#include <easy3d/renderer/drawable_lines.h>
54#include <easy3d/renderer/drawable_triangles.h>
55#include <easy3d/renderer/text_renderer.h>
56#include <easy3d/renderer/texture_manager.h>
57#include <easy3d/renderer/framebuffer_object.h>
58#include <easy3d/fileio/point_cloud_io.h>
59#include <easy3d/fileio/graph_io.h>
60#include <easy3d/fileio/surface_mesh_io.h>
61#include <easy3d/fileio/poly_mesh_io.h>
62#include <easy3d/fileio/ply_reader_writer.h>
63#include <easy3d/fileio/point_cloud_io_ptx.h>
64#include <easy3d/util/resource.h>
65#include <easy3d/util/file_system.h>
66#include <easy3d/util/setting.h>
74 wxBEGIN_EVENT_TABLE(Viewer, wxGLCanvas)
75 EVT_SIZE(Viewer::OnSize)
76 EVT_PAINT(Viewer::OnPaint)
77 EVT_MOUSE_EVENTS(Viewer::OnMouse)
78 EVT_KEY_DOWN(Viewer::OnKeyDown)
83 const wxGLAttributes& glAttrs,
88 const wxString &title)
89 : wxGLCanvas(parent, glAttrs,
id, pos, size, style | wxFULL_REPAINT_ON_RESIZE, title)
99 const int gl_major = 3;
100 const int gl_minor = 2;
101 VLOG(1) <<
"OpenGL version requested: " << gl_major <<
"." << gl_minor;
103 wxGLContextAttrs ctxAttrs;
104 ctxAttrs.PlatformDefaults().CoreProfile().OGLVersion(gl_major, gl_minor).EndList();
105 gl_context_ =
new wxGLContext(
this,
nullptr, &ctxAttrs);
107 if (!gl_context_->IsOK()) {
108 LOG(ERROR) <<
"OpenGL version error. This app needs an OpenGL 3.2 capable driver.";
110 gl_context_ =
nullptr;
114 camera_ = std::make_shared<Camera>();
116 camera_->setUpVector(
vec3(0, 0, 1));
117 camera_->setViewDirection(
vec3(-1, 0, 0));
118 camera_->showEntireScene();
126 drawable_axes_.reset();
135 LOG(INFO) <<
"viewer terminated. Bye!";
139 void Viewer::init() {
150 VLOG(1) <<
"OpenGL vendor: " << glGetString(GL_VENDOR);
151 VLOG(1) <<
"OpenGL renderer: " << glGetString(GL_RENDERER);
152 VLOG(1) <<
"OpenGL version received: " << glGetString(GL_VERSION);
153 VLOG(1) <<
"GLSL version received: " << glGetString(GL_SHADING_LANGUAGE_VERSION);
156 glEnable(GL_DEPTH_TEST);
157 glDepthFunc(GL_LESS);
159 glDepthRange(0.0f, 1.0f);
161 glClearColor(background_color_[0], background_color_[1], background_color_[2], background_color_[3]);
166 GetClientSize(&w, &h);
167 camera_->setScreenWidthAndHeight(w, h);
168 glViewport(0, 0,
static_cast<int>(
static_cast<float>(w) *
dpi_scaling()),
static_cast<int>(
static_cast<float>(h) *
dpi_scaling()));
171 texter_ = std::unique_ptr<TextRenderer>(
new TextRenderer(
dpi_scaling()));
178 auto mesh =
new SurfaceMesh;
179 mesh->set_name(
"bunny");
180 for (
const auto& p : points)
182 for (std::size_t i=0; i<indices.size(); i+=3)
183 mesh->add_triangle(SurfaceMesh::Vertex(
static_cast<int>(indices[i])), SurfaceMesh::Vertex(
static_cast<int>(indices[i+1])), SurfaceMesh::Vertex(
static_cast<int>(indices[i+2])));
184 add_model(std::shared_ptr<SurfaceMesh>(mesh),
true);
186 LOG(INFO) <<
"program initialized by creating a SurfaceMesh of the bunny model";
198 const_cast<Viewer*
>(
this)->Refresh();
202 void Viewer::OnPaint(wxPaintEvent & WXUNUSED(event)) {
206 SetCurrent(*gl_context_);
226 void Viewer::OnSize(wxSizeEvent & event) {
228 auto size =
event.GetSize();
229 const int w = size.GetWidth();
230 const int h = size.GetHeight();
231 camera_->setScreenWidthAndHeight(w, h);
232 glViewport(0, 0,
static_cast<int>(
static_cast<float>(w) *
dpi_scaling()),
static_cast<int>(
static_cast<float>(h) *
dpi_scaling()));
236 void Viewer::OnMouse(wxMouseEvent &event) {
237 static bool left_down =
false, right_down =
false;
238 static int prev_x =
event.GetX(), prev_y =
event.GetY();
240 if (event.ButtonDown(wxMOUSE_BTN_ANY)) {
241 camera_->frame()->action_start();
242 if (event.LeftDown()) left_down =
true;
243 else if (event.RightDown()) right_down =
true;
245 else if (event.ButtonUp(wxMOUSE_BTN_ANY)) {
246 camera_->frame()->action_end();
247 if (event.LeftUp()) left_down =
false;
248 else if (event.RightUp()) right_down =
false;
250 else if (event.Dragging()) {
251 const int x =
event.GetX();
252 const int y =
event.GetY();
253 const int dx = x - prev_x;
254 const int dy = y - prev_y;
261 const int rot =
event.GetWheelRotation();
263 camera_->frame()->action_zoom(rot > 0 ? 1 : -1,
camera());
266 prev_x =
event.GetX();
267 prev_y =
event.GetY();
271 void Viewer::OnKeyDown(wxKeyEvent &event) {
272 if (event.GetUnicodeKey() == wxKeyCode(
'A') && event.GetModifiers() == wxMOD_NONE) {
274 drawable_axes_->set_visible(!drawable_axes_->is_visible());
276 else if (event.GetUnicodeKey() == wxKeyCode(
'C') && event.GetModifiers() == wxMOD_NONE) {
279 }
else if (event.GetUnicodeKey() == wxKeyCode(
'F') && event.GetModifiers() == wxMOD_NONE) {
281 }
else if (event.GetUnicodeKey() == wxKeyCode(
'M') && event.GetModifiers() == wxMOD_NONE) {
285 d->set_smooth_shading(!d->smooth_shading());
287 }
else if (event.GetUnicodeKey() == wxKeyCode(
'P') &&
event.GetModifiers() == wxMOD_NONE) {
292 }
else if (event.GetUnicodeKey() == WXK_SPACE &&
event.GetModifiers() == wxMOD_NONE) {
295 frame.setTranslation(camera_->pivotPoint());
296 camera_->frame()->alignWithFrame(&frame,
true);
302 else if (event.GetUnicodeKey() == wxKeyCode(
'[') &&
event.GetModifiers() == wxMOD_NONE) {
303 for (
auto m: models_) {
304 for (
auto d: m->renderer()->lines_drawables()) {
305 float size = d->line_width() - 1.0f;
308 d->set_line_width(size);
312 else if (event.GetUnicodeKey() == wxKeyCode(
']') &&
event.GetModifiers() == wxMOD_NONE) {
313 for (
auto m: models_) {
314 for (
auto d: m->renderer()->lines_drawables()) {
315 float size = d->line_width() + 1.0f;
316 d->set_line_width(size);
320 else if (event.GetUnicodeKey() == wxKeyCode(
'-') &&
event.GetModifiers() == wxMOD_NONE) {
321 for (
auto m: models_) {
322 for (
auto d: m->renderer()->points_drawables()) {
323 float size = d->point_size() - 1.0f;
326 d->set_point_size(size);
330 else if (event.GetUnicodeKey() == wxKeyCode(
'=') &&
event.GetModifiers() == wxMOD_NONE) {
331 for (
auto m: models_) {
332 for (
auto d: m->renderer()->points_drawables()) {
333 float size = d->point_size() + 1.0f;
334 d->set_point_size(size);
338 else if (event.GetUnicodeKey() == wxKeyCode(
',') &&
event.GetModifiers() == wxMOD_NONE) {
342 model_idx_ = int((model_idx_ - 1 + models_.size()) % models_.size());
343 if (model_idx_ >= 0) {
345 std::cout <<
"current model: " << model_idx_ <<
", " << models_[model_idx_]->name() << std::endl;
348 else if (event.GetUnicodeKey() == wxKeyCode(
'.') && event.GetModifiers() == wxMOD_NONE) {
352 model_idx_ = int((model_idx_ + 1) % models_.size());
353 if (model_idx_ >= 0) {
355 std::cout <<
"current model: " << model_idx_ <<
", " << models_[model_idx_]->name() << std::endl;
358 else if (event.GetUnicodeKey() == WXK_DELETE && event.GetModifiers() == wxMOD_NONE) {
362 else if (event.GetUnicodeKey() == wxKeyCode(
'E') && event.GetModifiers() == wxMOD_NONE) {
366 edges->set_visible(!edges->is_visible());
369 else if (event.GetUnicodeKey() == wxKeyCode(
'V') && event.GetModifiers() == wxMOD_NONE) {
373 vertices->set_visible(!vertices->is_visible());
376 else if (event.GetUnicodeKey() == wxKeyCode(
'B') && event.GetModifiers() == wxMOD_NONE) {
379 auto drawable = mesh->renderer()->get_lines_drawable(
"borders");
381 drawable->set_visible(!drawable->is_visible());
384 else if (event.GetUnicodeKey() == wxKeyCode(
'L') &&
event.GetModifiers() == wxMOD_NONE) {
387 auto drawable = mesh->renderer()->get_points_drawable(
"locks");
389 drawable->set_visible(!drawable->is_visible());
392 else if (event.GetUnicodeKey() == wxKeyCode(
'D') &&
event.GetModifiers() == wxMOD_NONE) {
394 auto &output = std::cout;
399 output <<
"model is a surface mesh. #face: " << std::to_string(model->n_faces())
400 <<
", #vertex: " + std::to_string(model->n_vertices())
401 <<
", #edge: " + std::to_string(model->n_edges()) << std::endl;
404 output <<
"model is a point cloud. #vertex: " + std::to_string(model->n_vertices()) << std::endl;
407 output <<
"model is a graph. #vertex: " + std::to_string(model->n_vertices())
408 <<
", #edge: " + std::to_string(model->n_edges()) << std::endl;
411 if (!
current_model()->renderer()->points_drawables().empty()) {
412 output <<
"points drawables:\n";
414 d->buffer_stats(output);
417 output <<
"lines drawables:\n";
419 d->buffer_stats(output);
422 output <<
"triangles drawables:\n";
424 d->buffer_stats(output);
431 else if (event.GetUnicodeKey() == wxKeyCode(
'R') && event.GetModifiers() == wxMOD_NONE) {
441 if (!model && models_.empty() && drawables_.empty()) {
442 camera_->showEntireScene();
446 auto visual_box = [](
const Model *m) ->
Box3 {
447 Box3 box = m->bounding_box();
448 for (
auto d : m->renderer()->points_drawables()) box.
grow(d->bounding_box());
449 for (
auto d : m->renderer()->lines_drawables()) box.grow(d->bounding_box());
450 for (
auto d : m->renderer()->triangles_drawables()) box.grow(d->bounding_box());
456 box = visual_box(model);
458 for (
auto m : models_)
459 box.grow(visual_box(m.get()));
460 for (
auto d : drawables_)
461 box.grow(d->bounding_box());
464 if (box.is_valid()) {
465 camera_->setSceneBoundingBox(box.min_point(), box.max_point());
466 camera_->showEntireScene();
473 return static_cast<float>(wxWindow::GetContentScaleFactor());
477 Model *
Viewer::add_model(
const std::string &file_path,
bool create_default_drawables) {
479 for (
auto m: models_) {
480 if (m->name() == file_name) {
481 LOG(WARNING) <<
"model has already been added to the viewer: " << file_name;
487 bool is_ply_mesh =
false;
491 Model *model =
nullptr;
492 if ((ext ==
"ply" && is_ply_mesh) || ext ==
"obj" || ext ==
"off" || ext ==
"stl" || ext ==
"sm" ||
493 ext ==
"geojson" || ext ==
"trilist") {
497 }
else if (ext ==
"plm" || ext ==
"pm" || ext ==
"mesh") {
501 io::PointCloudIO_ptx serializer(file_name);
502 PointCloud *cloud =
nullptr;
503 while ((cloud = serializer.load_next())) {
504 model =
add_model(std::shared_ptr<PointCloud>(cloud), create_default_drawables);
513 model->set_name(file_name);
514 add_model(std::shared_ptr<Model>(model), create_default_drawables);
522 LOG(WARNING) <<
"model is nullptr";
525 for (
auto m: models_) {
527 LOG(WARNING) <<
"model has already been added to the viewer: " << m->
name();
532 model->set_renderer(std::make_shared<Renderer>(model.get(), create));
534 int pre_idx = model_idx_;
535 models_.push_back(model);
536 model_idx_ =
static_cast<int>(models_.size()) - 1;
538 if (model_idx_ != pre_idx) {
540 LOG(INFO) <<
"current model: " << model_idx_ <<
", " << models_[model_idx_]->name();
548 LOG(WARNING) <<
"model is nullptr";
552 for (
auto it = models_.begin(); it != models_.end(); ++it) {
553 if (it->get() == model) {
554 int pre_idx = model_idx_;
555 const std::string name = model->name();
557 model_idx_ =
static_cast<int>(models_.size()) - 1;
558 LOG(INFO) <<
"model deleted: " << name;
560 if (model_idx_ != pre_idx) {
562 LOG(INFO) <<
"current model: " << model_idx_ <<
", " << models_[model_idx_]->name();
569 LOG(WARNING) <<
"no such model: " << model->name();
577 if (model_idx_ < models_.size())
578 return models_[model_idx_].get();
583 bool Viewer::save_current_model(
const std::string &file_name)
const {
586 LOG(ERROR) <<
"no model exists";
591 if (
dynamic_cast<const PointCloud *
>(m))
593 else if (
dynamic_cast<const SurfaceMesh *
>(m))
595 else if (
dynamic_cast<const Graph *
>(m))
596 saved =
GraphIO::save(file_name,
dynamic_cast<const Graph *
>(m));
599 LOG(INFO) <<
"file successfully saved";
606 SetCurrent(*gl_context_);
612 fbo.add_color_buffer();
613 fbo.add_depth_buffer();
618 glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
620 glClearColor(background_color_[0], background_color_[1], background_color_[2], background_color_[3]);
621 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
628 return fbo.snapshot_color(0, file_name);
631 return fbo.snapshot_depth(file_name);
638 LOG(WARNING) <<
"drawable is nullptr";
641 for (
auto d : drawables_) {
643 LOG(WARNING) <<
"drawable has already been added to the viewer.";
644 return drawable.get();
648 drawables_.push_back(drawable);
649 return drawable.get();
655 LOG(WARNING) <<
"drawable is nullptr";
659 for (
auto it = drawables_.begin(); it != drawables_.end(); ++it) {
660 if (it->get() == drawable) {
661 drawables_.erase(it);
667 LOG(WARNING) <<
"no such drawable: " << drawable->name();
672 void Viewer::draw_corner_axes()
const {
675 std::vector<ShaderProgram::Attribute> attributes = {
686 if (!drawable_axes_) {
689 std::vector<vec3> points, normals, colors;
690 shape::create_cylinder(0.03, 10,
vec3(0, 0, 0),
vec3(base, 0, 0),
vec3(1, 0, 0), points, normals, colors);
691 shape::create_cylinder(0.03, 10,
vec3(0, 0, 0),
vec3(0, base, 0),
vec3(0, 1, 0), points, normals, colors);
692 shape::create_cylinder(0.03, 10,
vec3(0, 0, 0),
vec3(0, 0, base),
vec3(0, 0, 1), points, normals, colors);
693 shape::create_cone(0.06, 20,
vec3(base, 0, 0),
vec3(base + head, 0, 0),
vec3(1, 0, 0), points, normals,
695 shape::create_cone(0.06, 20,
vec3(0, base, 0),
vec3(0, base + head, 0),
vec3(0, 1, 0), points, normals,
697 shape::create_cone(0.06, 20,
vec3(0, 0, base),
vec3(0, 0, base + head),
vec3(0, 0, 1), points, normals,
699 shape::create_sphere(
vec3(0, 0, 0), 0.06, 20, 20,
vec3(0, 1, 1), points, normals, colors);
700 const_cast<Viewer*
>(
this)->drawable_axes_ = std::unique_ptr<TrianglesDrawable>(
new TrianglesDrawable(
"corner_axes"));
701 drawable_axes_->update_vertex_buffer(points);
702 drawable_axes_->update_normal_buffer(normals);
703 drawable_axes_->update_color_buffer(colors);
706 if (!drawable_axes_->is_visible())
711 glGetIntegerv(GL_VIEWPORT, viewport);
713 static int corner_frame_size =
static_cast<int>(100 *
dpi_scaling());
714 glViewport(0, 0, corner_frame_size, corner_frame_size);
718 glDepthRange(0, 0.01f);
721 const mat4 &view = camera_->orientation().inverse().matrix();
722 const mat4 &MVP = proj * view;
725 const vec3 &wCamPos = camera_->position();
728 const mat4 &MV = camera_->modelViewMatrix();
732 program->set_uniform(
"MVP", MVP)
735 ->set_uniform(
"lighting",
true)
736 ->set_uniform(
"two_sides_lighting",
false)
737 ->set_uniform(
"smooth_shading",
true)
738 ->set_uniform(
"wLightPos", wLightPos)
739 ->set_uniform(
"wCamPos", wCamPos)
740 ->set_uniform(
"ssaoEnabled",
false)
741 ->set_uniform(
"per_vertex_color",
true)
742 ->set_uniform(
"distinct_back_color",
false)
746 ->set_uniform(
"highlight",
false)
747 ->set_uniform(
"clippingPlaneEnabled",
false)
748 ->set_uniform(
"selected",
false)
750 ->set_uniform(
"use_texture",
false);
751 drawable_axes_->gl_draw();
755 glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
756 glDepthRange(0.0f, 1.0f);
760 void Viewer::pre_draw() {
761 glClearColor(background_color_[0], background_color_[1], background_color_[2], 1.0f);
763 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
767 void Viewer::post_draw() {
769 if (texter_ && texter_->num_fonts() >=2) {
770 const float font_size = 15.0f;
772 texter_->draw(
"Easy3D", offset, offset, font_size, 0);
780 void Viewer::draw()
const {
783 glEnable(GL_SCISSOR_TEST);
785 glGetIntegerv(GL_VIEWPORT, viewport);
786 glScissor(viewport[0], viewport[1], viewport[2] * 0.5f, viewport[3]);
787 for (
const auto m : models_) {
788 for (
auto d : m->renderer()->triangles_drawables())
791 glScissor(viewport[2] * 0.5f, viewport[1], viewport[2] * 0.5f, viewport[3]);
792 for (
const auto m : models_) {
793 for (
auto d : m->renderer()->lines_drawables())
796 glScissor(viewport[0], viewport[1], viewport[2], viewport[3]);
800 for (
const auto m : models_) {
801 if (!m->renderer()->is_visible())
807 std::size_t count = 0;
808 for (
auto d : m->renderer()->lines_drawables()) {
809 if (d->is_visible()) {
810 d->draw(
camera()); easy3d_debug_log_gl_error
815 for (
auto d : m->renderer()->points_drawables()) {
817 d->draw(
camera()); easy3d_debug_log_gl_error
821 glEnable(GL_POLYGON_OFFSET_FILL);
822 glPolygonOffset(0.5f, -0.0001f);
824 for (
auto d : m->renderer()->triangles_drawables()) {
826 d->draw(
camera()); easy3d_debug_log_gl_error
829 glDisable(GL_POLYGON_OFFSET_FILL);
832 for (
auto d : drawables_) {
@ PERSPECTIVE
Perspective camera.
Definition camera.h:144
@ ORTHOGRAPHIC
Orthographic camera.
Definition camera.h:145
void grow(const Point &p)
Add a point to this box. This will compute its new extent.
Definition box.h:276
static Graph * load(const std::string &file_name)
Reads a graph from file file_name.
Definition graph_io.cpp:37
static bool save(const std::string &file_name, const Graph *graph)
Saves graph to file file_name.
Definition graph_io.cpp:72
@ NONE
No constraint.
Definition manipulated_frame.h:165
static Mat< N, M, T > identity()
Returns an identity matrix.
Definition mat.h:697
const std::string & name() const
The name of a model.
Definition model.h:61
virtual void property_stats(std::ostream &output) const
Prints the names of all properties to an output stream (e.g., std::cout).
Definition model.h:87
Renderer * renderer()
Gets the renderer of this model.
Definition model.cpp:66
static int samples()
Get the number of samples.
Definition opengl_util.cpp:215
static void viewport(int &x, int &y, int &width, int &height)
Query the OpenGL viewport. The parameters are the same as in glViewport(x, y, width,...
Definition opengl_util.cpp:222
static bool init()
Initialize OpenGL.
Definition opengl_util.cpp:49
static bool save(const std::string &file_name, const PointCloud *cloud)
Saves a point_cloud to a file.
Definition point_cloud_io.cpp:83
static PointCloud * load(const std::string &file_name)
Reads a point cloud from file file_name.
Definition point_cloud_io.cpp:37
static PolyMesh * load(const std::string &file_name)
Reads a polyhedral mesh from a file.
Definition poly_mesh_io.cpp:37
const std::vector< std::shared_ptr< LinesDrawable > > & lines_drawables() const
Definition renderer.h:164
const std::vector< std::shared_ptr< TrianglesDrawable > > & triangles_drawables() const
Definition renderer.h:169
LinesDrawable * get_lines_drawable(const std::string &name, bool warning_not_found=true) const
Definition renderer.cpp:342
PointsDrawable * get_points_drawable(const std::string &name, bool warning_not_found=true) const
Definition renderer.cpp:332
const std::vector< std::shared_ptr< PointsDrawable > > & points_drawables() const
Definition renderer.h:159
static void terminate()
Destroy all shader programs.
Definition shader_manager.cpp:271
static ShaderProgram * create_program_from_files(const std::string &file_base_name, const std::vector< ShaderProgram::Attribute > &attributes=std::vector< ShaderProgram::Attribute >(), const std::vector< std::string > &outputs=std::vector< std::string >(), bool geom_shader=false)
Create a shader program from shader source files specified by the shader file's base name.
Definition shader_manager.cpp:49
static ShaderProgram * get_program(const std::string &shader_name)
Get the shader program if it exists and is working.
Definition shader_manager.cpp:41
static void reload()
Reload all shader programs.
Definition shader_manager.cpp:282
@ TEXCOORD
Texture coordinates.
Definition shader_program.h:82
@ NORMAL
Normal.
Definition shader_program.h:81
@ COLOR
Color.
Definition shader_program.h:80
@ POSITION
Position.
Definition shader_program.h:79
std::pair< AttribType, std::string > Attribute
Attribute: a pair of attribute type and attribute name.
Definition shader_program.h:85
@ VERTEX
Property defined on vertices.
Definition state.h:69
static SurfaceMesh * load(const std::string &file_name)
Reads a surface mesh from a file.
Definition surface_mesh_io.cpp:37
static bool save(const std::string &file_name, const SurfaceMesh *mesh)
Saves a surface mesh to a file.
Definition surface_mesh_io.cpp:84
static void terminate()
Destroy all textures and release their memory.
Definition texture_manager.cpp:161
Viewer(const std::string &title="Easy3D Viewer", int samples=4, int gl_major=3, int gl_minor=2, bool full_screen=false, bool resizable=true, int depth_bits=24, int stencil_bits=8, int width=800, int height=600)
Constructor.
Definition viewer.cpp:75
static std::size_t num_instances(const std::string &file_name, const std::string &element_name)
A quick check of the number of instances of a type of element.
std::string convert_to_native_style(const std::string &path)
Convert a path string such that it uses the current platform's path separators.
std::string simple_name(const std::string &path)
Gets file name without path but with extension (e.g, /a/b/c.Ext => c.Ext)
std::string extension(const std::string &path, bool lower=true)
Query the file extension without dot (e.g., /a/b/c.Ext => Ext).
void initialize(bool info_to_stdout=false, bool warning_to_stdcout=true, bool error_to_stdcout=true, bool verbose_to_stdcout=false, const std::string &log_file="default", int verbosity_threshold=9)
Initializes the logging module.
bool is_initialized()
Returns whether the logging has been initialized.
void setup_gl_debug_callback()
Set up a debug callback for OpenGL.
EASY3D_UTIL_EXPORT const std::vector< unsigned int > bunny_indices
EASY3D_UTIL_EXPORT const std::vector< vec3 > bunny_vertices
std::string directory()
Returns the resource directory (containing color maps, shaders, textures, fonts, etc....
EASY3D_UTIL_EXPORT vec4 highlight_color
Default highlight color for highlighted/selected primitives.
EASY3D_UTIL_EXPORT float material_shininess
Default shininess of the material.
EASY3D_UTIL_EXPORT vec4 material_specular
Default specular color of the material.
EASY3D_UTIL_EXPORT vec4 light_position
Default light position defined in the camera coordinate system.
EASY3D_UTIL_EXPORT vec4 background_color
Background color of the viewer.
EASY3D_UTIL_EXPORT vec4 material_ambient
Default ambient color of the material.
void create_sphere(const vec3 ¢er, double radius, int slices, int stacks, const vec3 &color, std::vector< vec3 > &points, std::vector< vec3 > &normals, std::vector< vec3 > &colors)
Generates data (points, normals, and colors) for a 3D sphere.
Definition shape.cpp:767
void create_cylinder(double radius, int slices, const vec3 &s, const vec3 &t, const vec3 &color, std::vector< vec3 > &points, std::vector< vec3 > &normals, std::vector< vec3 > &colors)
Prepares data (points, normals, and colors) for a 3D cylinder defined by two 3D points s and t.
Definition shape.cpp:839
void create_cone(double radius, int slices, const vec3 &s, const vec3 &t, const vec3 &color, std::vector< vec3 > &points, std::vector< vec3 > &normals, std::vector< vec3 > &colors)
Prepares data (points, normals, and colors) for a 3D cone defined by two 3D points b and t.
Definition shape.cpp:884
Vec< 3, float > vec3
A 3D point/vector of float type.
Definition types.h:44
GenericBox< 3, float > Box3
A 3D axis-aligned bounding box of float type.
Definition types.h:108
Mat< N, N, T > inverse(const Mat< N, N, T > &m)
Returns the inverse of an N x N (square) matrix.
Definition mat.h:1190
Mat4< float > mat4
A 4 by 4 matrix of float type.
Definition types.h:67
int connect(SIGNAL *signal, FUNCTION const &slot)
Connects a function to the signal.
Definition signal.h:200
35#include <easy3d/core/model.h>
36#include <easy3d/core/surface_mesh.h>
37#include <easy3d/algo/surface_mesh_subdivision.h>
38#include <easy3d/renderer/renderer.h>
39#include <easy3d/util/file_system.h>
45 wxDEFINE_EVENT(VIEW_FIT_SCREEN, wxCommandEvent);
46 wxDEFINE_EVENT(VIEW_SNAPSHOT, wxCommandEvent);
47 wxDEFINE_EVENT(EDIT_SUBDIVISION, wxCommandEvent);
49 wxBEGIN_EVENT_TABLE(Window, wxFrame)
50 EVT_MENU(wxID_OPEN, Window::menuFileOpen)
51 EVT_MENU(wxID_SAVE, Window::menuFileSave)
52 EVT_MENU(wxID_EXIT, Window::menuFileExit)
53 EVT_MENU(VIEW_FIT_SCREEN, Window::menuViewFitScreen)
54 EVT_MENU(VIEW_SNAPSHOT, Window::menuViewSnapshot)
55 EVT_MENU(EDIT_SUBDIVISION, Window::menuEditSubdivision)
56 EVT_MENU(wxID_HELP, Window::menuHelpAbout)
60 Window::Window(wxFrame *parent,
const wxString &title,
const wxPoint &pos,
const wxSize &size,
long style)
61 : wxFrame(parent, wxID_ANY, title, pos, size, style) {
63 SetIcon(wxICON(sample));
65 SetIcon(wxIcon(std::string(RESOURCE_DIR) +
"/icons/sample.xpm"));
69 auto fileMenu =
new wxMenu;
70 fileMenu->Append(wxID_OPEN,
"&Open...\tCTRL-O");
71 fileMenu->Append(wxID_SAVE,
"&Save...\tCTRL-S");
72 fileMenu->AppendSeparator();
73 fileMenu->Append(wxID_EXIT,
"E&xit\tALT-X");
76 auto viewMenu =
new wxMenu;
77 viewMenu->Append(VIEW_FIT_SCREEN,
"&Fit screen\tF");
78 viewMenu->Append(VIEW_SNAPSHOT,
"&Snapshot...\tS");
81 auto editMenu =
new wxMenu;
82 editMenu->Append(EDIT_SUBDIVISION,
"&Subdivision");
85 auto helpMenu =
new wxMenu;
86 helpMenu->Append(wxID_HELP,
"&About");
88 auto menuBar =
new wxMenuBar;
89 menuBar->Append(fileMenu,
"&File");
90 menuBar->Append(viewMenu,
"&View");
91 menuBar->Append(editMenu,
"&Edit");
92 menuBar->Append(helpMenu,
"&Help");
95 wxGLAttributes glAttrib;
96 glAttrib.PlatformDefaults().RGBA().DoubleBuffer().Depth(24).Stencil(8).SampleBuffers(1).Samplers(4).EndList();
97 viewer_ =
new Viewer(
this, glAttrib, wxID_ANY, wxDefaultPosition, GetClientSize(), wxDEFAULT_FRAME_STYLE, title);
103 void Window::menuFileOpen(wxCommandEvent & WXUNUSED(event)) {
104 const std::string &title =
"Choose a file";
105 wxString filename = wxFileSelector(title,
"",
"",
"",
106 "Surface Mesh (*.ply;*.obj;*.off;*.stl;*.sm;*.geojson;*.trilist)|"
107 "*.ply;*.obj;*.off;*.stl;*.sm;*.geojson;*.trilist|"
108 "Point Cloud (*.ply;*.bin;*.ptx;*.las;*.laz;*.xyz;*.bxyz;*.vg;*.bvg;*.ptx)|"
109 "*.ply;*.bin;*.ptx;*.las;*.laz;*.xyz;*.bxyz;*.vg;*.bvg;*.ptx|"
110 "Polyhedral Mesh (*.plm;*.pm;*.mesh)|"
112 "Graph (*.ply)|*.ply",
115 if (!filename.IsEmpty()) {
116 auto model = viewer_->add_model(filename.ToStdString(),
true);
118 viewer_->fit_screen(model);
123 void Window::menuFileSave(wxCommandEvent & WXUNUSED(event)) {
124 const Model *m = viewer_->current_model();
126 LOG(WARNING) <<
"no model exists";
130 std::string name = m->name();
134 const std::string &title =
"Please specify a file name";
138 wxString filename = wxFileSelector(title, default_path, simple_name, extension,
139 "Surface Mesh (*.ply;*.obj;*.off;*.stl;*.sm)|"
140 "*.ply;*.obj;*.off;*.stl;*.sm|"
141 "Point Cloud (*.ply;*.bin;*.las;*.laz;*.xyz;*.bxyz;*.vg;*.bvg)|"
142 "*.ply;*.bin;*.las;*.laz;*.xyz;*.bxyz;*.vg;*.bvg|"
143 "Polyhedral Mesh (*.plm;*.pm;*.mesh)|"
145 "Graph (*.ply)|*.ply",
148 if (!filename.IsEmpty())
149 viewer_->save_current_model(filename.ToStdString());
153 void Window::menuViewSnapshot(wxCommandEvent &event) {
154 const std::string &title =
"Please specify an image file name";
155 std::string name(
"untitled.png");
157 if (viewer_->current_model())
163 wxString filename = wxFileSelector(title, default_path, simple_name, extension,
164 "Image Files (*.png;*.jpg;*.bmp;*.ppm;*.tga)|"
165 "*.png;*.jpg;*.bmp;*.ppm;*.tga",
168 if (!filename.IsEmpty())
169 viewer_->snapshot(filename.ToStdString(),
true);
173 void Window::menuFileExit(wxCommandEvent & WXUNUSED(event)) {
179 void Window::menuViewFitScreen(wxCommandEvent &) {
180 viewer_->fit_screen();
184 void Window::menuEditSubdivision(wxCommandEvent &) {
185 auto mesh =
dynamic_cast<SurfaceMesh*
>(viewer_->current_model());
187 LOG(WARNING) <<
"current model is not a SurfaceMesh (or model does not exist)";
192 mesh->renderer()->update();
197 void Window::menuHelpAbout(wxCommandEvent & WXUNUSED(event)) {
198 wxMessageBox(
"Easy3D viewer based on wxWidgets");
static bool loop(SurfaceMesh *mesh)
The Loop subdivision.
Definition surface_mesh_subdivision.cpp:179
std::string parent_directory(const std::string &path)
Query the parent path from full name of a file or directory (e.g., /a/b/c.Ext => /a/b)
std::string replace_extension(std::string const &path, const std::string &ext)
Replaces the extension of the given file with 'ext'. If the file name does not have an extension,...