diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index b48ab0f97..8a9d13aae 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -85,9 +85,13 @@ sub new { # Initialize handlers for canvases my $on_select_object = sub { - my ($obj_idx) = @_; - # Ignore the special objects (the wipe tower proxy and such). - $self->select_object((defined($obj_idx) && $obj_idx >= 0 && $obj_idx < 1000) ? $obj_idx : undef); + my ($obj_idx, $vol_idx) = @_; + + if (($obj_idx != -1) && ($vol_idx == -1)) { + # Ignore the special objects (the wipe tower proxy and such). + $self->select_object((defined($obj_idx) && $obj_idx >= 0 && $obj_idx < 1000) ? $obj_idx : undef); + $self->item_changed_selection($obj_idx) if (defined($obj_idx)); + } }; my $on_double_click = sub { $self->object_settings_dialog if $self->selected_object; @@ -217,6 +221,29 @@ sub new { my $state = Slic3r::GUI::_3DScene::is_toolbar_item_pressed($self->{canvas3D}, "layersediting"); $self->on_layer_editing_toggled($state); }; + + my $on_action_selectbyparts = sub { + my $curr = Slic3r::GUI::_3DScene::get_select_by($self->{canvas3D}); + if ($curr eq 'volume') { + Slic3r::GUI::_3DScene::set_select_by($self->{canvas3D}, 'object'); + my $selections = $self->collect_selections; + Slic3r::GUI::_3DScene::set_objects_selections($self->{canvas3D}, \@$selections); + Slic3r::GUI::_3DScene::reload_scene($self->{canvas3D}, 1); + } + elsif ($curr eq 'object') { + Slic3r::GUI::_3DScene::set_select_by($self->{canvas3D}, 'volume'); + my $selections = []; + Slic3r::GUI::_3DScene::set_objects_selections($self->{canvas3D}, \@$selections); + Slic3r::GUI::_3DScene::deselect_volumes($self->{canvas3D}); + Slic3r::GUI::_3DScene::reload_scene($self->{canvas3D}, 1); + + my ($obj_idx, $object) = $self->selected_object; + if (defined $obj_idx) { + my $vol_idx = Slic3r::GUI::_3DScene::get_first_volume_id($self->{canvas3D}, $obj_idx); + Slic3r::GUI::_3DScene::select_volume($self->{canvas3D}, $vol_idx) if ($vol_idx != -1); + } + } + }; # Initialize 3D plater if ($Slic3r::GUI::have_OpenGL) { @@ -247,6 +274,7 @@ sub new { Slic3r::GUI::_3DScene::register_action_cut_callback($self->{canvas3D}, $on_action_cut); Slic3r::GUI::_3DScene::register_action_settings_callback($self->{canvas3D}, $on_action_settings); Slic3r::GUI::_3DScene::register_action_layersediting_callback($self->{canvas3D}, $on_action_layersediting); + Slic3r::GUI::_3DScene::register_action_selectbyparts_callback($self->{canvas3D}, $on_action_selectbyparts); Slic3r::GUI::_3DScene::enable_gizmos($self->{canvas3D}, 1); Slic3r::GUI::_3DScene::enable_toolbar($self->{canvas3D}, 1); Slic3r::GUI::_3DScene::enable_shader($self->{canvas3D}, 1); @@ -2331,12 +2359,24 @@ sub selection_changed { } Slic3r::GUI::_3DScene::enable_toolbar_item($self->{canvas3D}, "layersediting", $layers_height_allowed); + + my $can_select_by_parts = 0; if ($have_sel) { my $model_object = $self->{model}->objects->[$obj_idx]; + $can_select_by_parts = ($obj_idx >= 0) && ($obj_idx < 1000) && ($model_object->volumes_count > 1); Slic3r::GUI::_3DScene::enable_toolbar_item($self->{canvas3D}, "fewer", $model_object->instances_count > 1); } + if ($can_select_by_parts) { + # first disable to let the item in the toolbar to switch to the unpressed state + Slic3r::GUI::_3DScene::enable_toolbar_item($self->{canvas3D}, "selectbyparts", 0); + Slic3r::GUI::_3DScene::enable_toolbar_item($self->{canvas3D}, "selectbyparts", 1); + } else { + Slic3r::GUI::_3DScene::enable_toolbar_item($self->{canvas3D}, "selectbyparts", 0); + Slic3r::GUI::_3DScene::set_select_by($self->{canvas3D}, 'object'); + } + if ($self->{object_info_size}) { # have we already loaded the info pane? if ($have_sel) { my $model_object = $self->{model}->objects->[$obj_idx]; diff --git a/resources/icons/toolbar.png b/resources/icons/toolbar.png index ce954143d..79246bcf9 100644 Binary files a/resources/icons/toolbar.png and b/resources/icons/toolbar.png differ diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index 3f44b727e..2af4dda06 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -303,6 +303,16 @@ void GLVolume::set_convex_hull(const TriangleMesh& convex_hull) m_convex_hull = &convex_hull; } +void GLVolume::set_select_group_id(const std::string& select_by) +{ + if (select_by == "object") + select_group_id = object_idx() * 1000000; + else if (select_by == "volume") + select_group_id = object_idx() * 1000000 + volume_idx() * 1000; + else if (select_by == "instance") + select_group_id = composite_id; +} + const Transform3f& GLVolume::world_matrix() const { if (m_world_matrix_dirty) @@ -655,12 +665,7 @@ std::vector GLVolumeCollection::load_object( v.bounding_box = v.indexed_vertex_array.bounding_box(); v.indexed_vertex_array.finalize_geometry(use_VBOs); v.composite_id = obj_idx * 1000000 + volume_idx * 1000 + instance_idx; - if (select_by == "object") - v.select_group_id = obj_idx * 1000000; - else if (select_by == "volume") - v.select_group_id = obj_idx * 1000000 + volume_idx * 1000; - else if (select_by == "instance") - v.select_group_id = v.composite_id; + v.set_select_group_id(select_by); if (drag_by == "object") v.drag_group_id = obj_idx * 1000; else if (drag_by == "instance") @@ -949,6 +954,15 @@ void GLVolumeCollection::update_colors_by_extruder(const DynamicPrintConfig* con } } +void GLVolumeCollection::set_select_by(const std::string& select_by) +{ + for (GLVolume *vol : this->volumes) + { + if (vol != nullptr) + vol->set_select_group_id(select_by); + } +} + std::vector GLVolumeCollection::get_current_print_zs(bool active_only) const { // Collect layer top positions of all volumes. @@ -1772,12 +1786,12 @@ void _3DScene::set_model(wxGLCanvas* canvas, Model* model) void _3DScene::set_bed_shape(wxGLCanvas* canvas, const Pointfs& shape) { - return s_canvas_mgr.set_bed_shape(canvas, shape); + s_canvas_mgr.set_bed_shape(canvas, shape); } void _3DScene::set_auto_bed_shape(wxGLCanvas* canvas) { - return s_canvas_mgr.set_auto_bed_shape(canvas); + s_canvas_mgr.set_auto_bed_shape(canvas); } BoundingBoxf3 _3DScene::get_volumes_bounding_box(wxGLCanvas* canvas) @@ -1792,22 +1806,27 @@ void _3DScene::set_axes_length(wxGLCanvas* canvas, float length) void _3DScene::set_cutting_plane(wxGLCanvas* canvas, float z, const ExPolygons& polygons) { - return s_canvas_mgr.set_cutting_plane(canvas, z, polygons); + s_canvas_mgr.set_cutting_plane(canvas, z, polygons); } void _3DScene::set_color_by(wxGLCanvas* canvas, const std::string& value) { - return s_canvas_mgr.set_color_by(canvas, value); + s_canvas_mgr.set_color_by(canvas, value); } void _3DScene::set_select_by(wxGLCanvas* canvas, const std::string& value) { - return s_canvas_mgr.set_select_by(canvas, value); + s_canvas_mgr.set_select_by(canvas, value); } void _3DScene::set_drag_by(wxGLCanvas* canvas, const std::string& value) { - return s_canvas_mgr.set_drag_by(canvas, value); + s_canvas_mgr.set_drag_by(canvas, value); +} + +std::string _3DScene::get_select_by(wxGLCanvas* canvas) +{ + return s_canvas_mgr.get_select_by(canvas); } bool _3DScene::is_layers_editing_enabled(wxGLCanvas* canvas) @@ -2080,6 +2099,11 @@ void _3DScene::register_action_layersediting_callback(wxGLCanvas* canvas, void* s_canvas_mgr.register_action_layersediting_callback(canvas, callback); } +void _3DScene::register_action_selectbyparts_callback(wxGLCanvas* canvas, void* callback) +{ + s_canvas_mgr.register_action_selectbyparts_callback(canvas, callback); +} + static inline int hex_digit_to_int(const char c) { return @@ -2117,6 +2141,11 @@ std::vector _3DScene::load_object(wxGLCanvas* canvas, const Model* model, i return s_canvas_mgr.load_object(canvas, model, obj_idx); } +int _3DScene::get_first_volume_id(wxGLCanvas* canvas, int obj_idx) +{ + return s_canvas_mgr.get_first_volume_id(canvas, obj_idx); +} + void _3DScene::reload_scene(wxGLCanvas* canvas, bool force) { s_canvas_mgr.reload_scene(canvas, force); diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index 1785ecf4a..ec9d1a501 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -337,6 +337,8 @@ public: void set_convex_hull(const TriangleMesh& convex_hull); + void set_select_group_id(const std::string& select_by); + int object_idx() const { return this->composite_id / 1000000; } int volume_idx() const { return (this->composite_id / 1000) % 1000; } int instance_idx() const { return this->composite_id % 1000; } @@ -446,6 +448,8 @@ public: void update_colors_by_extruder(const DynamicPrintConfig* config); + void set_select_by(const std::string& select_by); + // Returns a vector containing the sorted list of all the print_zs of the volumes contained in this collection std::vector get_current_print_zs(bool active_only) const; @@ -499,6 +503,8 @@ public: static void set_select_by(wxGLCanvas* canvas, const std::string& value); static void set_drag_by(wxGLCanvas* canvas, const std::string& value); + static std::string get_select_by(wxGLCanvas* canvas); + static bool is_layers_editing_enabled(wxGLCanvas* canvas); static bool is_layers_editing_allowed(wxGLCanvas* canvas); static bool is_shader_enabled(wxGLCanvas* canvas); @@ -562,10 +568,13 @@ public: static void register_action_cut_callback(wxGLCanvas* canvas, void* callback); static void register_action_settings_callback(wxGLCanvas* canvas, void* callback); static void register_action_layersediting_callback(wxGLCanvas* canvas, void* callback); + static void register_action_selectbyparts_callback(wxGLCanvas* canvas, void* callback); static std::vector load_object(wxGLCanvas* canvas, const ModelObject* model_object, int obj_idx, std::vector instance_idxs); static std::vector load_object(wxGLCanvas* canvas, const Model* model, int obj_idx); + static int get_first_volume_id(wxGLCanvas* canvas, int obj_idx); + static void reload_scene(wxGLCanvas* canvas, bool force); static void load_gcode_preview(wxGLCanvas* canvas, const GCodePreviewData* preview_data, const std::vector& str_tool_colors); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index 2bec6b66b..56bfb8f37 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -2018,6 +2018,9 @@ void GLCanvas3D::update_volumes_selection(const std::vector& selections) if (m_model == nullptr) return; + if (selections.empty()) + return; + for (unsigned int obj_idx = 0; obj_idx < (unsigned int)m_model->objects.size(); ++obj_idx) { if ((selections[obj_idx] == 1) && (obj_idx < (unsigned int)m_objects_volumes_idxs.size())) @@ -2144,6 +2147,7 @@ void GLCanvas3D::set_color_by(const std::string& value) void GLCanvas3D::set_select_by(const std::string& value) { m_select_by = value; + m_volumes.set_select_by(value); } void GLCanvas3D::set_drag_by(const std::string& value) @@ -2151,6 +2155,11 @@ void GLCanvas3D::set_drag_by(const std::string& value) m_drag_by = value; } +const std::string& GLCanvas3D::get_select_by() const +{ + return m_select_by; +} + float GLCanvas3D::get_camera_zoom() const { return m_camera.zoom; @@ -2432,6 +2441,17 @@ std::vector GLCanvas3D::load_object(const Model& model, int obj_idx) return std::vector(); } +int GLCanvas3D::get_first_volume_id(int obj_idx) const +{ + for (int i = 0; i < (int)m_volumes.volumes.size(); ++i) + { + if ((m_volumes.volumes[i] != nullptr) && (m_volumes.volumes[i]->object_idx() == obj_idx)) + return i; + } + + return -1; +} + void GLCanvas3D::reload_scene(bool force) { if ((m_canvas == nullptr) || (m_config == nullptr) || (m_model == nullptr)) @@ -2757,6 +2777,12 @@ void GLCanvas3D::register_action_layersediting_callback(void* callback) m_action_layersediting_callback.register_callback(callback); } +void GLCanvas3D::register_action_selectbyparts_callback(void* callback) +{ + if (callback != nullptr) + m_action_selectbyparts_callback.register_callback(callback); +} + void GLCanvas3D::bind_event_handlers() { if (m_canvas != nullptr) @@ -3043,7 +3069,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) // propagate event through callback if (m_picking_enabled && (volume_idx != -1)) - _on_select(volume_idx); + _on_select(volume_idx, selected_object_idx); if (volume_idx != -1) { @@ -3287,7 +3313,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) if (m_picking_enabled && !m_toolbar_action_running) { deselect_volumes(); - _on_select(-1); + _on_select(-1, -1); update_gizmos_data(); } } @@ -3512,6 +3538,17 @@ bool GLCanvas3D::_init_toolbar() if (!m_toolbar.add_item(item)) return false; + if (!m_toolbar.add_separator()) + return false; + + item.name = "selectbyparts"; + item.tooltip = GUI::L_str("Select by parts"); + item.sprite_id = 10; + item.is_toggable = true; + item.action_callback = &m_action_selectbyparts_callback; + if (!m_toolbar.add_item(item)) + return false; + enable_toolbar_item("add", true); return true; @@ -3751,6 +3788,7 @@ void GLCanvas3D::_deregister_callbacks() m_action_cut_callback.deregister_callback(); m_action_settings_callback.deregister_callback(); m_action_layersediting_callback.deregister_callback(); + m_action_selectbyparts_callback.deregister_callback(); } void GLCanvas3D::_mark_volumes_for_layer_height() const @@ -5262,17 +5300,35 @@ void GLCanvas3D::_on_move(const std::vector& volume_idxs) m_on_wipe_tower_moved_callback.call(wipe_tower_origin(0), wipe_tower_origin(1)); } -void GLCanvas3D::_on_select(int volume_idx) +void GLCanvas3D::_on_select(int volume_idx, int object_idx) { - int id = -1; + int vol_id = -1; + int obj_id = -1; + if ((volume_idx != -1) && (volume_idx < (int)m_volumes.volumes.size())) { if (m_select_by == "volume") - id = m_volumes.volumes[volume_idx]->volume_idx(); + { + if (m_volumes.volumes[volume_idx]->object_idx() != object_idx) + { + set_select_by("object"); + obj_id = m_volumes.volumes[volume_idx]->object_idx(); + vol_id = -1; + } + else + { + obj_id = object_idx; + vol_id = m_volumes.volumes[volume_idx]->volume_idx(); + } + } else if (m_select_by == "object") - id = m_volumes.volumes[volume_idx]->object_idx(); + { + obj_id = m_volumes.volumes[volume_idx]->object_idx(); + vol_id = -1; + } } - m_on_select_object_callback.call(id); + + m_on_select_object_callback.call(obj_id, vol_id); } std::vector GLCanvas3D::_parse_colors(const std::vector& colors) diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index 2969d12c6..541495eec 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -514,6 +514,7 @@ private: PerlCallback m_action_cut_callback; PerlCallback m_action_settings_callback; PerlCallback m_action_layersediting_callback; + PerlCallback m_action_selectbyparts_callback; public: GLCanvas3D(wxGLCanvas* canvas); @@ -556,6 +557,8 @@ public: void set_select_by(const std::string& value); void set_drag_by(const std::string& value); + const std::string& get_select_by() const; + float get_camera_zoom() const; BoundingBoxf3 volumes_bounding_box() const; @@ -597,6 +600,8 @@ public: std::vector load_object(const ModelObject& model_object, int obj_idx, std::vector instance_idxs); std::vector load_object(const Model& model, int obj_idx); + int get_first_volume_id(int obj_idx) const; + void reload_scene(bool force); void load_gcode_preview(const GCodePreviewData& preview_data, const std::vector& str_tool_colors); @@ -631,6 +636,7 @@ public: void register_action_cut_callback(void* callback); void register_action_settings_callback(void* callback); void register_action_layersediting_callback(void* callback); + void register_action_selectbyparts_callback(void* callback); void bind_event_handlers(); void unbind_event_handlers(); @@ -733,7 +739,7 @@ private: void _show_warning_texture_if_needed(); void _on_move(const std::vector& volume_idxs); - void _on_select(int volume_idx); + void _on_select(int volume_idx, int object_idx); // generates the legend texture in dependence of the current shown view type void _generate_legend_texture(const GCodePreviewData& preview_data, const std::vector& tool_colors); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index 57a49d7ab..a09d83b89 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -338,6 +338,12 @@ void GLCanvas3DManager::set_drag_by(wxGLCanvas* canvas, const std::string& value it->second->set_drag_by(value); } +std::string GLCanvas3DManager::get_select_by(wxGLCanvas* canvas) const +{ + CanvasesMap::const_iterator it = _get_canvas(canvas); + return (it != m_canvases.end()) ? it->second->get_select_by() : ""; +} + bool GLCanvas3DManager::is_layers_editing_enabled(wxGLCanvas* canvas) const { CanvasesMap::const_iterator it = _get_canvas(canvas); @@ -536,6 +542,12 @@ std::vector GLCanvas3DManager::load_object(wxGLCanvas* canvas, const Model* return (it != m_canvases.end()) ? it->second->load_object(*model, obj_idx) : std::vector(); } +int GLCanvas3DManager::get_first_volume_id(wxGLCanvas* canvas, int obj_idx) const +{ + CanvasesMap::const_iterator it = _get_canvas(canvas); + return (it != m_canvases.end()) ? it->second->get_first_volume_id(obj_idx) : -1; +} + void GLCanvas3DManager::reload_scene(wxGLCanvas* canvas, bool force) { CanvasesMap::iterator it = _get_canvas(canvas); @@ -765,6 +777,13 @@ void GLCanvas3DManager::register_action_layersediting_callback(wxGLCanvas* canva it->second->register_action_layersediting_callback(callback); } +void GLCanvas3DManager::register_action_selectbyparts_callback(wxGLCanvas* canvas, void* callback) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->register_action_selectbyparts_callback(callback); +} + GLCanvas3DManager::CanvasesMap::iterator GLCanvas3DManager::_get_canvas(wxGLCanvas* canvas) { return (canvas == nullptr) ? m_canvases.end() : m_canvases.find(canvas); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp index f7705a56c..1c715a9a3 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -98,6 +98,8 @@ public: void set_select_by(wxGLCanvas* canvas, const std::string& value); void set_drag_by(wxGLCanvas* canvas, const std::string& value); + std::string get_select_by(wxGLCanvas* canvas) const; + bool is_layers_editing_enabled(wxGLCanvas* canvas) const; bool is_layers_editing_allowed(wxGLCanvas* canvas) const; bool is_shader_enabled(wxGLCanvas* canvas) const; @@ -135,6 +137,8 @@ public: std::vector load_object(wxGLCanvas* canvas, const ModelObject* model_object, int obj_idx, std::vector instance_idxs); std::vector load_object(wxGLCanvas* canvas, const Model* model, int obj_idx); + int get_first_volume_id(wxGLCanvas* canvas, int obj_idx) const; + void reload_scene(wxGLCanvas* canvas, bool force); void load_gcode_preview(wxGLCanvas* canvas, const GCodePreviewData* preview_data, const std::vector& str_tool_colors); @@ -171,6 +175,7 @@ public: void register_action_cut_callback(wxGLCanvas* canvas, void* callback); void register_action_settings_callback(wxGLCanvas* canvas, void* callback); void register_action_layersediting_callback(wxGLCanvas* canvas, void* callback); + void register_action_selectbyparts_callback(wxGLCanvas* canvas, void* callback); private: CanvasesMap::iterator _get_canvas(wxGLCanvas* canvas); diff --git a/xs/src/slic3r/GUI/GUI_ObjectParts.cpp b/xs/src/slic3r/GUI/GUI_ObjectParts.cpp index 9ec39b976..af57db8ed 100644 --- a/xs/src/slic3r/GUI/GUI_ObjectParts.cpp +++ b/xs/src/slic3r/GUI/GUI_ObjectParts.cpp @@ -1572,7 +1572,6 @@ void update_position_values() void update_position_values(const Vec3d& position) { auto og = get_optgroup(ogFrequentlyObjectSettings); - auto instance = (*m_objects)[m_selected_object_id]->instances.front(); og->set_value("position_x", int(position(0))); og->set_value("position_y", int(position(1))); diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index 6c199aacb..756f9e547 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -337,6 +337,14 @@ set_drag_by(canvas, value) CODE: _3DScene::set_drag_by((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), value); +std::string +get_select_by(canvas) + SV *canvas; + CODE: + RETVAL = _3DScene::get_select_by((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); + OUTPUT: + RETVAL + bool is_layers_editing_enabled(canvas) SV *canvas; @@ -720,6 +728,13 @@ register_action_layersediting_callback(canvas, callback) CODE: _3DScene::register_action_layersediting_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); +void +register_action_selectbyparts_callback(canvas, callback) + SV *canvas; + SV *callback; + CODE: + _3DScene::register_action_selectbyparts_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); + void reset_legend_texture() CODE: @@ -736,6 +751,15 @@ load_model_object(canvas, model_object, obj_idx, instance_idxs) OUTPUT: RETVAL +int +get_first_volume_id(canvas, obj_idx) + SV *canvas; + int obj_idx; + CODE: + RETVAL = _3DScene::get_first_volume_id((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), obj_idx); + OUTPUT: + RETVAL + std::vector load_model(canvas, model, obj_idx) SV *canvas;