Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/pstack/graphics/global.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,8 @@ void draw_triangles(GLsizei count) {
glDrawArrays(GL_TRIANGLES, 0, count);
}

void draw_lines(GLsizei count) {
glDrawArrays(GL_LINES, 0, count);
}

} // namespace pstack::graphics
2 changes: 2 additions & 0 deletions src/pstack/graphics/global.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ void clear(float r, float g, float b, float a);

void draw_triangles(int count);

void draw_lines(int count);

} // namespace pstack::graphics

#endif // PSTACK_GRAPHICS_GLOBAL_HPP
4 changes: 0 additions & 4 deletions src/pstack/gui/controls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,6 @@ void controls::initialize(main_window* parent) {
rotation_dropdown->Disable();
preview_voxelization_button = new wxButton(panel, wxID_ANY, "Preview voxelization");
preview_voxelization_button->Disable();
preview_bounding_box_button = new wxButton(panel, wxID_ANY, "Preview bounding box");
preview_bounding_box_button->Disable();

const wxString quantity_tooltip = "How many copies of the selected parts should be included in the stacking";
const wxString min_hole_tooltip =
Expand All @@ -97,7 +95,6 @@ void controls::initialize(main_window* parent) {
"Cubic = The parts will be rotated by some multiple of 90 degrees from their starting orientations.\n\n"
"Arbitrary = The parts will be oriented in one of 32 random possible rotations. The rotations are constant for the duration of the application, and will be re-randomized next time the application is launched.";
const wxString preview_voxelization_tooltip = "*NOT YET IMPLEMENTED*\nShows a preview of the voxelization. Used to check if there are any open holes into the internal volume of the part.";
const wxString preview_bounding_box_tooltip = "*NOT YET IMPLEMENTED*\nShows a preview of the bounding box. Used to check the part's orientation.";

quantity_text->SetToolTip(quantity_tooltip);
min_hole_text->SetToolTip(min_hole_tooltip);
Expand All @@ -108,7 +105,6 @@ void controls::initialize(main_window* parent) {
rotation_text->SetToolTip(rotation_tooltip);
rotation_dropdown->SetToolTip(rotation_tooltip);
preview_voxelization_button->SetToolTip(preview_voxelization_tooltip);
preview_bounding_box_button->SetToolTip(preview_bounding_box_tooltip);
}

{
Expand Down
1 change: 0 additions & 1 deletion src/pstack/gui/controls.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ struct controls {
wxStaticText* rotation_text;
wxChoice* rotation_dropdown;
wxButton* preview_voxelization_button;
wxButton* preview_bounding_box_button;

// Stack settings tab
wxStaticText* initial_x_text;
Expand Down
13 changes: 7 additions & 6 deletions src/pstack/gui/main_window.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,6 @@ void main_window::enable_part_settings(bool enable) {
_controls.minimize_checkbox->Enable(enable);
_controls.rotation_dropdown->Enable(enable);
_controls.preview_voxelization_button->Enable(enable);
_controls.preview_bounding_box_button->Enable(enable);
}

void main_window::on_select_results(const std::vector<std::size_t>& indices) {
Expand Down Expand Up @@ -295,7 +294,7 @@ wxMenuBar* main_window::make_menu_bar() {
// Menu items cannot be 0 on Mac
new_ = 1, open, save, close,
import, export_,
pref_scroll, pref_extra,
pref_scroll, pref_extra, pref_bounding_box,
about, website, issue,
};
menu_bar->Bind(wxEVT_MENU, [this](wxCommandEvent& event) {
Expand Down Expand Up @@ -332,6 +331,11 @@ wxMenuBar* main_window::make_menu_bar() {
_parts_list.reload_all_text();
break;
}
case menu_item::pref_bounding_box: {
_preferences.show_bounding_box = not _preferences.show_bounding_box;
_viewport->show_bounding_box(_preferences.show_bounding_box);
break;
}
case menu_item::about: {
constexpr auto str =
"PartStacker Community Edition\n\n"
Expand Down Expand Up @@ -368,6 +372,7 @@ wxMenuBar* main_window::make_menu_bar() {
auto preferences_menu = new wxMenu();
preferences_menu->AppendCheckItem((int)menu_item::pref_scroll, "Invert &scroll", "Change the viewport scroll direction");
preferences_menu->AppendCheckItem((int)menu_item::pref_extra, "Display &extra parts", "Display the quantity of extra parts separately");
preferences_menu->AppendCheckItem((int)menu_item::pref_bounding_box, "Show &bounding box", "Show the bounding box around the currently displayed mesh");
menu_bar->Append(preferences_menu, "&Preferences");

auto help_menu = new wxMenu();
Expand Down Expand Up @@ -449,9 +454,6 @@ void main_window::bind_all_controls() {
_controls.preview_voxelization_button->Bind(wxEVT_BUTTON, [this](wxCommandEvent& event) {
wxMessageBox("Not yet implemented", "Error", wxICON_WARNING);
});
_controls.preview_bounding_box_button->Bind(wxEVT_BUTTON, [this](wxCommandEvent& event) {
wxMessageBox("Not yet implemented", "Error", wxICON_WARNING);
});

_controls.section_view_checkbox->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent& event) {
wxMessageBox("Not yet implemented", "Error", wxICON_WARNING);
Expand Down Expand Up @@ -696,7 +698,6 @@ void main_window::arrange_tab_part_settings(wxPanel* panel) {
rotation_sizer->Add(_controls.rotation_dropdown, 0, wxALIGN_CENTER_VERTICAL);
button_sizer->Add(rotation_sizer);
button_sizer->Add(_controls.preview_voxelization_button);
button_sizer->Add(_controls.preview_bounding_box_button);

lower_sizer->Add(label_sizer, 0, wxEXPAND);
lower_sizer->AddSpacer(panel->FromDIP(3 * constants::inner_border));
Expand Down
1 change: 1 addition & 0 deletions src/pstack/gui/preferences.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ namespace pstack::gui {
struct preferences {
bool invert_scroll = false;
bool extra_parts = false;
bool show_bounding_box = false;
};

} // namespace pstack::gui
Expand Down
136 changes: 97 additions & 39 deletions src/pstack/gui/viewport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ viewport::viewport(main_window* parent, const wxGLAttributes& canvasAttrs)
Bind(wxEVT_MOUSEWHEEL, &viewport::on_scroll, this);
}

constexpr auto vertex_shader_source = R"(
constexpr auto mesh_vertex_shader_source = R"(
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
Expand All @@ -41,7 +41,7 @@ constexpr auto vertex_shader_source = R"(
}
)";

constexpr auto fragment_shader_source = R"(
constexpr auto mesh_fragment_shader_source = R"(
#version 330 core
in vec4 frag_normal;
out vec4 frag_colour;
Expand All @@ -54,6 +54,23 @@ constexpr auto fragment_shader_source = R"(
}
)";

constexpr auto bounding_box_vertex_shader_source = R"(
#version 330 core
layout (location = 0) in vec3 aPos;
uniform mat4 transform_vertices;
void main() {
gl_Position = transform_vertices * vec4(aPos, 1.0);
}
)";

constexpr auto bounding_box_fragment_shader_source = R"(
#version 330 core
out vec4 frag_colour;
void main() {
frag_colour = vec4(224.0 / 255.0, 110.0 / 255.0, 0.0, 1.0);
}
)";

bool viewport::initialize() {
if (!_opengl_context) {
return false;
Expand All @@ -69,64 +86,101 @@ bool viewport::initialize() {
return false;
}

if (const auto err = _shader.initialize(vertex_shader_source, fragment_shader_source);
if (const auto err = _mesh_shader.initialize(mesh_vertex_shader_source, mesh_fragment_shader_source);
not err.has_value())
{
wxMessageBox(wxString::Format("Error in creating OpenGL shader.\n%s", err.error()),
"OpenGL shader error", wxOK | wxICON_ERROR, this);
wxMessageBox(wxString::Format("Error in creating OpenGL shader for the mesh.\n%s", err.error()),
"OpenGL mesh shader error", wxOK | wxICON_ERROR, this);
return false;
}

_vao.initialize();
if (const auto err = _bounding_box_shader.initialize(bounding_box_vertex_shader_source, bounding_box_fragment_shader_source);
not err.has_value())
{
wxMessageBox(wxString::Format("Error in creating OpenGL shader for the bounding box.\n%s", err.error()),
"OpenGL line shader error", wxOK | wxICON_ERROR, this);
return false;
}

_mesh_vao.initialize();
_bounding_box_vao.initialize();
remove_mesh();

return true;
}

void viewport::set_mesh(const calc::mesh& mesh, const geo::point3<float>& centroid) {
_vao.clear();

using vector3 = geo::vector3<float>;

std::vector<vector3> vertices;
std::vector<vector3> normals;
for (const auto& t : mesh.triangles()) {
vertices.push_back(t.v1.as_vector());
vertices.push_back(t.v2.as_vector());
vertices.push_back(t.v3.as_vector());
normals.push_back(t.normal);
normals.push_back(t.normal);
normals.push_back(t.normal);
}
const auto bounding = mesh.bounding();

_vao.add_vertex_buffer(0, std::move(vertices));
_vao.add_vertex_buffer(1, std::move(normals));
set_mesh_vao(mesh);
set_bounding_box_vao(bounding.min, bounding.max);

const auto bounding = mesh.bounding();
_transform.translation(geo::origin3<float> - centroid);
const auto size = bounding.max - bounding.min;
const auto zoom_factor = 1 / std::max({ size.x, size.y, size.z });
_transform.scale_mesh(zoom_factor);
_shader.set_uniform("transform_vertices", _transform.for_vertices());
_shader.set_uniform("transform_normals", _transform.for_normals());

render();
}

void viewport::remove_mesh() {
_vao.clear();
_mesh_vao.clear();
_mesh_vao.add_vertex_buffer(0, {});
_mesh_vao.add_vertex_buffer(1, {});

_vao.add_vertex_buffer(0, {});
_vao.add_vertex_buffer(1, {});
_bounding_box_vao.clear();
_bounding_box_vao.add_vertex_buffer(0, {});

_transform.translation({ 0, 0, 0 });
_transform.scale_mesh(1);
_shader.set_uniform("transform_vertices", _transform.for_vertices());
_shader.set_uniform("transform_normals", _transform.for_normals());

render();
}

void viewport::set_mesh_vao(const calc::mesh& mesh) {
_mesh_vao.clear();
std::vector<geo::vector3<float>> vertices;
std::vector<geo::vector3<float>> normals;
for (const auto& t : mesh.triangles()) {
vertices.push_back(t.v1.as_vector());
vertices.push_back(t.v2.as_vector());
vertices.push_back(t.v3.as_vector());
normals.push_back(t.normal);
normals.push_back(t.normal);
normals.push_back(t.normal);
}
_mesh_vao.add_vertex_buffer(0, std::move(vertices));
_mesh_vao.add_vertex_buffer(1, std::move(normals));
}

void viewport::set_bounding_box_vao(const geo::point3<float> min, const geo::point3<float> max) {
_bounding_box_vao.clear();
std::vector<geo::vector3<float>> vertices{
{ min.x, min.y, min.z },
{ min.x, min.y, max.z },
{ min.x, max.y, min.z },
{ min.x, max.y, max.z },
{ max.x, min.y, min.z },
{ max.x, min.y, max.z },
{ max.x, max.y, min.z },
{ max.x, max.y, max.z },
};
_bounding_box_vao.add_vertex_buffer(0, std::vector{
vertices[0], vertices[1],
vertices[2], vertices[3],
vertices[4], vertices[5],
vertices[6], vertices[7],
vertices[0], vertices[2],
vertices[1], vertices[3],
vertices[4], vertices[6],
vertices[5], vertices[7],
vertices[0], vertices[4],
vertices[1], vertices[5],
vertices[2], vertices[6],
vertices[3], vertices[7],
});
}

void viewport::render() {
wxClientDC dc(this);
render(dc);
Expand All @@ -140,9 +194,19 @@ void viewport::render(wxDC& dc) {
SetCurrent(*_opengl_context);

graphics::clear(40 / 255.0, 50 / 255.0, 120 / 255.0, 1);
_shader.use_program();
_vao.bind_arrays();
graphics::draw_triangles(_vao[0].size());

_mesh_shader.use_program();
_mesh_shader.set_uniform("transform_vertices", _transform.for_vertices());
_mesh_shader.set_uniform("transform_normals", _transform.for_normals());
_mesh_vao.bind_arrays();
graphics::draw_triangles(_mesh_vao[0].size());

if (_show_bounding_box) {
_bounding_box_shader.use_program();
_bounding_box_shader.set_uniform("transform_vertices", _transform.for_vertices());
_bounding_box_vao.bind_arrays();
graphics::draw_lines(_bounding_box_vao[0].size());
}

SwapBuffers();
}
Expand All @@ -164,8 +228,6 @@ void viewport::on_size(wxSizeEvent& event) {

static constexpr float scale_baseline = 866;
_transform.scale_screen(scale_baseline / _viewport_size.x, scale_baseline / _viewport_size.y);
_shader.set_uniform("transform_vertices", _transform.for_vertices());
_shader.set_uniform("transform_normals", _transform.for_normals());

if (first_appearance) {
render();
Expand Down Expand Up @@ -200,17 +262,13 @@ void viewport::on_scroll(wxMouseEvent& evt) {
const double zoom_amount = ((double)evt.GetWheelRotation() / (double)evt.GetWheelDelta()) / 4;
const float zoom_factor = (float)std::pow(2.0, _scroll_direction * zoom_amount);
_transform.zoom_by(zoom_factor);
_shader.set_uniform("transform_vertices", _transform.for_vertices());
_shader.set_uniform("transform_normals", _transform.for_normals());
render();
}

void viewport::on_move_by(wxPoint position) {
const auto [dx, dy] = _cached_position - position;
_cached_position = position;
_transform.rotate_by((float)dy / 256, (float)dx / 256);
_shader.set_uniform("transform_vertices", _transform.for_vertices());
_shader.set_uniform("transform_normals", _transform.for_normals());
render();
}

Expand Down
19 changes: 17 additions & 2 deletions src/pstack/gui/viewport.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,21 @@ class viewport : public wxGLCanvas {
public:
void set_mesh(const calc::mesh& mesh, const geo::point3<float>& centroid);
void remove_mesh();

private:
void set_mesh_vao(const calc::mesh& mesh);
void set_bounding_box_vao(geo::point3<float> min, geo::point3<float> max);

public:
void render();

void scroll_direction(bool invert_scroll) {
_scroll_direction = invert_scroll ? -1 : 1;
}
void show_bounding_box(bool show) {
_show_bounding_box = show;
render();
}

private:
void render(wxDC& dc);
Expand All @@ -51,8 +61,13 @@ class viewport : public wxGLCanvas {
std::unique_ptr<wxGLContext> _opengl_context = nullptr;
bool _opengl_initialized = false;

graphics::shader _shader{};
graphics::vertex_array_object _vao{};
graphics::shader _mesh_shader{};
graphics::vertex_array_object _mesh_vao{};

graphics::shader _bounding_box_shader{};
graphics::vertex_array_object _bounding_box_vao{};
bool _show_bounding_box = false;

transformation _transform{};

wxSize _viewport_size{};
Expand Down