Add section view feature for 3D canvas (#15879)
@@ -0,0 +1,103 @@
|
||||
# Section view — High Level Design
|
||||
|
||||
## Purpose and scope
|
||||
|
||||
Section view hides whatever lies between the camera and a plane, so the user can look inside
|
||||
objects in Prepare and in the assembly view, and inside the toolpaths in Preview. It is a view
|
||||
setting: it changes nothing in the model, the slice or the project file, and it does not reach
|
||||
plate thumbnails.
|
||||
|
||||
The user controls it from the section button of the canvas toolbar in the bottom left corner of
|
||||
the 3D view. The button opens a panel above it with a slider for the depth of the cut, a "Set
|
||||
viewing angle" button that turns the plane to face the camera at the same depth, and a button that
|
||||
resets the depth to zero. The panel is an ordinary overlay window, not a popup, so the scene keeps
|
||||
taking clicks and drags while it is open; the button or Esc closes it again. Esc closes the panel
|
||||
before it closes a gizmo or clears the selection. The button is highlighted
|
||||
while a section cuts the scene and has shortcuts of its own: the mouse wheel over it moves the
|
||||
plane, a right click switches the section off and back on, and a middle click sets the viewing
|
||||
angle. Alt + mouse wheel moves the plane anywhere in the 3D view, with or without a gizmo open.
|
||||
|
||||
## State
|
||||
|
||||
Prepare and Preview share one section view, so a cut made in either tab is the same cut in the
|
||||
other. The assembly view, whose objects sit apart from their places on the plate, and the Design
|
||||
tab keep their own. The section itself is two values.
|
||||
|
||||
- **Ratio**, from 0 to 1. At 0 the section is off. As the ratio grows, the plane sweeps the
|
||||
sphere around the objects, from its side facing the camera to the opposite side, so at 1
|
||||
everything is cut away.
|
||||
- **Normal**, taken from the camera direction the first time the section is switched on, and
|
||||
again whenever the user sets the viewing angle. The plane keeps that orientation while the
|
||||
camera orbits and while the section is off, so the cut face can be seen from any side and
|
||||
bringing the depth back to 0 does not lose the angle.
|
||||
|
||||
The ratio in use when the section is switched off is kept, and the right click on the button
|
||||
brings the section back at that ratio, which restores the same cut. Whether the panel is open is
|
||||
shared along with the section.
|
||||
|
||||
The sphere is recomputed every frame from the volumes of the canvas the section view belongs to:
|
||||
the objects on the current plate, or every object when that plate is empty. Preview holds no
|
||||
objects of its own, so it places the plane across the volumes of Prepare, which makes it cut the
|
||||
toolpaths exactly where Prepare cuts the objects. Only G-code opened on its own, without objects,
|
||||
is measured by its toolpaths. In the assembly view the sphere is around the whole assembly. The
|
||||
ratio therefore keeps its meaning when objects move or the user switches plates. It is not a
|
||||
fixed position in world space.
|
||||
|
||||
Only the tab on screen can change the section, and switching tabs redraws the whole scene and
|
||||
closes the open gizmo, so neither tab ever shows a stale cut.
|
||||
|
||||
## Where the plane applies
|
||||
|
||||
`GLCanvas3D::_get_section_view_plane()` turns the state into a plane in the convention of
|
||||
`ClippingPlane::is_point_clipped()`. Everything that draws or picks the scene reads that plane.
|
||||
|
||||
- **Volumes.** The plane goes to the `clipping_plane` uniform of the volume shaders. The same
|
||||
uniform serves the gouraud, phong and X-ray passes and the colour picking pass.
|
||||
- **Cut faces.** Clipping only discards fragments, which would leave the cut volumes hollow.
|
||||
`_render_section_view_caps()` draws their cut faces with one `MeshClipper` per model part the
|
||||
plane passes through. A clipper recomputes its face only when the plane or the volume moves.
|
||||
Modifiers, the wipe tower and SLA auxiliaries get no face.
|
||||
- **Toolpaths.** libvgcode takes the plane through `Viewer::set_clipping_plane()`. It draws each
|
||||
extrusion as only the faces of a diamond-section prism that turn towards the camera, so
|
||||
discarding the fragments on the clipped side would leave open shells. Instead, the segment
|
||||
shader follows the view ray from a fragment that is cut away to the plane. When the
|
||||
extrusion's diamond section still holds that point, the fragment is shaded as the cut face, lit
|
||||
as the plane faces; otherwise it is discarded. The cut face keeps the depth of the fragment it
|
||||
replaces, which is safe: along that ray everything else still shown lies behind the plane. The
|
||||
shader writes no `gl_FragDepth`, so early depth testing survives. Option markers are cut away
|
||||
whole, by their centres. The shadow casters share the segment shader, so what is cut away casts
|
||||
no shadow either. Preview shells are drawn by another shader and are not clipped.
|
||||
- **Picking.** `get_raycaster_clipping_plane()` returns the same plane, so hover, selection and
|
||||
the perspective pan anchor ignore what the user cannot see.
|
||||
|
||||
## Gizmos
|
||||
|
||||
A gizmo that clips its object itself owns the gizmo data pool's `ObjectClipper`, and its plane
|
||||
replaces the canvas section while the gizmo is open. `GLGizmosManager::get_clipping_plane()`
|
||||
reports that plane, or nothing when no open gizmo has a clipper. There are two cases.
|
||||
|
||||
- **Painting tools and brim ears** show the canvas section on the object they edit.
|
||||
`GLGizmosManager::update_section_view()` copies the ratio and normal into their clipper
|
||||
whenever the pool is updated or the section changes. The clipper then places the plane across
|
||||
the edited instance, which is the only object shown. The painting tools keep clipping their
|
||||
own triangles, raycasts and cut face through it. Brim ears always cut horizontally from the
|
||||
top, because the ears sit on the plate. At ratio 0 the clipper holds no plane at all, so the
|
||||
raycasts are not clipped.
|
||||
- **Cut and mesh boolean** use the clipper for their own purposes, so the canvas section is
|
||||
suspended while they are open.
|
||||
|
||||
Every other gizmo, including move, rotate and scale, leaves the canvas section in place.
|
||||
|
||||
## Alt + mouse wheel
|
||||
|
||||
The canvas handles Alt + wheel after the gizmos had their turn, so it works the same in every
|
||||
tab and with any gizmo open. On Windows, releasing Alt when no key was pressed since it went down
|
||||
opens the window menu, and a wheel turn does not count as a key. Under the custom title bar that
|
||||
menu is invisible, yet it takes the keyboard and the next click, which looks like a frozen 3D
|
||||
view. After Alt + wheel the canvas therefore consumes the Alt release instead of passing it on.
|
||||
|
||||
## Redraw
|
||||
|
||||
The button and the panel are part of the ImGui overlay, which is built after the frame's scene is
|
||||
drawn. A change to the section therefore marks the scene dirty and asks for one more frame. The
|
||||
cached scene is never reused across a change.
|
||||
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><svg id="a" xmlns="http://www.w3.org/2000/svg" width="36" height="36" viewBox="0 0 36 36"><circle cx="17.5" cy="17.5" r="17.5" style="fill:#fafafa;"/><polygon points="13.82,11.62 21.18,15.88 21.18,23.38 13.82,19.12" style="fill:#2b3436; fill-opacity:.55;"/><polygon points="8.75,14.55 16.11,18.80 16.11,26.30 8.75,22.05" style="fill:none; stroke:#2b3436; stroke-linecap:round; stroke-linejoin:round; stroke-opacity:.45;"/><polyline points="8.75,14.55 12.43,12.43 19.79,16.68 16.11,18.80" style="fill:none; stroke:#2b3436; stroke-linecap:round; stroke-linejoin:round; stroke-opacity:.45;"/><polyline points="19.79,16.68 19.79,24.18 16.11,26.30" style="fill:none; stroke:#2b3436; stroke-linecap:round; stroke-linejoin:round; stroke-opacity:.45;"/><polygon points="13.82,11.62 17.50,9.50 24.86,13.75 24.86,21.25 21.18,23.38 13.82,19.12" style="fill:none; stroke:#2b3436; stroke-linecap:round; stroke-linejoin:round;"/><polyline points="13.82,11.62 21.18,15.88 24.86,13.75" style="fill:none; stroke:#2b3436; stroke-linecap:round; stroke-linejoin:round;"/><line x1="21.18" y1="15.88" x2="21.18" y2="23.38" style="fill:none; stroke:#2b3436; stroke-linecap:round; stroke-linejoin:round;"/></svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><svg id="a" xmlns="http://www.w3.org/2000/svg" width="36" height="36" viewBox="0 0 36 36"><circle cx="17.5" cy="17.5" r="17.5" style="fill:#009688;"/><polygon points="13.82,11.62 21.18,15.88 21.18,23.38 13.82,19.12" style="fill:#ffffff; fill-opacity:.55;"/><polygon points="8.75,14.55 16.11,18.80 16.11,26.30 8.75,22.05" style="fill:none; stroke:#ffffff; stroke-linecap:round; stroke-linejoin:round; stroke-opacity:.45;"/><polyline points="8.75,14.55 12.43,12.43 19.79,16.68 16.11,18.80" style="fill:none; stroke:#ffffff; stroke-linecap:round; stroke-linejoin:round; stroke-opacity:.45;"/><polyline points="19.79,16.68 19.79,24.18 16.11,26.30" style="fill:none; stroke:#ffffff; stroke-linecap:round; stroke-linejoin:round; stroke-opacity:.45;"/><polygon points="13.82,11.62 17.50,9.50 24.86,13.75 24.86,21.25 21.18,23.38 13.82,19.12" style="fill:none; stroke:#ffffff; stroke-linecap:round; stroke-linejoin:round;"/><polyline points="13.82,11.62 21.18,15.88 24.86,13.75" style="fill:none; stroke:#ffffff; stroke-linecap:round; stroke-linejoin:round;"/><line x1="21.18" y1="15.88" x2="21.18" y2="23.38" style="fill:none; stroke:#ffffff; stroke-linecap:round; stroke-linejoin:round;"/></svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><svg id="a" xmlns="http://www.w3.org/2000/svg" width="36" height="36" viewBox="0 0 36 36"><circle cx="17.5" cy="17.5" r="17.5" style="fill:#00675b;"/><polygon points="13.82,11.62 21.18,15.88 21.18,23.38 13.82,19.12" style="fill:#ffffff; fill-opacity:.55;"/><polygon points="8.75,14.55 16.11,18.80 16.11,26.30 8.75,22.05" style="fill:none; stroke:#ffffff; stroke-linecap:round; stroke-linejoin:round; stroke-opacity:.45;"/><polyline points="8.75,14.55 12.43,12.43 19.79,16.68 16.11,18.80" style="fill:none; stroke:#ffffff; stroke-linecap:round; stroke-linejoin:round; stroke-opacity:.45;"/><polyline points="19.79,16.68 19.79,24.18 16.11,26.30" style="fill:none; stroke:#ffffff; stroke-linecap:round; stroke-linejoin:round; stroke-opacity:.45;"/><polygon points="13.82,11.62 17.50,9.50 24.86,13.75 24.86,21.25 21.18,23.38 13.82,19.12" style="fill:none; stroke:#ffffff; stroke-linecap:round; stroke-linejoin:round;"/><polyline points="13.82,11.62 21.18,15.88 24.86,13.75" style="fill:none; stroke:#ffffff; stroke-linecap:round; stroke-linejoin:round;"/><line x1="21.18" y1="15.88" x2="21.18" y2="23.38" style="fill:none; stroke:#ffffff; stroke-linecap:round; stroke-linejoin:round;"/></svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><svg id="a" xmlns="http://www.w3.org/2000/svg" width="36" height="36" viewBox="0 0 36 36"><circle cx="17.5" cy="17.5" r="17.5" style="fill:#008172;"/><polygon points="13.82,11.62 21.18,15.88 21.18,23.38 13.82,19.12" style="fill:#ffffff; fill-opacity:.55;"/><polygon points="8.75,14.55 16.11,18.80 16.11,26.30 8.75,22.05" style="fill:none; stroke:#ffffff; stroke-linecap:round; stroke-linejoin:round; stroke-opacity:.45;"/><polyline points="8.75,14.55 12.43,12.43 19.79,16.68 16.11,18.80" style="fill:none; stroke:#ffffff; stroke-linecap:round; stroke-linejoin:round; stroke-opacity:.45;"/><polyline points="19.79,16.68 19.79,24.18 16.11,26.30" style="fill:none; stroke:#ffffff; stroke-linecap:round; stroke-linejoin:round; stroke-opacity:.45;"/><polygon points="13.82,11.62 17.50,9.50 24.86,13.75 24.86,21.25 21.18,23.38 13.82,19.12" style="fill:none; stroke:#ffffff; stroke-linecap:round; stroke-linejoin:round;"/><polyline points="13.82,11.62 21.18,15.88 24.86,13.75" style="fill:none; stroke:#ffffff; stroke-linecap:round; stroke-linejoin:round;"/><line x1="21.18" y1="15.88" x2="21.18" y2="23.38" style="fill:none; stroke:#ffffff; stroke-linecap:round; stroke-linejoin:round;"/></svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><svg id="a" xmlns="http://www.w3.org/2000/svg" width="36" height="36" viewBox="0 0 36 36"><circle cx="17.5" cy="17.5" r="17.5" style="fill:#26a69a;"/><polygon points="13.82,11.62 21.18,15.88 21.18,23.38 13.82,19.12" style="fill:#ffffff; fill-opacity:.55;"/><polygon points="8.75,14.55 16.11,18.80 16.11,26.30 8.75,22.05" style="fill:none; stroke:#ffffff; stroke-linecap:round; stroke-linejoin:round; stroke-opacity:.45;"/><polyline points="8.75,14.55 12.43,12.43 19.79,16.68 16.11,18.80" style="fill:none; stroke:#ffffff; stroke-linecap:round; stroke-linejoin:round; stroke-opacity:.45;"/><polyline points="19.79,16.68 19.79,24.18 16.11,26.30" style="fill:none; stroke:#ffffff; stroke-linecap:round; stroke-linejoin:round; stroke-opacity:.45;"/><polygon points="13.82,11.62 17.50,9.50 24.86,13.75 24.86,21.25 21.18,23.38 13.82,19.12" style="fill:none; stroke:#ffffff; stroke-linecap:round; stroke-linejoin:round;"/><polyline points="13.82,11.62 21.18,15.88 24.86,13.75" style="fill:none; stroke:#ffffff; stroke-linecap:round; stroke-linejoin:round;"/><line x1="21.18" y1="15.88" x2="21.18" y2="23.38" style="fill:none; stroke:#ffffff; stroke-linecap:round; stroke-linejoin:round;"/></svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><svg id="a" xmlns="http://www.w3.org/2000/svg" width="36" height="36" viewBox="0 0 36 36"><circle cx="17.5" cy="17.5" r="17.5" style="fill:#393c42;"/><polygon points="13.82,11.62 21.18,15.88 21.18,23.38 13.82,19.12" style="fill:#b6b6b6; fill-opacity:.55;"/><polygon points="8.75,14.55 16.11,18.80 16.11,26.30 8.75,22.05" style="fill:none; stroke:#b6b6b6; stroke-linecap:round; stroke-linejoin:round; stroke-opacity:.45;"/><polyline points="8.75,14.55 12.43,12.43 19.79,16.68 16.11,18.80" style="fill:none; stroke:#b6b6b6; stroke-linecap:round; stroke-linejoin:round; stroke-opacity:.45;"/><polyline points="19.79,16.68 19.79,24.18 16.11,26.30" style="fill:none; stroke:#b6b6b6; stroke-linecap:round; stroke-linejoin:round; stroke-opacity:.45;"/><polygon points="13.82,11.62 17.50,9.50 24.86,13.75 24.86,21.25 21.18,23.38 13.82,19.12" style="fill:none; stroke:#b6b6b6; stroke-linecap:round; stroke-linejoin:round;"/><polyline points="13.82,11.62 21.18,15.88 24.86,13.75" style="fill:none; stroke:#b6b6b6; stroke-linecap:round; stroke-linejoin:round;"/><line x1="21.18" y1="15.88" x2="21.18" y2="23.38" style="fill:none; stroke:#b6b6b6; stroke-linecap:round; stroke-linejoin:round;"/></svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><svg id="a" xmlns="http://www.w3.org/2000/svg" width="36" height="36" viewBox="0 0 36 36"><circle cx="17.5" cy="17.5" r="17.5" style="fill:#283232;"/><polygon points="13.82,11.62 21.18,15.88 21.18,23.38 13.82,19.12" style="fill:#b6b6b6; fill-opacity:.55;"/><polygon points="8.75,14.55 16.11,18.80 16.11,26.30 8.75,22.05" style="fill:none; stroke:#b6b6b6; stroke-linecap:round; stroke-linejoin:round; stroke-opacity:.45;"/><polyline points="8.75,14.55 12.43,12.43 19.79,16.68 16.11,18.80" style="fill:none; stroke:#b6b6b6; stroke-linecap:round; stroke-linejoin:round; stroke-opacity:.45;"/><polyline points="19.79,16.68 19.79,24.18 16.11,26.30" style="fill:none; stroke:#b6b6b6; stroke-linecap:round; stroke-linejoin:round; stroke-opacity:.45;"/><polygon points="13.82,11.62 17.50,9.50 24.86,13.75 24.86,21.25 21.18,23.38 13.82,19.12" style="fill:none; stroke:#b6b6b6; stroke-linecap:round; stroke-linejoin:round;"/><polyline points="13.82,11.62 21.18,15.88 24.86,13.75" style="fill:none; stroke:#b6b6b6; stroke-linecap:round; stroke-linejoin:round;"/><line x1="21.18" y1="15.88" x2="21.18" y2="23.38" style="fill:none; stroke:#b6b6b6; stroke-linecap:round; stroke-linejoin:round;"/></svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><svg id="a" xmlns="http://www.w3.org/2000/svg" width="36" height="36" viewBox="0 0 36 36"><circle cx="17.5" cy="17.5" r="17.5" style="fill:#e5f0ee;"/><polygon points="13.82,11.62 21.18,15.88 21.18,23.38 13.82,19.12" style="fill:#2b3436; fill-opacity:.55;"/><polygon points="8.75,14.55 16.11,18.80 16.11,26.30 8.75,22.05" style="fill:none; stroke:#2b3436; stroke-linecap:round; stroke-linejoin:round; stroke-opacity:.45;"/><polyline points="8.75,14.55 12.43,12.43 19.79,16.68 16.11,18.80" style="fill:none; stroke:#2b3436; stroke-linecap:round; stroke-linejoin:round; stroke-opacity:.45;"/><polyline points="19.79,16.68 19.79,24.18 16.11,26.30" style="fill:none; stroke:#2b3436; stroke-linecap:round; stroke-linejoin:round; stroke-opacity:.45;"/><polygon points="13.82,11.62 17.50,9.50 24.86,13.75 24.86,21.25 21.18,23.38 13.82,19.12" style="fill:none; stroke:#2b3436; stroke-linecap:round; stroke-linejoin:round;"/><polyline points="13.82,11.62 21.18,15.88 24.86,13.75" style="fill:none; stroke:#2b3436; stroke-linecap:round; stroke-linejoin:round;"/><line x1="21.18" y1="15.88" x2="21.18" y2="23.38" style="fill:none; stroke:#2b3436; stroke-linecap:round; stroke-linejoin:round;"/></svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -76,6 +76,10 @@ public:
|
||||
// caller decides which of the two it varies with the realistic view setting.
|
||||
//
|
||||
void set_tone(float exposure, float saturation);
|
||||
//
|
||||
// ORCA: section view. Cuts away where dot(plane, (x, y, z, 1)) < 0; { 0, 0, 0, 1 } keeps all.
|
||||
//
|
||||
void set_clipping_plane(const std::array<float, 4>& plane);
|
||||
|
||||
//
|
||||
// ************************************************************************
|
||||
|
||||
@@ -38,6 +38,8 @@ static const char* Segments_Vertex_Shader =
|
||||
// rejection discards most of the hidden fragments; set when the camera looks down on the print
|
||||
"uniform int reverse_order;\n"
|
||||
"uniform int instance_count;\n"
|
||||
// ORCA: section view
|
||||
"uniform vec4 clipping_plane;\n"
|
||||
"in int vertex_id;\n"
|
||||
"out vec3 color;\n"
|
||||
"// ORCA: realistic view - the light the shadow map is able to block, kept apart from the\n"
|
||||
@@ -48,6 +50,9 @@ static const char* Segments_Vertex_Shader =
|
||||
"// needs its world position and, for the depth bias, its eye space normal.\n"
|
||||
"out vec3 world_position;\n"
|
||||
"out vec3 shadow_normal;\n"
|
||||
// ORCA: section view - the segment and the light on its cut face
|
||||
"flat out int segment_id;\n"
|
||||
"out vec3 cut_color_direct;\n"
|
||||
"vec3 decode_color(float color) {\n"
|
||||
" int c = int(round(color));\n"
|
||||
" int r = (c >> 16) & 0xFF;\n"
|
||||
@@ -159,6 +164,9 @@ static const char* Segments_Vertex_Shader =
|
||||
" color_direct = color_base * direct_lighting(eye_position, eye_normal);\n"
|
||||
" world_position = pos;\n"
|
||||
" shadow_normal = eye_normal;\n"
|
||||
" segment_id = id_a;\n"
|
||||
" cut_color_direct = clipping_plane.xyz == vec3(0.0) ? vec3(0.0) :\n"
|
||||
" color_base * direct_lighting(eye_position, mat3(view_matrix) * -clipping_plane.xyz);\n"
|
||||
" gl_Position = projection_matrix * vec4(eye_position, 1.0);\n"
|
||||
"}\n";
|
||||
|
||||
@@ -178,23 +186,33 @@ static const char* Segments_Fragment_Shader =
|
||||
// passes in each mode is decided in GLCanvas3D::_render_gcode, not here.
|
||||
"uniform float exposure;\n"
|
||||
"uniform float saturation;\n"
|
||||
// ORCA: section view - only camera-facing faces are drawn, so cut faces are ray cast
|
||||
"uniform vec4 clipping_plane;\n"
|
||||
"uniform mat4 view_matrix;\n"
|
||||
"uniform mat4 projection_matrix;\n"
|
||||
"uniform vec3 camera_position;\n"
|
||||
"uniform samplerBuffer position_tex;\n"
|
||||
"uniform samplerBuffer height_width_angle_tex;\n"
|
||||
"const vec3 UP = vec3(0, 0, 1);\n"
|
||||
"const vec3 LUMA = vec3(0.2126, 0.7152, 0.0722);\n"
|
||||
"in vec3 color;\n"
|
||||
"in vec3 color_direct;\n"
|
||||
"in vec3 world_position;\n"
|
||||
"in vec3 shadow_normal;\n"
|
||||
"flat in int segment_id;\n"
|
||||
"in vec3 cut_color_direct;\n"
|
||||
"out vec4 fragment_color;\n"
|
||||
"float shadow_shade() {\n"
|
||||
"float shadow_shade(vec3 position, vec3 normal) {\n"
|
||||
" if (shadow_intensity <= 0.0)\n"
|
||||
" return 1.0;\n"
|
||||
" vec4 lp = shadow_light_vp * vec4(world_position, 1.0);\n"
|
||||
" vec4 lp = shadow_light_vp * vec4(position, 1.0);\n"
|
||||
" vec3 proj = lp.xyz / lp.w;\n"
|
||||
" proj = proj * 0.5 + 0.5;\n"
|
||||
" if (proj.z > 1.0)\n"
|
||||
" return 1.0;\n"
|
||||
" // Slope-scaled bias, as in gouraud.fs. An extrusion is only a handful of shadow-map texels\n"
|
||||
" // wide, so grazing faces need the larger bias to keep self-shadow acne off the top surfaces.\n"
|
||||
" float NdotL = dot(normalize(shadow_normal), SHADOW_LIGHT_DIR);\n"
|
||||
" float NdotL = dot(normalize(normal), SHADOW_LIGHT_DIR);\n"
|
||||
" float bias = mix(0.0004, 0.004, clamp(1.0 - NdotL, 0.0, 1.0));\n"
|
||||
" float sum = 0.0;\n"
|
||||
" for (int x = -2; x <= 2; ++x) {\n"
|
||||
@@ -205,8 +223,39 @@ static const char* Segments_Fragment_Shader =
|
||||
" }\n"
|
||||
" return 1.0 - shadow_intensity * (sum / 25.0);\n"
|
||||
"}\n"
|
||||
// ORCA: section view - where the view ray meets the plane, if inside the extrusion's diamond section
|
||||
"bool find_cut(out vec3 cut) {\n"
|
||||
// parallel rays for an orthographic projection
|
||||
" vec3 ray = projection_matrix[3][3] == 1.0 ? -vec3(view_matrix[0][2], view_matrix[1][2], view_matrix[2][2]) :\n"
|
||||
" normalize(world_position - camera_position);\n"
|
||||
" float approach = dot(clipping_plane.xyz, ray);\n"
|
||||
" if (approach <= 0.0)\n"
|
||||
" return false;\n"
|
||||
" cut = world_position - dot(vec4(world_position, 1.0), clipping_plane) / approach * ray;\n"
|
||||
" vec3 pos_a = texelFetch(position_tex, segment_id).xyz;\n"
|
||||
" vec3 line = texelFetch(position_tex, segment_id + 1).xyz - pos_a;\n"
|
||||
" float length_sq = dot(line, line);\n"
|
||||
" float along = length_sq < 1e-8 ? -1.0 : dot(cut - pos_a, line) / length_sq;\n"
|
||||
" if (along < 0.0 || along > 1.0)\n"
|
||||
" return false;\n"
|
||||
" vec3 line_dir = line * inversesqrt(length_sq);\n"
|
||||
" vec3 right_dir = abs(dot(line_dir, UP)) > 0.9 ? normalize(cross(vec3(1, 0, 0), line_dir)) : normalize(cross(line_dir, UP));\n"
|
||||
" vec3 offset = cut - pos_a - along * line;\n"
|
||||
" vec2 half_height_width = 0.5 * mix(texelFetch(height_width_angle_tex, segment_id).xy,\n"
|
||||
" texelFetch(height_width_angle_tex, segment_id + 1).xy, along);\n"
|
||||
" return abs(dot(offset, right_dir)) / half_height_width.y + abs(dot(offset, cross(right_dir, line_dir))) / half_height_width.x <= 1.0;\n"
|
||||
"}\n"
|
||||
"void main() {\n"
|
||||
" vec3 c = (color + color_direct * shadow_shade()) * exposure;\n"
|
||||
" vec3 c;\n"
|
||||
" if (dot(vec4(world_position, 1.0), clipping_plane) >= 0.0)\n"
|
||||
" c = color + color_direct * shadow_shade(world_position, shadow_normal);\n"
|
||||
" else {\n"
|
||||
" vec3 cut;\n"
|
||||
" if (!find_cut(cut))\n"
|
||||
" discard;\n"
|
||||
" c = color + cut_color_direct * shadow_shade(cut, mat3(view_matrix) * -clipping_plane.xyz);\n"
|
||||
" }\n"
|
||||
" c *= exposure;\n"
|
||||
" c = mix(vec3(dot(c, LUMA)), c, saturation);\n"
|
||||
" fragment_color = vec4(clamp(c, 0.0, 1.0), 1.0);\n"
|
||||
"}\n";
|
||||
@@ -234,9 +283,12 @@ static const char* Options_Vertex_Shader =
|
||||
"uniform samplerBuffer height_width_angle_tex;\n"
|
||||
"uniform samplerBuffer color_tex;\n"
|
||||
"uniform usamplerBuffer segment_index_tex;\n"
|
||||
// ORCA: section view
|
||||
"uniform vec4 clipping_plane;\n"
|
||||
"in vec3 in_position;\n"
|
||||
"in vec3 in_normal;\n"
|
||||
"out vec3 color;\n"
|
||||
"out float clipping_plane_dot;\n"
|
||||
"vec3 decode_color(float color) {\n"
|
||||
" int c = int(round(color));\n"
|
||||
" int r = (c >> 16) & 0xFF;\n"
|
||||
@@ -263,6 +315,8 @@ static const char* Options_Vertex_Shader =
|
||||
" height_width.y, 0.0, 0.0,\n"
|
||||
" 0.0, height_width.y, 0.0,\n"
|
||||
" 0.0, 0.0, height_width.x);\n"
|
||||
// ORCA: section view - markers are cut away whole, by their centre
|
||||
" clipping_plane_dot = dot(vec4(offset, 1.0), clipping_plane);\n"
|
||||
" vec3 eye_position = (view_matrix * vec4(scale_matrix * in_position + offset, 1.0)).xyz;\n"
|
||||
" // ORCA: Apply bias to z-position to avoid z-fighting\n"
|
||||
" eye_position.z += bias;\n"
|
||||
@@ -275,8 +329,11 @@ static const char* Options_Vertex_Shader =
|
||||
static const char* Options_Fragment_Shader =
|
||||
"#version 150\n"
|
||||
"in vec3 color;\n"
|
||||
"in float clipping_plane_dot;\n"
|
||||
"out vec4 fragment_color;\n"
|
||||
"void main() {\n"
|
||||
" if (clipping_plane_dot < 0.0)\n"
|
||||
" discard;\n"
|
||||
" fragment_color = vec4(color, 1.0);\n"
|
||||
"}\n";
|
||||
|
||||
|
||||
@@ -32,6 +32,8 @@ static const char* Segments_Vertex_Shader_ES =
|
||||
"uniform sampler2D height_width_angle_tex;\n"
|
||||
"uniform sampler2D color_tex;\n"
|
||||
"uniform usampler2D segment_index_tex;\n"
|
||||
// ORCA: section view
|
||||
"uniform vec4 clipping_plane;\n"
|
||||
"in float vertex_id_float;\n"
|
||||
"out vec3 color;\n"
|
||||
"// ORCA: realistic view - the light the shadow map is able to block, kept apart from the\n"
|
||||
@@ -42,6 +44,9 @@ static const char* Segments_Vertex_Shader_ES =
|
||||
"// needs its world position and, for the depth bias, its eye space normal.\n"
|
||||
"out vec3 world_position;\n"
|
||||
"out vec3 shadow_normal;\n"
|
||||
// ORCA: section view - the segment and the light on its cut face
|
||||
"flat out int segment_id;\n"
|
||||
"out vec3 cut_color_direct;\n"
|
||||
"vec3 decode_color(float color) {\n"
|
||||
" int c = int(round(color));\n"
|
||||
" int r = (c >> 16) & 0xFF;\n"
|
||||
@@ -156,12 +161,17 @@ static const char* Segments_Vertex_Shader_ES =
|
||||
" color_direct = color_base * direct_lighting(eye_position, eye_normal);\n"
|
||||
" world_position = pos;\n"
|
||||
" shadow_normal = eye_normal;\n"
|
||||
" segment_id = id_a;\n"
|
||||
" cut_color_direct = clipping_plane.xyz == vec3(0.0) ? vec3(0.0) :\n"
|
||||
" color_base * direct_lighting(eye_position, mat3(view_matrix) * -clipping_plane.xyz);\n"
|
||||
" gl_Position = projection_matrix * vec4(eye_position, 1.0);\n"
|
||||
"}\n";
|
||||
|
||||
static const char* Segments_Fragment_Shader_ES =
|
||||
"#version 300 es\n"
|
||||
"precision highp float;\n"
|
||||
// ORCA: section view - segment ids exceed mediump
|
||||
"precision highp int;\n"
|
||||
"// ORCA: sampler2D defaults to lowp in an ES fragment shader, far too coarse to compare\n"
|
||||
"// shadow map depths against.\n"
|
||||
"precision highp sampler2D;\n"
|
||||
@@ -179,23 +189,33 @@ static const char* Segments_Fragment_Shader_ES =
|
||||
// passes in each mode is decided in GLCanvas3D::_render_gcode, not here.
|
||||
"uniform float exposure;\n"
|
||||
"uniform float saturation;\n"
|
||||
// ORCA: section view - cut faces are ray cast; data samplers stay lowp as in the vertex shader
|
||||
"uniform vec4 clipping_plane;\n"
|
||||
"uniform mat4 view_matrix;\n"
|
||||
"uniform mat4 projection_matrix;\n"
|
||||
"uniform vec3 camera_position;\n"
|
||||
"uniform lowp sampler2D position_tex;\n"
|
||||
"uniform lowp sampler2D height_width_angle_tex;\n"
|
||||
"const vec3 UP = vec3(0, 0, 1);\n"
|
||||
"const vec3 LUMA = vec3(0.2126, 0.7152, 0.0722);\n"
|
||||
"in vec3 color;\n"
|
||||
"in vec3 color_direct;\n"
|
||||
"in vec3 world_position;\n"
|
||||
"in vec3 shadow_normal;\n"
|
||||
"flat in int segment_id;\n"
|
||||
"in vec3 cut_color_direct;\n"
|
||||
"out vec4 fragment_color;\n"
|
||||
"float shadow_shade() {\n"
|
||||
"float shadow_shade(vec3 position, vec3 normal) {\n"
|
||||
" if (shadow_intensity <= 0.0)\n"
|
||||
" return 1.0;\n"
|
||||
" vec4 lp = shadow_light_vp * vec4(world_position, 1.0);\n"
|
||||
" vec4 lp = shadow_light_vp * vec4(position, 1.0);\n"
|
||||
" vec3 proj = lp.xyz / lp.w;\n"
|
||||
" proj = proj * 0.5 + 0.5;\n"
|
||||
" if (proj.z > 1.0)\n"
|
||||
" return 1.0;\n"
|
||||
" // Slope-scaled bias, as in gouraud.fs. An extrusion is only a handful of shadow-map texels\n"
|
||||
" // wide, so grazing faces need the larger bias to keep self-shadow acne off the top surfaces.\n"
|
||||
" float NdotL = dot(normalize(shadow_normal), SHADOW_LIGHT_DIR);\n"
|
||||
" float NdotL = dot(normalize(normal), SHADOW_LIGHT_DIR);\n"
|
||||
" float bias = mix(0.0004, 0.004, clamp(1.0 - NdotL, 0.0, 1.0));\n"
|
||||
" float sum = 0.0;\n"
|
||||
" for (int x = -2; x <= 2; ++x) {\n"
|
||||
@@ -206,8 +226,43 @@ static const char* Segments_Fragment_Shader_ES =
|
||||
" }\n"
|
||||
" return 1.0 - shadow_intensity * (sum / 25.0);\n"
|
||||
"}\n"
|
||||
"vec4 fetch(lowp sampler2D sampler, int id) {\n"
|
||||
" ivec2 tex_size = textureSize(sampler, 0);\n"
|
||||
" return texelFetch(sampler, (tex_size.y == 1) ? ivec2(id, 0) : ivec2(id % tex_size.x, id / tex_size.x), 0);\n"
|
||||
"}\n"
|
||||
// ORCA: section view - where the view ray meets the plane, if inside the extrusion's diamond section
|
||||
"bool find_cut(out vec3 cut) {\n"
|
||||
// parallel rays for an orthographic projection
|
||||
" vec3 ray = projection_matrix[3][3] == 1.0 ? -vec3(view_matrix[0][2], view_matrix[1][2], view_matrix[2][2]) :\n"
|
||||
" normalize(world_position - camera_position);\n"
|
||||
" float approach = dot(clipping_plane.xyz, ray);\n"
|
||||
" if (approach <= 0.0)\n"
|
||||
" return false;\n"
|
||||
" cut = world_position - dot(vec4(world_position, 1.0), clipping_plane) / approach * ray;\n"
|
||||
" vec3 pos_a = fetch(position_tex, segment_id).xyz;\n"
|
||||
" vec3 line = fetch(position_tex, segment_id + 1).xyz - pos_a;\n"
|
||||
" float length_sq = dot(line, line);\n"
|
||||
" float along = length_sq < 1e-8 ? -1.0 : dot(cut - pos_a, line) / length_sq;\n"
|
||||
" if (along < 0.0 || along > 1.0)\n"
|
||||
" return false;\n"
|
||||
" vec3 line_dir = line * inversesqrt(length_sq);\n"
|
||||
" vec3 right_dir = abs(dot(line_dir, UP)) > 0.9 ? normalize(cross(vec3(1, 0, 0), line_dir)) : normalize(cross(line_dir, UP));\n"
|
||||
" vec3 offset = cut - pos_a - along * line;\n"
|
||||
" vec2 half_height_width = 0.5 * mix(fetch(height_width_angle_tex, segment_id).xy,\n"
|
||||
" fetch(height_width_angle_tex, segment_id + 1).xy, along);\n"
|
||||
" return abs(dot(offset, right_dir)) / half_height_width.y + abs(dot(offset, cross(right_dir, line_dir))) / half_height_width.x <= 1.0;\n"
|
||||
"}\n"
|
||||
"void main() {\n"
|
||||
" vec3 c = (color + color_direct * shadow_shade()) * exposure;\n"
|
||||
" vec3 c;\n"
|
||||
" if (dot(vec4(world_position, 1.0), clipping_plane) >= 0.0)\n"
|
||||
" c = color + color_direct * shadow_shade(world_position, shadow_normal);\n"
|
||||
" else {\n"
|
||||
" vec3 cut;\n"
|
||||
" if (!find_cut(cut))\n"
|
||||
" discard;\n"
|
||||
" c = color + cut_color_direct * shadow_shade(cut, mat3(view_matrix) * -clipping_plane.xyz);\n"
|
||||
" }\n"
|
||||
" c *= exposure;\n"
|
||||
" c = mix(vec3(dot(c, LUMA)), c, saturation);\n"
|
||||
" fragment_color = vec4(clamp(c, 0.0, 1.0), 1.0);\n"
|
||||
"}\n";
|
||||
@@ -230,9 +285,12 @@ static const char* Options_Vertex_Shader_ES =
|
||||
"uniform sampler2D height_width_angle_tex;\n"
|
||||
"uniform sampler2D color_tex;\n"
|
||||
"uniform usampler2D segment_index_tex;\n"
|
||||
// ORCA: section view
|
||||
"uniform vec4 clipping_plane;\n"
|
||||
"in vec3 in_position;\n"
|
||||
"in vec3 in_normal;\n"
|
||||
"out vec3 color;\n"
|
||||
"out float clipping_plane_dot;\n"
|
||||
"vec3 decode_color(float color) {\n"
|
||||
" int c = int(round(color));\n"
|
||||
" int r = (c >> 16) & 0xFF;\n"
|
||||
@@ -264,6 +322,8 @@ static const char* Options_Vertex_Shader_ES =
|
||||
" height_width.y, 0.0, 0.0,\n"
|
||||
" 0.0, height_width.y, 0.0,\n"
|
||||
" 0.0, 0.0, height_width.x);\n"
|
||||
// ORCA: section view - markers are cut away whole, by their centre
|
||||
" clipping_plane_dot = dot(vec4(offset, 1.0), clipping_plane);\n"
|
||||
" vec3 eye_position = (view_matrix * vec4(scale_matrix * in_position + offset, 1.0)).xyz;\n"
|
||||
" vec3 eye_normal = (view_matrix * vec4(in_normal, 0.0)).xyz;\n"
|
||||
" vec3 color_base = decode_color(texelFetch(color_tex, tex_coord(color_tex, id), 0).r);\n"
|
||||
@@ -275,8 +335,11 @@ static const char* Options_Fragment_Shader_ES =
|
||||
"#version 300 es\n"
|
||||
"precision highp float;\n"
|
||||
"in vec3 color;\n"
|
||||
"in float clipping_plane_dot;\n"
|
||||
"out vec4 fragment_color;\n"
|
||||
"void main() {\n"
|
||||
" if (clipping_plane_dot < 0.0)\n"
|
||||
" discard;\n"
|
||||
" fragment_color = vec4(color, 1.0);\n"
|
||||
"}\n";
|
||||
|
||||
|
||||
@@ -57,6 +57,11 @@ void Viewer::set_tone(float exposure, float saturation)
|
||||
m_impl->set_tone(exposure, saturation);
|
||||
}
|
||||
|
||||
void Viewer::set_clipping_plane(const std::array<float, 4>& plane)
|
||||
{
|
||||
m_impl->set_clipping_plane(plane);
|
||||
}
|
||||
|
||||
EViewType Viewer::get_view_type() const
|
||||
{
|
||||
return m_impl->get_view_type();
|
||||
|
||||
@@ -773,6 +773,8 @@ void ViewerImpl::init(const std::string& opengl_context_version)
|
||||
m_uni_segments_exposure_id = glGetUniformLocation(m_segments_shader_id, "exposure");
|
||||
m_uni_segments_saturation_id = glGetUniformLocation(m_segments_shader_id, "saturation");
|
||||
m_uni_segments_bias_scale_id = glGetUniformLocation(m_segments_shader_id, "bias_scale");
|
||||
// ORCA: section view
|
||||
m_uni_segments_clipping_plane_id = glGetUniformLocation(m_segments_shader_id, "clipping_plane");
|
||||
glcheck();
|
||||
assert(m_uni_segments_view_matrix_id != -1 &&
|
||||
m_uni_segments_projection_matrix_id != -1 &&
|
||||
@@ -797,6 +799,8 @@ void ViewerImpl::init(const std::string& opengl_context_version)
|
||||
m_uni_options_height_width_angle_tex_id = glGetUniformLocation(m_options_shader_id, "height_width_angle_tex");
|
||||
m_uni_options_colors_tex_id = glGetUniformLocation(m_options_shader_id, "color_tex");
|
||||
m_uni_options_segment_index_tex_id = glGetUniformLocation(m_options_shader_id, "segment_index_tex");
|
||||
// ORCA: section view
|
||||
m_uni_options_clipping_plane_id = glGetUniformLocation(m_options_shader_id, "clipping_plane");
|
||||
glcheck();
|
||||
assert(m_uni_options_view_matrix_id != -1 &&
|
||||
m_uni_options_projection_matrix_id != -1 &&
|
||||
@@ -2110,6 +2114,7 @@ void ViewerImpl::render_segments(const Mat4x4& view_matrix, const Mat4x4& projec
|
||||
glsafe(glUniform1f(m_uni_segments_exposure_id, m_exposure));
|
||||
glsafe(glUniform1f(m_uni_segments_saturation_id, m_saturation));
|
||||
glsafe(glUniform1f(m_uni_segments_bias_scale_id, m_rendering_shadow_casters ? 0.0f : 1.0f));
|
||||
glsafe(glUniform4fv(m_uni_segments_clipping_plane_id, 1, m_clipping_plane.data()));
|
||||
|
||||
glsafe(glDisable(GL_CULL_FACE));
|
||||
|
||||
@@ -2197,6 +2202,7 @@ void ViewerImpl::render_options(const Mat4x4& view_matrix, const Mat4x4& project
|
||||
glsafe(glUniform1i(m_uni_options_segment_index_tex_id, 3));
|
||||
glsafe(glUniformMatrix4fv(m_uni_options_view_matrix_id, 1, GL_FALSE, view_matrix.data()));
|
||||
glsafe(glUniformMatrix4fv(m_uni_options_projection_matrix_id, 1, GL_FALSE, projection_matrix.data()));
|
||||
glsafe(glUniform4fv(m_uni_options_clipping_plane_id, 1, m_clipping_plane.data()));
|
||||
|
||||
glsafe(glEnable(GL_CULL_FACE));
|
||||
|
||||
|
||||
@@ -89,6 +89,8 @@ public:
|
||||
// caller decides which of the two it varies with the realistic view setting.
|
||||
//
|
||||
void set_tone(float exposure, float saturation);
|
||||
// ORCA: section view, see Viewer::set_clipping_plane()
|
||||
void set_clipping_plane(const std::array<float, 4>& plane) { m_clipping_plane = plane; }
|
||||
|
||||
EViewType get_view_type() const { return m_settings.view_type; }
|
||||
void set_view_type(EViewType type);
|
||||
@@ -371,6 +373,7 @@ private:
|
||||
int m_uni_segments_exposure_id{ -1 };
|
||||
int m_uni_segments_saturation_id{ -1 };
|
||||
int m_uni_segments_bias_scale_id{ -1 };
|
||||
int m_uni_segments_clipping_plane_id{ -1 };
|
||||
//
|
||||
// Caches for OpenGL uniforms id for options shader
|
||||
//
|
||||
@@ -380,6 +383,7 @@ private:
|
||||
int m_uni_options_height_width_angle_tex_id{ -1 };
|
||||
int m_uni_options_colors_tex_id{ -1 };
|
||||
int m_uni_options_segment_index_tex_id{ -1 };
|
||||
int m_uni_options_clipping_plane_id{ -1 };
|
||||
#if VGCODE_ENABLE_COG_AND_TOOL_MARKERS
|
||||
//
|
||||
// Caches for OpenGL uniforms id for cog marker shader
|
||||
@@ -530,6 +534,9 @@ private:
|
||||
float m_exposure{ 1.0f };
|
||||
float m_saturation{ 1.0f };
|
||||
|
||||
// ORCA: section view
|
||||
std::array<float, 4> m_clipping_plane{ 0.0f, 0.0f, 0.0f, 1.0f };
|
||||
|
||||
void apply_pending_updates();
|
||||
void update_view_full_range();
|
||||
void update_color_ranges();
|
||||
|
||||
@@ -1782,6 +1782,15 @@ void GCodeViewer::set_tone(float exposure, float saturation)
|
||||
m_viewer.set_tone(exposure, saturation);
|
||||
}
|
||||
|
||||
void GCodeViewer::set_clipping_plane(const ClippingPlane& plane)
|
||||
{
|
||||
// Flipped to match ClippingPlane::distance().
|
||||
const Vec3f normal = -plane.get_normal().cast<float>();
|
||||
m_viewer.set_clipping_plane(plane.is_active() ?
|
||||
std::array<float, 4>{ normal.x(), normal.y(), normal.z(), float(plane.get_offset()) } :
|
||||
std::array<float, 4>{ 0.0f, 0.0f, 0.0f, 1.0f });
|
||||
}
|
||||
|
||||
void GCodeViewer::render_overlay(int canvas_width, int canvas_height, int right_margin)
|
||||
{
|
||||
if (m_viewer.get_extrusion_roles().empty())
|
||||
|
||||
@@ -300,6 +300,8 @@ public:
|
||||
// ORCA: tone applied to the shaded toolpaths, paying back the light the lighting term,
|
||||
// the shadow and the SSAO pass each take off. 1.0/1.0 is a no-op.
|
||||
void set_tone(float exposure, float saturation);
|
||||
// ORCA: section view
|
||||
void set_clipping_plane(const ClippingPlane& plane);
|
||||
//BBS
|
||||
// void _render_calibration_thumbnail_internal(ThumbnailData& thumbnail_data, const ThumbnailsParams& thumbnail_params, PartPlateList& partplate_list, OpenGLManager& opengl_manager);
|
||||
// void _render_calibration_thumbnail_framebuffer(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params, PartPlateList& partplate_list, OpenGLManager& opengl_manager);
|
||||
|
||||
@@ -1324,8 +1324,11 @@ GLCanvas3D::~GLCanvas3D()
|
||||
m_shadow_map_fbo = 0;
|
||||
}
|
||||
m_plate_shadow_mask.reset();
|
||||
m_section_view_caps.clear();
|
||||
}
|
||||
m_plate_shadow_mask_key.clear();
|
||||
if (m_section_view->owner == this)
|
||||
m_section_view->owner = nullptr;
|
||||
|
||||
reset_volumes();
|
||||
|
||||
@@ -1474,6 +1477,7 @@ void GLCanvas3D::reset_volumes()
|
||||
|
||||
m_selection.clear();
|
||||
m_volumes.clear();
|
||||
m_section_view_caps.clear();
|
||||
m_dirty = true;
|
||||
|
||||
_set_warning_notification(EWarning::ObjectOutside, false);
|
||||
@@ -1624,6 +1628,42 @@ ModelInstanceEPrintVolumeState GLCanvas3D::check_volumes_outside_state(ObjectFil
|
||||
return state;
|
||||
}
|
||||
|
||||
void GLCanvas3D::set_section_view_ratio(double ratio)
|
||||
{
|
||||
ratio = std::clamp(ratio, 0., 1.);
|
||||
if (ratio == m_section_view->ratio)
|
||||
return;
|
||||
|
||||
if (ratio == 0.)
|
||||
m_section_view->last_ratio = m_section_view->ratio;
|
||||
m_section_view->ratio = ratio;
|
||||
// Only the first cut faces the camera; later ones keep their angle until it is set again.
|
||||
if (m_section_view->normal.isZero())
|
||||
align_section_view_to_camera();
|
||||
else
|
||||
_on_section_view_changed();
|
||||
}
|
||||
|
||||
void GLCanvas3D::toggle_section_view()
|
||||
{
|
||||
const double last_ratio = m_section_view->last_ratio;
|
||||
set_section_view_ratio(is_section_view_active() ? 0. : last_ratio > 0. ? last_ratio : 0.5);
|
||||
}
|
||||
|
||||
void GLCanvas3D::align_section_view_to_camera()
|
||||
{
|
||||
m_section_view->normal = -wxGetApp().plater()->get_camera().get_dir_forward();
|
||||
_on_section_view_changed();
|
||||
}
|
||||
|
||||
void GLCanvas3D::_on_section_view_changed()
|
||||
{
|
||||
m_gizmos.update_section_view();
|
||||
// Set from the overlay, after this frame's scene was drawn.
|
||||
set_as_dirty();
|
||||
request_extra_frame();
|
||||
}
|
||||
|
||||
void GLCanvas3D::toggle_world_axes_visibility(bool force_show)
|
||||
{
|
||||
if (force_show) {
|
||||
@@ -2339,6 +2379,8 @@ void GLCanvas3D::_render_scene(const Camera& camera, const Size& cnv_size)
|
||||
}
|
||||
/* preview render */
|
||||
else if (m_canvas_type == ECanvasType::CanvasPreview && m_render_preview) {
|
||||
// Before the shadow pass, so the cut away part casts no shadow.
|
||||
m_gcode_viewer.set_clipping_plane(_get_section_view_plane());
|
||||
_render_objects(GLVolumeCollection::ERenderType::Opaque, !m_gizmos.is_running());
|
||||
_render_sla_slices();
|
||||
_render_selection();
|
||||
@@ -3121,7 +3163,6 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
|
||||
m_scene_raycaster.remove_raycasters(SceneRaycaster::EType::Bed);
|
||||
m_scene_raycaster.remove_raycasters(SceneRaycaster::EType::Volume);
|
||||
m_gizmos.update_data();
|
||||
m_gizmos.update_assemble_view_data();
|
||||
m_gizmos.refresh_on_off_state();
|
||||
|
||||
// Update the toolbar
|
||||
@@ -3837,12 +3878,18 @@ void GLCanvas3D::on_key(wxKeyEvent& evt)
|
||||
m_key_down = { keyCode, key_repeats(keyCode) };
|
||||
else if (evt.GetEventType() == wxEVT_KEY_UP)
|
||||
key_released(keyCode);
|
||||
const bool alt_wheel_released = evt.GetEventType() == wxEVT_KEY_UP && keyCode == WXK_ALT && std::exchange(m_alt_wheel_used, false);
|
||||
|
||||
auto imgui = wxGetApp().imgui();
|
||||
if (imgui->update_key_data(evt))
|
||||
render();
|
||||
else
|
||||
{
|
||||
// Before the gizmos and the selection, which Esc also closes.
|
||||
if (evt.GetEventType() == wxEVT_KEY_DOWN && keyCode == WXK_ESCAPE && std::exchange(m_section_view->panel_open, false)) {
|
||||
_set_overlay_as_dirty();
|
||||
return;
|
||||
}
|
||||
if (!m_gizmos.on_key(evt)) {
|
||||
if (evt.GetEventType() == wxEVT_KEY_UP) {
|
||||
if (evt.ShiftDown() && evt.ControlDown() && keyCode == WXK_SPACE) {
|
||||
@@ -3911,7 +3958,8 @@ void GLCanvas3D::on_key(wxKeyEvent& evt)
|
||||
&& keyCode != WXK_LEFT
|
||||
&& keyCode != WXK_UP
|
||||
&& keyCode != WXK_RIGHT
|
||||
&& keyCode != WXK_DOWN) {
|
||||
&& keyCode != WXK_DOWN
|
||||
&& !alt_wheel_released) {
|
||||
evt.Skip(); // Needed to have EVT_CHAR generated as well
|
||||
}
|
||||
}
|
||||
@@ -3931,6 +3979,8 @@ void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt)
|
||||
if (evt.MiddleIsDown())
|
||||
return;
|
||||
|
||||
m_alt_wheel_used |= evt.AltDown();
|
||||
|
||||
#if ENABLE_RETINA_GL
|
||||
const float scale = m_retina_helper->get_scale_factor();
|
||||
evt.SetX(evt.GetX() * scale);
|
||||
@@ -3976,25 +4026,21 @@ void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt)
|
||||
if (m_gizmos.on_mouse_wheel(evt))
|
||||
return;
|
||||
|
||||
if (m_canvas_type == CanvasAssembleView && (evt.AltDown() || evt.CmdDown()) && m_gizmos.m_assemble_view_data != nullptr) {
|
||||
if (evt.AltDown()) {
|
||||
set_section_view_ratio(get_section_view_ratio() + (evt.GetWheelRotation() < 0 ? -0.01 : 0.01));
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_canvas_type == CanvasAssembleView && evt.CmdDown()) {
|
||||
float rotation = (float)evt.GetWheelRotation() / (float)evt.GetWheelDelta();
|
||||
if (evt.AltDown()) {
|
||||
auto clp_dist = m_gizmos.m_assemble_view_data->model_objects_clipper()->get_position();
|
||||
clp_dist = rotation < 0.f
|
||||
? std::max(0., clp_dist - 0.01)
|
||||
: std::min(1., clp_dist + 0.01);
|
||||
m_gizmos.m_assemble_view_data->model_objects_clipper()->set_position(clp_dist, true);
|
||||
}
|
||||
else if (evt.CmdDown()) {
|
||||
m_explosion_ratio = rotation < 0.f
|
||||
? std::max(1., m_explosion_ratio - 0.01)
|
||||
: std::min(3., m_explosion_ratio + 0.01);
|
||||
if (m_explosion_ratio != GLVolume::explosion_ratio) {
|
||||
for (GLVolume* volume : m_volumes.volumes) {
|
||||
volume->set_bounding_boxes_as_dirty();
|
||||
}
|
||||
GLVolume::explosion_ratio = m_explosion_ratio;
|
||||
m_explosion_ratio = rotation < 0.f
|
||||
? std::max(1., m_explosion_ratio - 0.01)
|
||||
: std::min(3., m_explosion_ratio + 0.01);
|
||||
if (m_explosion_ratio != GLVolume::explosion_ratio) {
|
||||
for (GLVolume* volume : m_volumes.volumes) {
|
||||
volume->set_bounding_boxes_as_dirty();
|
||||
}
|
||||
GLVolume::explosion_ratio = m_explosion_ratio;
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -8556,7 +8602,7 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with
|
||||
|
||||
glsafe(::glEnable(GL_DEPTH_TEST));
|
||||
|
||||
m_camera_clipping_plane = m_gizmos.get_clipping_plane();
|
||||
m_camera_clipping_plane = _get_volumes_clipping_plane();
|
||||
|
||||
if (m_picking_enabled)
|
||||
// Update the layer editing selection to the first object selected, update the current object maximum Z.
|
||||
@@ -8599,10 +8645,7 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with
|
||||
|
||||
GLGizmosManager& gm = get_gizmos_manager();
|
||||
GLGizmoBase* current_gizmo = gm.get_current();
|
||||
if (m_canvas_type == CanvasAssembleView) {
|
||||
m_volumes.set_clipping_plane(m_gizmos.get_assemble_view_clipping_plane().get_data());
|
||||
}
|
||||
else if (current_gizmo && !current_gizmo->apply_clipping_plane()) {
|
||||
if (current_gizmo && !current_gizmo->apply_clipping_plane()) {
|
||||
m_volumes.set_clipping_plane(ClippingPlane::ClipsNothing().get_data());
|
||||
}
|
||||
else {
|
||||
@@ -8711,6 +8754,7 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with
|
||||
}
|
||||
}
|
||||
|
||||
_render_section_view_caps();
|
||||
break;
|
||||
}
|
||||
case GLVolumeCollection::ERenderType::Transparent:
|
||||
@@ -8732,12 +8776,6 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with
|
||||
}
|
||||
},
|
||||
partly_inside_enable, printable_heights);
|
||||
if (m_canvas_type == CanvasAssembleView && m_gizmos.m_assemble_view_data->model_objects_clipper()->get_position() > 0) {
|
||||
const GLGizmosManager& gm = get_gizmos_manager();
|
||||
shader->stop_using();
|
||||
gm.render_painter_assemble_view();
|
||||
shader->start_using();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -8758,6 +8796,42 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with
|
||||
m_camera_clipping_plane = ClippingPlane::ClipsNothing();
|
||||
}
|
||||
|
||||
void GLCanvas3D::_render_section_view_caps()
|
||||
{
|
||||
// A gizmo with its own clipper draws its own cut faces.
|
||||
const ClippingPlane plane = m_gizmos.get_clipping_plane().has_value() ? ClippingPlane::ClipsNothing() : _get_section_view_plane();
|
||||
if (!plane.is_active() || m_model == nullptr) {
|
||||
m_section_view_caps.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
// Last frame's clippers are reused, so unchanged cuts are not recomputed.
|
||||
std::map<const GLVolume*, MeshClipper> caps;
|
||||
for (const GLVolume* volume : m_volumes.volumes) {
|
||||
if (!volume->is_active || volume->is_modifier || volume->is_wipe_tower || volume->volume_idx() < 0 ||
|
||||
volume->object_idx() >= int(m_model->objects.size()))
|
||||
continue;
|
||||
const ModelObject* object = m_model->objects[volume->object_idx()];
|
||||
if (volume->volume_idx() >= int(object->volumes.size()))
|
||||
continue;
|
||||
// Skip volumes the plane misses.
|
||||
const BoundingBoxf3& box = volume->transformed_bounding_box();
|
||||
if (std::abs(plane.distance(box.center())) > 0.5 * box.size().dot(plane.get_normal().cwiseAbs()))
|
||||
continue;
|
||||
|
||||
auto node = m_section_view_caps.extract(volume);
|
||||
MeshClipper& clipper = node ? caps.insert(std::move(node)).position->second : caps[volume];
|
||||
clipper.set_mesh(object->volumes[volume->volume_idx()]->mesh().its);
|
||||
clipper.set_plane(plane);
|
||||
clipper.set_transformation(Geometry::Transformation(volume->world_matrix()));
|
||||
// No cut face below the plate.
|
||||
if (m_canvas_type != CanvasAssembleView)
|
||||
clipper.set_limiting_plane(ClippingPlane(Vec3d::UnitZ(), -SINKING_Z_THRESHOLD));
|
||||
clipper.render_cut({ 0.25f, 0.25f, 0.25f, 1.0f });
|
||||
}
|
||||
m_section_view_caps = std::move(caps);
|
||||
}
|
||||
|
||||
bool GLCanvas3D::_is_xray_view_active() const
|
||||
{
|
||||
if (m_canvas_type == ECanvasType::CanvasPreview || !wxGetApp().plater()->is_show_xray())
|
||||
@@ -9846,7 +9920,31 @@ void GLCanvas3D::_render_canvas_toolbar()
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding , padding); // without padding images clipping
|
||||
|
||||
imgui.begin(_L("Canvas Toolbar"), ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoMove |
|
||||
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse);//
|
||||
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoScrollWithMouse);//
|
||||
|
||||
// Section view, on top so its panel opens above the toolbar.
|
||||
const bool section_view = is_section_view_active();
|
||||
ImTextureID s_normal_id = m_gizmos.get_icon_texture_id(section_view ?
|
||||
(m_is_dark ? GLGizmosManager::MENU_ICON_NAME::IC_CANVAS_SECTION_ACTIVE_DARK : GLGizmosManager::MENU_ICON_NAME::IC_CANVAS_SECTION_ACTIVE) :
|
||||
(m_is_dark ? GLGizmosManager::MENU_ICON_NAME::IC_CANVAS_SECTION_DARK : GLGizmosManager::MENU_ICON_NAME::IC_CANVAS_SECTION));
|
||||
ImTextureID s_hover_id = m_gizmos.get_icon_texture_id(section_view ?
|
||||
(m_is_dark ? GLGizmosManager::MENU_ICON_NAME::IC_CANVAS_SECTION_ACTIVE_DARK_HOVER : GLGizmosManager::MENU_ICON_NAME::IC_CANVAS_SECTION_ACTIVE_HOVER) :
|
||||
(m_is_dark ? GLGizmosManager::MENU_ICON_NAME::IC_CANVAS_SECTION_DARK_HOVER : GLGizmosManager::MENU_ICON_NAME::IC_CANVAS_SECTION_HOVER));
|
||||
|
||||
if (ImGui::ImageButton3(s_normal_id, s_hover_id, btn_size))
|
||||
m_section_view->panel_open = !m_section_view->panel_open;
|
||||
else if (ImGui::IsItemHovered()) {
|
||||
if (const float wheel = ImGui::GetIO().MouseWheel; wheel != 0.f)
|
||||
set_section_view_ratio(get_section_view_ratio() + (wheel < 0.f ? -0.01 : 0.01));
|
||||
if (ImGui::IsMouseClicked(ImGuiMouseButton_Right))
|
||||
toggle_section_view();
|
||||
else if (ImGui::IsMouseClicked(ImGuiMouseButton_Middle))
|
||||
align_section_view_to_camera();
|
||||
imgui.tooltip(_L("Section view"), ImGui::GetFontSize() * 20.0f);
|
||||
}
|
||||
const ImVec2 section_panel_pos = ImGui::GetItemRectMin() - ImVec2(0.f, spacing.y);
|
||||
|
||||
ImGui::Dummy({ 0, spacing.y});
|
||||
|
||||
ImTextureID m_normal_id = m_gizmos.get_icon_texture_id(m_is_dark ? GLGizmosManager::MENU_ICON_NAME::IC_CANVAS_MENU_DARK : GLGizmosManager::MENU_ICON_NAME::IC_CANVAS_MENU);
|
||||
ImTextureID m_hover_id = m_gizmos.get_icon_texture_id(m_is_dark ? GLGizmosManager::MENU_ICON_NAME::IC_CANVAS_MENU_DARK_HOVER : GLGizmosManager::MENU_ICON_NAME::IC_CANVAS_MENU_HOVER);
|
||||
@@ -10012,6 +10110,76 @@ void GLCanvas3D::_render_canvas_toolbar()
|
||||
ImGui::PopStyleVar(6);
|
||||
|
||||
imgui.end();
|
||||
|
||||
_render_section_view_panel(section_panel_pos);
|
||||
}
|
||||
|
||||
void GLCanvas3D::_render_section_view_panel(const ImVec2& bottom_left)
|
||||
{
|
||||
if (!m_section_view->panel_open)
|
||||
return;
|
||||
|
||||
ImGuiWrapper& imgui = *wxGetApp().imgui();
|
||||
const float sc = get_scale();
|
||||
|
||||
ImGuiWrapper::push_toolbar_style(sc);
|
||||
ImGui::PushStyleColor(ImGuiCol_WindowBg, m_is_dark ? ImGuiWrapper::COL_TOOLBAR_BG_DARK : ImGuiWrapper::COL_TOOLBAR_BG);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 8.f * sc);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(12.f, 8.f) * sc);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.f, 4.f) * sc);
|
||||
|
||||
ImGui::SetNextWindowPos(bottom_left, ImGuiCond_Always, ImVec2(0.f, 1.f));
|
||||
imgui.begin(std::string("##CanvasSectionView"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove |
|
||||
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoScrollbar);
|
||||
|
||||
const wxString alt = GUI::shortkey_alt_prefix();
|
||||
const std::vector<std::pair<wxString, wxString>> tips = {
|
||||
{ alt + _L("Mouse wheel"), _L("Move section plane") },
|
||||
{ _L("Mouse wheel on icon"), _L("Move section plane") },
|
||||
{ _L("Right click on icon"), _L("Toggle section view") },
|
||||
{ _L("Middle click on icon"), _L("Set viewing angle") },
|
||||
};
|
||||
// render_tooltip_button() puts the tips below y plus the panel height; they go above the panel.
|
||||
const ImGuiStyle& style = ImGui::GetStyle();
|
||||
const float tips_h = tips.size() * (ImGui::GetTextLineHeight() + style.ItemSpacing.y) - style.ItemSpacing.y + 2.f * style.WindowPadding.y;
|
||||
GLGizmoUtils::render_tooltip_button(&imgui, *this, tips, ImGui::GetWindowPos().x,
|
||||
ImGui::GetWindowPos().y - style.ItemSpacing.y - tips_h - ImGui::GetContentRegionMax().y - ImGui::GetFrameHeight());
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGui::AlignTextToFramePadding();
|
||||
imgui.text(_L("Section view"));
|
||||
|
||||
float ratio = float(get_section_view_ratio());
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(imgui.scaled(7.f));
|
||||
bool changed = imgui.bbl_slider_float_style("##section_view", &ratio, 0.f, 1.f, "%.2f", 1.0f, true);
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(1.5f * imgui.get_slider_icon_size().x);
|
||||
changed |= ImGui::BBLDragFloat("##section_view_input", &ratio, 0.05f, 0.0f, 0.0f, "%.2f");
|
||||
if (changed)
|
||||
set_section_view_ratio(ratio);
|
||||
|
||||
ImGui::SameLine();
|
||||
if (imgui.button(_L("Set viewing angle")))
|
||||
align_section_view_to_camera();
|
||||
|
||||
imgui.disabled_begin(!is_section_view_active());
|
||||
ImGui::SameLine();
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.f, 0.f, 0.f, 0.f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.f, 0.f, 0.f, 0.f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.f, 0.f, 0.f, 0.f));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.f);
|
||||
if (imgui.button(wxString(ImGui::RevertBtn) + "##section_view_reset", _L("Reset")))
|
||||
set_section_view_ratio(0.);
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::PopStyleColor(3);
|
||||
imgui.disabled_end();
|
||||
|
||||
imgui.end();
|
||||
|
||||
ImGui::PopStyleVar(3);
|
||||
ImGui::PopStyleColor();
|
||||
ImGuiWrapper::pop_toolbar_style();
|
||||
}
|
||||
|
||||
void GLCanvas3D::_render_separator_toolbar_right() const
|
||||
@@ -10259,17 +10427,12 @@ float GLCanvas3D::_render_assembly_tooltip_button(ImGuiWrapper* imgui_wrapper) c
|
||||
//BBS
|
||||
void GLCanvas3D::_render_assemble_control()
|
||||
{
|
||||
if(m_gizmos.m_assemble_view_data == nullptr)
|
||||
return;
|
||||
|
||||
if (m_canvas_type != ECanvasType::CanvasAssembleView) {
|
||||
GLVolume::explosion_ratio = m_explosion_ratio = 1.0;
|
||||
return;
|
||||
}
|
||||
if (m_gizmos.get_current_type() == GLGizmosManager::EType::MmSegmentation) {
|
||||
m_gizmos.m_assemble_view_data->model_objects_clipper()->set_position(0.0, true);
|
||||
if (m_gizmos.get_current_type() == GLGizmosManager::EType::MmSegmentation)
|
||||
return;
|
||||
}
|
||||
|
||||
ImGuiWrapper* imgui = wxGetApp().imgui();
|
||||
|
||||
@@ -10279,8 +10442,6 @@ void GLCanvas3D::_render_assemble_control()
|
||||
auto canvas_h = float(get_canvas_size().get_height());
|
||||
|
||||
const float text_padding = 7.0f;
|
||||
const float text_size_x = std::max(imgui->calc_text_size(_L("Reset direction")).x + 2 * ImGui::GetStyle().FramePadding.x,
|
||||
std::max(imgui->calc_text_size(_L("Explosion Ratio")).x, imgui->calc_text_size(_L("Section View")).x));
|
||||
const float slider_width = 60.0f;
|
||||
const float value_size = imgui->calc_text_size("3.00"sv).x + text_padding * 2;
|
||||
const float item_spacing = imgui->get_item_spacing().x;
|
||||
@@ -10295,34 +10456,6 @@ void GLCanvas3D::_render_assemble_control()
|
||||
tooltip_button_width = _render_assembly_tooltip_button(imgui);
|
||||
}
|
||||
float same_line_width = tooltip_button_width;
|
||||
{
|
||||
float clp_dist = m_gizmos.m_assemble_view_data->model_objects_clipper()->get_position();
|
||||
if (clp_dist == 0.f) {
|
||||
ImGui::AlignTextToFramePadding();
|
||||
imgui->text(_L("Section View"));
|
||||
}
|
||||
else {
|
||||
if (imgui->button(_L("Reset direction"))) {
|
||||
wxGetApp().CallAfter([this]() {
|
||||
m_gizmos.m_assemble_view_data->model_objects_clipper()->set_position(-1., false);
|
||||
});
|
||||
}
|
||||
}
|
||||
same_line_width += (text_size_x + item_spacing);
|
||||
ImGui::SameLine(same_line_width);
|
||||
ImGui::PushItemWidth(slider_width);
|
||||
bool view_slider_changed = imgui->bbl_slider_float_style("##clp_dist", &clp_dist, 0.f, 1.f, "%.2f", 1.0f, true);
|
||||
|
||||
same_line_width += (slider_width + item_spacing);
|
||||
ImGui::SameLine(same_line_width);
|
||||
ImGui::PushItemWidth(value_size);
|
||||
bool view_input_changed = ImGui::BBLDragFloat("##clp_dist_input", &clp_dist, 0.05f, 0.0f, 0.0f, "%.2f");
|
||||
|
||||
if (view_slider_changed || view_input_changed)
|
||||
m_gizmos.m_assemble_view_data->model_objects_clipper()->set_position(clp_dist, true);
|
||||
|
||||
same_line_width += (value_size + item_spacing * 2);
|
||||
}
|
||||
{
|
||||
auto temp_x = imgui->calc_text_size(_L("Explosion Ratio")).x;
|
||||
ImGui::SameLine(same_line_width);
|
||||
@@ -10746,12 +10879,42 @@ Vec3d GLCanvas3D::_mouse_to_bed_3d(const Point& mouse_pos)
|
||||
return mouse_ray(mouse_pos).intersect_plane(0.0);
|
||||
}
|
||||
|
||||
ClippingPlane GLCanvas3D::_get_section_view_plane() const
|
||||
{
|
||||
const SectionView& section_view = *m_section_view;
|
||||
if (section_view.ratio == 0.)
|
||||
return ClippingPlane::ClipsNothing();
|
||||
|
||||
// The owner's objects, so Preview cuts where Prepare does.
|
||||
const GLCanvas3D& owner = section_view.owner != nullptr ? *section_view.owner : *this;
|
||||
BoundingBoxf3 box = owner.volumes_bounding_box(true);
|
||||
if (!box.defined)
|
||||
box = owner.volumes_bounding_box();
|
||||
// G-code without objects: its toolpaths, merged by corner as a flat box is undefined.
|
||||
if (!box.defined && m_gcode_viewer.has_data()) {
|
||||
box.merge(m_gcode_viewer.get_paths_bounding_box().min);
|
||||
box.merge(m_gcode_viewer.get_paths_bounding_box().max);
|
||||
}
|
||||
if (!box.defined)
|
||||
return ClippingPlane::ClipsNothing();
|
||||
|
||||
// Sweeps the bounding sphere from the camera side (0) to the far side (1), as ObjectClipper does.
|
||||
const double radius = box.radius();
|
||||
return ClippingPlane(section_view.normal, section_view.normal.dot(box.center()) + radius - 2. * radius * section_view.ratio);
|
||||
}
|
||||
|
||||
ClippingPlane GLCanvas3D::_get_volumes_clipping_plane() const
|
||||
{
|
||||
const std::optional<ClippingPlane> gizmo_plane = m_gizmos.get_clipping_plane();
|
||||
return gizmo_plane.has_value() ? *gizmo_plane : _get_section_view_plane().inverted_normal();
|
||||
}
|
||||
|
||||
ClippingPlane GLCanvas3D::get_raycaster_clipping_plane() const
|
||||
{
|
||||
// Orca: Ignore the gizmo clipping plane when the active tool does not apply it, and
|
||||
// invert the result into the convention expected by SceneRaycaster.
|
||||
GLGizmoBase* current_gizmo = m_gizmos.get_current();
|
||||
return ((!current_gizmo || current_gizmo->apply_clipping_plane()) ? m_gizmos.get_clipping_plane() :
|
||||
return ((!current_gizmo || current_gizmo->apply_clipping_plane()) ? _get_volumes_clipping_plane() :
|
||||
ClippingPlane::ClipsNothing())
|
||||
.inverted_normal();
|
||||
}
|
||||
|
||||
@@ -619,6 +619,19 @@ private:
|
||||
std::array<ClippingPlane, 2> m_clipping_planes;
|
||||
ClippingPlane m_camera_clipping_plane;
|
||||
bool m_use_clipping_planes;
|
||||
struct SectionView
|
||||
{
|
||||
explicit SectionView(const GLCanvas3D* owner) : owner(owner) {}
|
||||
double ratio{ 0. }; // 0 = off
|
||||
double last_ratio{ 0. }; // before it was switched off
|
||||
Vec3d normal{ Vec3d::Zero() }; // zero until first aimed
|
||||
bool panel_open{ false };
|
||||
const GLCanvas3D* owner; // whose objects place the plane
|
||||
};
|
||||
std::shared_ptr<SectionView> m_section_view{ std::make_shared<SectionView>(this) };
|
||||
std::map<const GLVolume*, MeshClipper> m_section_view_caps;
|
||||
// Its release would open the hidden window menu on Windows.
|
||||
bool m_alt_wheel_used{ false };
|
||||
std::array<SlaCap, 2> m_sla_caps;
|
||||
std::string m_sidebar_field;
|
||||
// when true renders an extra frame by not resetting m_dirty to false
|
||||
@@ -943,6 +956,14 @@ public:
|
||||
void set_color_clip_plane(const Vec3d& cp_normal, double offset) { m_volumes.set_color_clip_plane(cp_normal, offset); }
|
||||
void set_color_clip_plane_colors(const std::array<ColorRGBA, 2>& colors) { m_volumes.set_color_clip_plane_colors(colors); }
|
||||
|
||||
bool is_section_view_active() const { return m_section_view->ratio > 0.; }
|
||||
double get_section_view_ratio() const { return m_section_view->ratio; }
|
||||
const Vec3d& get_section_view_normal() const { return m_section_view->normal; }
|
||||
void set_section_view_ratio(double ratio);
|
||||
void toggle_section_view();
|
||||
void align_section_view_to_camera();
|
||||
void share_section_view(const GLCanvas3D& owner) { m_section_view = owner.m_section_view; }
|
||||
|
||||
void toggle_world_axes_visibility(bool force_show = false);
|
||||
void refresh_camera_scene_box();
|
||||
void set_color_by(const std::string& value);
|
||||
@@ -1392,6 +1413,7 @@ private:
|
||||
void _render_cad_grid(const Transform3d& view_matrix, const Transform3d& projection_matrix);
|
||||
//BBS: add outline drawing logic
|
||||
void _render_objects(GLVolumeCollection::ERenderType type, bool with_outline = true);
|
||||
void _render_section_view_caps();
|
||||
void _render_wireframe_overlay();
|
||||
bool _is_xray_view_active() const;
|
||||
void _render_xray_volumes();
|
||||
@@ -1418,6 +1440,7 @@ private:
|
||||
void _render_assemble_view_toolbar() const;
|
||||
void _render_return_toolbar() const;
|
||||
void _render_canvas_toolbar();
|
||||
void _render_section_view_panel(const ImVec2& bottom_left);
|
||||
void _render_separator_toolbar_right() const;
|
||||
void _render_separator_toolbar_left() const;
|
||||
void _render_collapse_toolbar() const;
|
||||
@@ -1449,6 +1472,11 @@ private:
|
||||
Gesture
|
||||
};
|
||||
|
||||
void _on_section_view_changed();
|
||||
// In the ClippingPlane::is_point_clipped() convention.
|
||||
ClippingPlane _get_section_view_plane() const;
|
||||
// In the volume shaders' convention: the open gizmo's own plane, else the section view's.
|
||||
ClippingPlane _get_volumes_clipping_plane() const;
|
||||
// Orca: These helpers keep clipping, orbit pivots, and perspective-pan depth selection consistent.
|
||||
ClippingPlane get_raycaster_clipping_plane() const;
|
||||
bool is_bed_visible() const;
|
||||
|
||||
@@ -85,7 +85,6 @@ bool GLGizmoBrimEars::on_init()
|
||||
m_shortcut = Shortcut::GizmoBrimEars;
|
||||
|
||||
const wxString ctrl = GUI::shortkey_ctrl_prefix();
|
||||
const wxString alt = GUI::shortkey_alt_prefix();
|
||||
|
||||
m_desc["brim_ear_radius"] = _L("Brim ear radius");
|
||||
m_desc["max_angle"] = _L("Max angle");
|
||||
@@ -96,13 +95,11 @@ bool GLGizmoBrimEars::on_init()
|
||||
m_desc["create"] = _L("Create");
|
||||
m_desc["auto_generate"] = _L("Auto-generate");
|
||||
m_desc["auto-generate-tooltip"] = _L("Generate brim ears using Max angle and Detection radius");
|
||||
m_desc["section_view"] = _L("Section view");
|
||||
|
||||
m_shortcuts = {
|
||||
{_L("Left mouse button"), _L("Add or Select")},
|
||||
{_L("Right mouse button"), _L("Remove")},
|
||||
{ctrl + _L("Mouse wheel"), m_desc["brim_ear_radius"]},
|
||||
{alt + _L("Mouse wheel"), m_desc["section_view"]},
|
||||
};
|
||||
|
||||
return true;
|
||||
@@ -551,26 +548,6 @@ bool GLGizmoBrimEars::gizmo_event(SLAGizmoEventType action, const Vec2d &mouse_p
|
||||
apply_radius_change();
|
||||
}
|
||||
|
||||
if (action == SLAGizmoEventType::MouseWheelUp && alt_down) {
|
||||
double pos = m_c->object_clipper()->get_position();
|
||||
pos = std::min(1., pos + 0.01);
|
||||
m_c->object_clipper()->set_position_by_ratio(pos, false, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (action == SLAGizmoEventType::MouseWheelDown && alt_down) {
|
||||
double pos = m_c->object_clipper()->get_position();
|
||||
pos = std::max(0., pos - 0.01);
|
||||
m_c->object_clipper()->set_position_by_ratio(pos, false, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
// reset clipper position
|
||||
if (action == SLAGizmoEventType::ResetClippingPlane) {
|
||||
m_c->object_clipper()->set_position_by_ratio(-1., false);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -692,8 +669,8 @@ void GLGizmoBrimEars::on_render_input_window(float x, float y, float bottom_limi
|
||||
ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar);
|
||||
|
||||
float space_size = m_imgui->get_style_scaling() * 8;
|
||||
std::vector<wxString> text_list = {m_desc["brim_ear_radius"], m_desc["max_angle"], m_desc["detection_radius"], m_desc["clipping_of_view"],
|
||||
m_desc["create"], m_desc["remove"]};
|
||||
std::vector<wxString> text_list = {m_desc["brim_ear_radius"], m_desc["max_angle"], m_desc["detection_radius"], m_desc["create"],
|
||||
m_desc["remove"]};
|
||||
float widest_text = m_imgui->find_widest_text(text_list);
|
||||
float caption_size = widest_text + space_size + ImGui::GetStyle().WindowPadding.x;
|
||||
float input_text_size = m_imgui->scaled(10.0f);
|
||||
@@ -782,21 +759,6 @@ void GLGizmoBrimEars::on_render_input_window(float x, float y, float bottom_limi
|
||||
}
|
||||
}
|
||||
m_imgui->disabled_end();
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::AlignTextToFramePadding();
|
||||
float clp_dist = float(m_c->object_clipper()->get_position());
|
||||
m_imgui->text(m_desc["section_view"]);
|
||||
ImGui::SameLine(caption_size);
|
||||
ImGui::PushItemWidth(slider_width);
|
||||
bool slider_clp_dist = m_imgui->bbl_slider_float_style("##section_view", &clp_dist, 0.f, 1.f, "%.2f", 1.0f, true);
|
||||
ImGui::SameLine(drag_left_width);
|
||||
ImGui::PushItemWidth(1.5 * slider_icon_width);
|
||||
bool b_clp_dist_input = ImGui::BBLDragFloat("##section_view_input", &clp_dist, 0.05f, 0.0f, 0.0f, "%.2f");
|
||||
if (slider_clp_dist || b_clp_dist_input) {
|
||||
m_c->object_clipper()->set_position_by_ratio(clp_dist, false, true);
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
|
||||
@@ -112,36 +112,30 @@ bool GLGizmoFdmSupports::on_init()
|
||||
m_desc["highlight_by_angle"] = _L("Highlight overhangs");
|
||||
m_desc["tool_type"] = _L("Tool type");
|
||||
m_desc["gap_fill"] = _L("Gap fill");
|
||||
m_desc["reset_direction"] = _L("Reset direction");
|
||||
m_desc["clipping_of_view"] = _L("Section view");
|
||||
m_desc["cursor_size"] = _L("Brush size");
|
||||
m_desc["smart_fill_angle"] = _L("Smart fill angle");
|
||||
m_desc["gap_area"] = _L("Gap area");
|
||||
|
||||
|
||||
const wxString ctrl = GUI::shortkey_ctrl_prefix();
|
||||
const wxString alt = GUI::shortkey_alt_prefix();
|
||||
const wxString shift = GUI::shortkey_shift_prefix();
|
||||
|
||||
std::pair<wxString, wxString> enforce_shortcut = {_L("Left mouse button"), _L("Enforce supports")};
|
||||
std::pair<wxString, wxString> block_shortcut = {_L("Right mouse button"), _L("Block supports")};
|
||||
std::pair<wxString, wxString> remove_shortcut = {shift + _L("Left mouse button"), _L("Erase")};
|
||||
std::pair<wxString, wxString> clipping_shortcut = {alt + _L("Mouse wheel"), m_desc["clipping_of_view"]};
|
||||
|
||||
m_shortcuts_brush = {
|
||||
enforce_shortcut,
|
||||
block_shortcut,
|
||||
remove_shortcut,
|
||||
{ctrl + _L("Mouse wheel"), m_desc["cursor_size"]},
|
||||
clipping_shortcut
|
||||
{ctrl + _L("Mouse wheel"), m_desc["cursor_size"]}
|
||||
};
|
||||
|
||||
m_shortcuts_bucket_fill = {
|
||||
enforce_shortcut,
|
||||
block_shortcut,
|
||||
remove_shortcut,
|
||||
{ctrl + _L("Mouse wheel"), m_desc["smart_fill_angle"]},
|
||||
clipping_shortcut
|
||||
{ctrl + _L("Mouse wheel"), m_desc["smart_fill_angle"]}
|
||||
};
|
||||
|
||||
m_shortcuts_gap_fill = {
|
||||
@@ -237,11 +231,9 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
|
||||
|
||||
// First calculate width of all the texts that are could possibly be shown. We will decide set the dialog width based on that:
|
||||
const float space_size = m_imgui->get_style_scaling() * 8;
|
||||
const float clipping_slider_left = m_imgui->calc_text_size(m_desc.at("clipping_of_view")).x + m_imgui->scaled(1.5f);
|
||||
const float cursor_slider_left = m_imgui->calc_text_size(m_desc.at("cursor_size")).x + m_imgui->scaled(1.5f);
|
||||
const float gap_fill_slider_left = m_imgui->calc_text_size(m_desc.at("gap_fill")).x + m_imgui->scaled(1.5f);
|
||||
const float highlight_slider_left = m_imgui->calc_text_size(m_desc.at("highlight_by_angle")).x + m_imgui->scaled(1.5f);
|
||||
const float reset_button_slider_left = m_imgui->calc_text_size(m_desc.at("reset_direction")).x + m_imgui->scaled(1.5f) + ImGui::GetStyle().FramePadding.x * 2;
|
||||
const float on_overhangs_only_width = m_imgui->calc_text_size(m_desc["on_overhangs_only"]).x + m_imgui->scaled(1.5f);
|
||||
const float filter_btn_width = m_imgui->calc_text_size(m_desc.at("perform")).x + m_imgui->scaled(1.5f);
|
||||
const float gap_area_txt_width = m_imgui->calc_text_size(m_desc.at("gap_area")).x + m_imgui->scaled(1.5f);
|
||||
@@ -254,14 +246,14 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
|
||||
|
||||
float caption_max = 0.f;
|
||||
float total_text_max = 0.f;
|
||||
for (const auto &t : std::array<std::string, 5>{"enforce", "block", "remove", "cursor_size", "clipping_of_view"}) {
|
||||
for (const auto &t : std::array<std::string, 4>{"enforce", "block", "remove", "cursor_size"}) {
|
||||
caption_max = std::max(caption_max, m_imgui->calc_text_size(m_desc[t + "_caption"]).x);
|
||||
total_text_max = std::max(total_text_max, m_imgui->calc_text_size(m_desc[t]).x);
|
||||
}
|
||||
total_text_max += caption_max + m_imgui->scaled(1.f);
|
||||
caption_max += m_imgui->scaled(1.f);
|
||||
|
||||
const float sliders_left_width = std::max(gap_area_txt_width, std::max(smart_fill_angle_txt_width, std::max(reset_button_slider_left, std::max(std::max(cursor_slider_left, clipping_slider_left), std::max(highlight_slider_left, gap_fill_slider_left)))));
|
||||
const float sliders_left_width = std::max(gap_area_txt_width, std::max(smart_fill_angle_txt_width, std::max(cursor_slider_left, std::max(highlight_slider_left, gap_fill_slider_left))));
|
||||
const float slider_icon_width = m_imgui->get_slider_icon_size().x;
|
||||
const float max_tooltip_width = ImGui::GetFontSize() * 20.0f;
|
||||
|
||||
@@ -458,29 +450,6 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
|
||||
ImGui::PushItemWidth(1.5 * slider_icon_width);
|
||||
ImGui::BBLDragFloat("##angle_threshold_deg_input", &m_highlight_by_angle_threshold_deg, 0.05f, 0.0f, 0.0f, "%.2f");
|
||||
|
||||
ImGui::Separator();
|
||||
if (m_c->object_clipper()->get_position() == 0.f) {
|
||||
ImGui::AlignTextToFramePadding();
|
||||
m_imgui->text(m_desc.at("clipping_of_view"));
|
||||
}
|
||||
else {
|
||||
if (m_imgui->button(m_desc.at("reset_direction"))) {
|
||||
wxGetApp().CallAfter([this]() {
|
||||
m_c->object_clipper()->set_position_by_ratio(-1., false);
|
||||
});
|
||||
}
|
||||
}
|
||||
auto clp_dist = float(m_c->object_clipper()->get_position());
|
||||
ImGui::SameLine(sliders_left_width);
|
||||
ImGui::PushItemWidth(sliders_width);
|
||||
bool b_bbl_slider_float = m_imgui->bbl_slider_float_style("##clp_dist", &clp_dist, 0.f, 1.f, "%.2f", 1.0f, true);
|
||||
|
||||
ImGui::SameLine(drag_left_width + sliders_left_width);
|
||||
ImGui::PushItemWidth(1.5 * slider_icon_width);
|
||||
bool b_drag_input = ImGui::BBLDragFloat("##clp_dist_input", &clp_dist, 0.05f, 0.0f, 0.0f, "%.2f");
|
||||
|
||||
if (b_bbl_slider_float || b_drag_input) m_c->object_clipper()->set_position_by_ratio(clp_dist, true);
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
render_tooltip_button(x, y);
|
||||
|
||||
@@ -51,10 +51,8 @@ bool GLGizmoFuzzySkin::on_init()
|
||||
m_shortcut = Shortcut::GizmoFuzzySkin;
|
||||
|
||||
const wxString ctrl = GUI::shortkey_ctrl_prefix();
|
||||
const wxString alt = GUI::shortkey_alt_prefix();
|
||||
const wxString shift = GUI::shortkey_shift_prefix();
|
||||
|
||||
m_desc["reset_direction"] = _L("Reset direction");
|
||||
m_desc["remove_all"] = _L("Erase all");
|
||||
m_desc["circle"] = _L("Circle");
|
||||
m_desc["sphere"] = _L("Sphere");
|
||||
@@ -62,7 +60,6 @@ bool GLGizmoFuzzySkin::on_init()
|
||||
m_desc["tool_type"] = _L("Tool type");
|
||||
m_desc["tool_brush"] = _L("Brush");
|
||||
m_desc["tool_smart_fill"] = _L("Smart fill");
|
||||
m_desc["clipping_of_view"] = _L("Section view");
|
||||
m_desc["cursor_size"] = _L("Brush size");
|
||||
m_desc["add_fuzzy_skin"] = _L("Add fuzzy skin");
|
||||
m_desc["remove_fuzzy_skin"] = _L("Remove fuzzy skin");
|
||||
@@ -70,26 +67,22 @@ bool GLGizmoFuzzySkin::on_init()
|
||||
|
||||
std::pair<wxString, wxString> add_fuzzy_skin_shortcut = {_L("Left mouse button"), m_desc["add_fuzzy_skin"]};
|
||||
std::pair<wxString, wxString> remove_fuzzy_skin_shortcut = {shift + _L("Left mouse button"), m_desc["remove_fuzzy_skin"]};
|
||||
std::pair<wxString, wxString> clipping_shortcut = {alt + _L("Mouse wheel"), m_desc["clipping_of_view"]};
|
||||
|
||||
m_shortcuts_brush = {
|
||||
add_fuzzy_skin_shortcut,
|
||||
remove_fuzzy_skin_shortcut,
|
||||
{ctrl + _L("Mouse wheel"), m_desc["cursor_size"]},
|
||||
clipping_shortcut
|
||||
{ctrl + _L("Mouse wheel"), m_desc["cursor_size"]}
|
||||
};
|
||||
|
||||
m_shortcuts_triangle = {
|
||||
add_fuzzy_skin_shortcut,
|
||||
remove_fuzzy_skin_shortcut,
|
||||
clipping_shortcut
|
||||
remove_fuzzy_skin_shortcut
|
||||
};
|
||||
|
||||
m_shortcuts_smart_fill = {
|
||||
add_fuzzy_skin_shortcut,
|
||||
remove_fuzzy_skin_shortcut,
|
||||
{ctrl + _L("Mouse wheel"), m_desc["smart_fill_angle"]},
|
||||
clipping_shortcut
|
||||
{ctrl + _L("Mouse wheel"), m_desc["smart_fill_angle"]}
|
||||
};
|
||||
|
||||
return true;
|
||||
@@ -162,8 +155,6 @@ void GLGizmoFuzzySkin::on_render_input_window(float x, float y, float bottom_lim
|
||||
|
||||
// First calculate width of all the texts that are could possibly be shown. We will decide set the dialog width based on that:
|
||||
const float space_size = m_imgui->get_style_scaling() * 8;
|
||||
const float clipping_slider_left = std::max(m_imgui->calc_text_size(m_desc.at("clipping_of_view")).x + m_imgui->scaled(1.5f),
|
||||
m_imgui->calc_text_size(m_desc.at("reset_direction")).x + m_imgui->scaled(1.5f) + ImGui::GetStyle().FramePadding.x * 2);
|
||||
const float cursor_slider_left = m_imgui->calc_text_size(m_desc.at("cursor_size")).x + m_imgui->scaled(1.5f);
|
||||
const float smart_fill_slider_left = m_imgui->calc_text_size(m_desc.at("smart_fill_angle")).x + m_imgui->scaled(1.5f);
|
||||
|
||||
@@ -187,9 +178,7 @@ void GLGizmoFuzzySkin::on_render_input_window(float x, float y, float bottom_lim
|
||||
total_text_max += caption_max + m_imgui->scaled(1.f);
|
||||
caption_max += m_imgui->scaled(1.f);
|
||||
|
||||
const float circle_max_width = std::max(clipping_slider_left, cursor_slider_left);
|
||||
|
||||
const float sliders_left_width = std::max(smart_fill_slider_left, std::max(cursor_slider_left, clipping_slider_left));
|
||||
const float sliders_left_width = std::max(smart_fill_slider_left, cursor_slider_left);
|
||||
const float slider_icon_width = m_imgui->get_slider_icon_size().x;
|
||||
float window_width = minimal_slider_width + sliders_left_width + slider_icon_width;
|
||||
const float empty_button_width = m_imgui->calc_button_size("").x;
|
||||
@@ -259,10 +248,10 @@ void GLGizmoFuzzySkin::on_render_input_window(float x, float y, float bottom_lim
|
||||
|
||||
ImGui::AlignTextToFramePadding();
|
||||
m_imgui->text(m_desc.at("cursor_size"));
|
||||
ImGui::SameLine(circle_max_width);
|
||||
ImGui::SameLine(cursor_slider_left);
|
||||
ImGui::PushItemWidth(sliders_width);
|
||||
m_imgui->bbl_slider_float_style("##cursor_radius", &m_cursor_radius, CursorRadiusMin, CursorRadiusMax, "%.2f", 1.0f, true);
|
||||
ImGui::SameLine(drag_left_width + circle_max_width);
|
||||
ImGui::SameLine(drag_left_width + cursor_slider_left);
|
||||
ImGui::PushItemWidth(1.5 * slider_icon_width);
|
||||
ImGui::BBLDragFloat("##cursor_radius_input", &m_cursor_radius, 0.05f, 0.0f, 0.0f, "%.2f");
|
||||
} else if (m_current_tool == ImGui::TriangleButtonIcon) {
|
||||
@@ -291,30 +280,6 @@ void GLGizmoFuzzySkin::on_render_input_window(float x, float y, float bottom_lim
|
||||
ImGui::BBLDragFloat("##smart_fill_angle_input", &m_smart_fill_angle, 0.05f, 0.0f, 0.0f, "%.2f");
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
if (m_c->object_clipper()->get_position() == 0.f) {
|
||||
ImGui::AlignTextToFramePadding();
|
||||
m_imgui->text(m_desc.at("clipping_of_view"));
|
||||
}
|
||||
else {
|
||||
if (m_imgui->button(m_desc.at("reset_direction"))) {
|
||||
wxGetApp().CallAfter([this](){
|
||||
m_c->object_clipper()->set_position_by_ratio(-1., false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
auto clp_dist = float(m_c->object_clipper()->get_position());
|
||||
ImGui::SameLine(sliders_left_width);
|
||||
|
||||
ImGui::PushItemWidth(sliders_width);
|
||||
bool slider_clp_dist = m_imgui->bbl_slider_float_style("##clp_dist", &clp_dist, 0.f, 1.f, "%.2f", 1.0f, true);
|
||||
|
||||
ImGui::SameLine(drag_left_width + sliders_left_width);
|
||||
ImGui::PushItemWidth(1.5 * slider_icon_width);
|
||||
bool b_clp_dist_input = ImGui::BBLDragFloat("##clp_dist_input", &clp_dist, 0.05f, 0.0f, 0.0f, "%.2f");
|
||||
if (slider_clp_dist || b_clp_dist_input) { m_c->object_clipper()->set_position_by_ratio(clp_dist, true); }
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
render_tooltip_button(x, y);
|
||||
|
||||
@@ -118,8 +118,6 @@ bool GLGizmoMmuSegmentation::on_init()
|
||||
const wxString alt = GUI::shortkey_alt_prefix();
|
||||
const wxString shift = GUI::shortkey_shift_prefix();
|
||||
|
||||
m_desc["clipping_of_view"] = _L("Section view");
|
||||
m_desc["reset_direction"] = _L("Reset direction");
|
||||
m_desc["cursor_size"] = _L("Brush size");
|
||||
m_desc["cursor_type"] = _L("Brush shape");
|
||||
m_desc["paint"] = _L("Paint");
|
||||
@@ -146,14 +144,12 @@ bool GLGizmoMmuSegmentation::on_init()
|
||||
|
||||
std::pair<wxString, wxString> paint_shortcut = {_L("Left mouse button"), m_desc["paint"]};
|
||||
std::pair<wxString, wxString> erase_shortcut = {shift + _L("Left mouse button"), m_desc["erase"]};
|
||||
std::pair<wxString, wxString> clipping_shortcut = {alt + _L("Mouse wheel"), m_desc["clipping_of_view"]};
|
||||
std::pair<wxString, wxString> toggle_wireframe_shortcut = {alt + shift + _L_CONTEXT("Enter", "Keyboard Shortcut"), m_desc["toggle_wireframe"]};
|
||||
|
||||
m_shortcuts_brush = {
|
||||
paint_shortcut,
|
||||
erase_shortcut,
|
||||
{ctrl + _L("Mouse wheel"), m_desc["cursor_size"]},
|
||||
clipping_shortcut,
|
||||
toggle_wireframe_shortcut
|
||||
};
|
||||
|
||||
@@ -161,7 +157,6 @@ bool GLGizmoMmuSegmentation::on_init()
|
||||
paint_shortcut,
|
||||
erase_shortcut,
|
||||
{ctrl + _L("Mouse wheel"), m_desc["smart_fill_angle"]},
|
||||
clipping_shortcut,
|
||||
toggle_wireframe_shortcut
|
||||
};
|
||||
|
||||
@@ -397,8 +392,6 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
|
||||
|
||||
// First calculate width of all the texts that are could possibly be shown. We will decide set the dialog width based on that:
|
||||
const float space_size = m_imgui->get_style_scaling() * 8;
|
||||
const float clipping_slider_left = std::max(m_imgui->calc_text_size(m_desc.at("clipping_of_view")).x + m_imgui->scaled(1.5f),
|
||||
m_imgui->calc_text_size(m_desc.at("reset_direction")).x + m_imgui->scaled(1.5f) + ImGui::GetStyle().FramePadding.x * 2);
|
||||
const float cursor_slider_left = m_imgui->calc_text_size(m_desc.at("cursor_size")).x + m_imgui->scaled(1.5f);
|
||||
const float smart_fill_slider_left = m_imgui->calc_text_size(m_desc.at("smart_fill_angle")).x + m_imgui->scaled(1.5f);
|
||||
const float edge_detect_slider_left = m_imgui->calc_text_size(m_desc.at("edge_detection")).x + m_imgui->scaled(1.f);
|
||||
@@ -413,18 +406,15 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
|
||||
|
||||
float caption_max = 0.f;
|
||||
float total_text_max = 0.f;
|
||||
for (const auto &t : std::array<std::string, 6>{"paint", "erase", "cursor_size", "smart_fill_angle", "height_range", "clipping_of_view"}) {
|
||||
for (const auto &t : std::array<std::string, 5>{"paint", "erase", "cursor_size", "smart_fill_angle", "height_range"}) {
|
||||
caption_max = std::max(caption_max, m_imgui->calc_text_size(m_desc[t + "_caption"]).x);
|
||||
total_text_max = std::max(total_text_max, m_imgui->calc_text_size(m_desc[t]).x);
|
||||
}
|
||||
total_text_max += caption_max + m_imgui->scaled(1.f);
|
||||
caption_max += m_imgui->scaled(1.f);
|
||||
|
||||
const float circle_max_width = std::max(clipping_slider_left,cursor_slider_left);
|
||||
const float height_max_width = std::max(clipping_slider_left,height_range_slider_left);
|
||||
const float sliders_left_width = std::max(smart_fill_slider_left,
|
||||
std::max(cursor_slider_left, std::max(edge_detect_slider_left, std::max(gap_area_slider_left, std::max(height_range_slider_left,
|
||||
clipping_slider_left))))) + space_size;
|
||||
std::max(cursor_slider_left, std::max(edge_detect_slider_left, std::max(gap_area_slider_left, height_range_slider_left)))) + space_size;
|
||||
const float slider_icon_width = m_imgui->get_slider_icon_size().x;
|
||||
float window_width = minimal_slider_width + sliders_left_width + slider_icon_width;
|
||||
const int max_filament_items_per_line = 8;
|
||||
@@ -658,28 +648,6 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
if (m_c->object_clipper()->get_position() == 0.f) {
|
||||
ImGui::AlignTextToFramePadding();
|
||||
m_imgui->text(m_desc.at("clipping_of_view"));
|
||||
} else {
|
||||
if (m_imgui->button(m_desc.at("reset_direction"))) {
|
||||
wxGetApp().CallAfter([this]() { m_c->object_clipper()->set_position_by_ratio(-1., false); });
|
||||
}
|
||||
}
|
||||
|
||||
auto clp_dist = float(m_c->object_clipper()->get_position());
|
||||
ImGui::SameLine(sliders_left_width);
|
||||
ImGui::PushItemWidth(sliders_width);
|
||||
bool slider_clp_dist = m_imgui->bbl_slider_float_style("##clp_dist", &clp_dist, 0.f, 1.f, "%.2f", 1.0f, true);
|
||||
ImGui::SameLine(drag_left_width + sliders_left_width);
|
||||
ImGui::PushItemWidth(1.5 * slider_icon_width);
|
||||
bool b_clp_dist_input = ImGui::BBLDragFloat("##clp_dist_input", &clp_dist, 0.05f, 0.0f, 0.0f, "%.2f");
|
||||
|
||||
if (slider_clp_dist || b_clp_dist_input) {
|
||||
m_c->object_clipper()->set_position_by_ratio(clp_dist, true);
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
render_tooltip_button(x, y);
|
||||
|
||||
@@ -750,20 +750,6 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (alt_down) {
|
||||
// BBS
|
||||
double pos = m_c->object_clipper()->get_position();
|
||||
pos = action == SLAGizmoEventType::MouseWheelDown
|
||||
? std::max(0., pos - 0.01)
|
||||
: std::min(1., pos + 0.01);
|
||||
m_c->object_clipper()->set_position_by_ratio(pos, true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (action == SLAGizmoEventType::ResetClippingPlane) {
|
||||
m_c->object_clipper()->set_position_by_ratio(-1., false);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (action == SLAGizmoEventType::LeftDown
|
||||
|
||||
@@ -46,11 +46,8 @@ bool GLGizmoSeam::on_init()
|
||||
m_shortcut = Shortcut::GizmoSeam;
|
||||
|
||||
const wxString ctrl = GUI::shortkey_ctrl_prefix();
|
||||
const wxString alt = GUI::shortkey_alt_prefix();
|
||||
const wxString shift = GUI::shortkey_shift_prefix();
|
||||
|
||||
m_desc["clipping_of_view"] = _L("Section view");
|
||||
m_desc["reset_direction"] = _L("Reset direction");
|
||||
m_desc["cursor_size"] = _L("Brush size");
|
||||
m_desc["tool_type"] = _L("Tool type");
|
||||
m_desc["enforce"] = _L("Enforce seam");
|
||||
@@ -64,8 +61,7 @@ bool GLGizmoSeam::on_init()
|
||||
{_L("Left mouse button"), m_desc["enforce"]},
|
||||
{_L("Right mouse button"), m_desc["block"]},
|
||||
{shift + _L("Left mouse button"), m_desc["remove"]},
|
||||
{ctrl + _L("Mouse wheel"), m_desc["cursor_size"]},
|
||||
{alt + _L("Mouse wheel"), m_desc["clipping_of_view"]}
|
||||
{ctrl + _L("Mouse wheel"), m_desc["cursor_size"]}
|
||||
};
|
||||
|
||||
return true;
|
||||
@@ -155,20 +151,17 @@ void GLGizmoSeam::on_render_input_window(float x, float y, float bottom_limit)
|
||||
|
||||
// First calculate width of all the texts that are could possibly be shown. We will decide set the dialog width based on that:
|
||||
const float space_size = m_imgui->get_style_scaling() * 8;
|
||||
const float clipping_slider_left = std::max(m_imgui->calc_text_size(m_desc.at("clipping_of_view")).x,
|
||||
m_imgui->calc_text_size(m_desc.at("reset_direction")).x + ImGui::GetStyle().FramePadding.x * 2)
|
||||
+ m_imgui->scaled(1.5f);
|
||||
const float cursor_size_slider_left = m_imgui->calc_text_size(m_desc.at("cursor_size")).x + m_imgui->scaled(1.f);
|
||||
const float empty_button_width = m_imgui->calc_button_size("").x;
|
||||
|
||||
float caption_max = 0.f;
|
||||
float total_text_max = 0.f;
|
||||
for (const auto &t : std::array<std::string, 6>{"enforce", "block", "remove", "cursor_size", "clipping_of_view"}) {
|
||||
for (const auto &t : std::array<std::string, 4>{"enforce", "block", "remove", "cursor_size"}) {
|
||||
caption_max = std::max(caption_max, m_imgui->calc_text_size(m_desc[t + "_caption"]).x);
|
||||
total_text_max = std::max(total_text_max, m_imgui->calc_text_size(m_desc[t]).x);
|
||||
}
|
||||
|
||||
const float sliders_left_width = std::max(cursor_size_slider_left, clipping_slider_left);
|
||||
const float sliders_left_width = cursor_size_slider_left;
|
||||
const float slider_icon_width = m_imgui->get_slider_icon_size().x;
|
||||
|
||||
const float sliders_width = m_imgui->scaled(7.0f);
|
||||
@@ -242,28 +235,6 @@ void GLGizmoSeam::on_render_input_window(float x, float y, float bottom_limit)
|
||||
|
||||
m_imgui->bbl_checkbox(_L("Vertical"), m_vertical_only);
|
||||
|
||||
ImGui::Separator();
|
||||
if (m_c->object_clipper()->get_position() == 0.f) {
|
||||
ImGui::AlignTextToFramePadding();
|
||||
m_imgui->text(m_desc.at("clipping_of_view"));
|
||||
}
|
||||
else {
|
||||
if (m_imgui->button(m_desc.at("reset_direction"))) {
|
||||
wxGetApp().CallAfter([this](){
|
||||
m_c->object_clipper()->set_position_by_ratio(-1., false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
auto clp_dist = float(m_c->object_clipper()->get_position());
|
||||
ImGui::SameLine(sliders_left_width);
|
||||
ImGui::PushItemWidth(sliders_width);
|
||||
bool slider_clp_dist = m_imgui->bbl_slider_float_style("##clp_dist", &clp_dist, 0.f, 1.f, "%.2f", 1.0f, true);
|
||||
ImGui::SameLine(drag_left_width + sliders_left_width);
|
||||
ImGui::PushItemWidth(1.5 * slider_icon_width);
|
||||
bool b_clp_dist_input = ImGui::BBLDragFloat("##clp_dist_input", &clp_dist, 0.05f, 0.0f, 0.0f, "%.2f");
|
||||
if (slider_clp_dist || b_clp_dist_input) { m_c->object_clipper()->set_position_by_ratio(clp_dist, true); }
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
GLGizmoUtils::render_tooltip_button(m_imgui, m_parent, m_shortcuts, x, y);
|
||||
|
||||
@@ -380,22 +380,19 @@ std::vector<Vec3d> ObjectClipper::point_per_contour() const
|
||||
}
|
||||
|
||||
|
||||
void ObjectClipper::set_position_by_ratio(double pos, bool keep_normal, bool vertical_normal)
|
||||
void ObjectClipper::set_position_by_ratio(double pos, const Vec3d& normal)
|
||||
{
|
||||
const ModelObject* mo = get_pool()->selection_info()->model_object();
|
||||
int active_inst = get_pool()->selection_info()->get_active_instance();
|
||||
double z_shift = get_pool()->selection_info()->get_sla_shift();
|
||||
|
||||
Vec3d normal;
|
||||
if(vertical_normal) {
|
||||
normal = {0, 0, 1};
|
||||
}else {
|
||||
//Vec3d camera_dir = wxGetApp().plater()->get_camera().get_dir_forward();
|
||||
//if (abs(camera_dir(0)) > EPSILON || abs(camera_dir(1)) > EPSILON)
|
||||
// camera_dir(2) = 0;
|
||||
|
||||
normal = (keep_normal && m_clp) ? m_clp->get_normal() : /*-camera_dir;*/ -wxGetApp().plater()->get_camera().get_dir_forward();
|
||||
if (pos == 0. || mo == nullptr || active_inst < 0) {
|
||||
// No plane, so the raycasts are not clipped.
|
||||
m_clp_ratio = 0.;
|
||||
m_clp.reset();
|
||||
get_pool()->get_canvas()->set_as_dirty();
|
||||
return;
|
||||
}
|
||||
|
||||
Vec3d center;
|
||||
|
||||
if (get_pool()->get_canvas()->get_canvas_type() == GLCanvas3D::CanvasAssembleView) {
|
||||
@@ -410,9 +407,6 @@ void ObjectClipper::set_position_by_ratio(double pos, bool keep_normal, bool ver
|
||||
|
||||
float dist = normal.dot(center);
|
||||
|
||||
if (pos < 0.)
|
||||
pos = m_clp_ratio;
|
||||
|
||||
m_clp_ratio = pos;
|
||||
|
||||
m_clp.reset(new ClippingPlane(normal, (dist - (-m_active_inst_bb_radius) - m_clp_ratio * 2 * m_active_inst_bb_radius)));
|
||||
@@ -445,171 +439,5 @@ void ObjectClipper::set_behavior(bool hide_clipped, bool fill_cut, double contou
|
||||
}
|
||||
|
||||
|
||||
using namespace AssembleViewDataObjects;
|
||||
AssembleViewDataPool::AssembleViewDataPool(GLCanvas3D* canvas)
|
||||
: m_canvas(canvas)
|
||||
{
|
||||
using c = AssembleViewDataID;
|
||||
m_data[c::ModelObjectsInfo].reset(new ModelObjectsInfo(this));
|
||||
m_data[c::ModelObjectsClipper].reset(new ModelObjectsClipper(this));
|
||||
}
|
||||
|
||||
void AssembleViewDataPool::update(AssembleViewDataID required)
|
||||
{
|
||||
assert(check_dependencies(required));
|
||||
for (auto& [id, data] : m_data) {
|
||||
if (int(required) & int(AssembleViewDataID(id)))
|
||||
data->update();
|
||||
else
|
||||
if (data->is_valid())
|
||||
data->release();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ModelObjectsInfo* AssembleViewDataPool::model_objects_info() const
|
||||
{
|
||||
ModelObjectsInfo* sel_info = dynamic_cast<ModelObjectsInfo*>(m_data.at(AssembleViewDataID::ModelObjectsInfo).get());
|
||||
assert(sel_info);
|
||||
return sel_info->is_valid() ? sel_info : nullptr;
|
||||
}
|
||||
|
||||
|
||||
ModelObjectsClipper* AssembleViewDataPool::model_objects_clipper() const
|
||||
{
|
||||
ModelObjectsClipper* oc = dynamic_cast<ModelObjectsClipper*>(m_data.at(AssembleViewDataID::ModelObjectsClipper).get());
|
||||
// ObjectClipper is used from outside the gizmos to report current clipping plane.
|
||||
// This function can be called when oc is nullptr.
|
||||
return (oc && oc->is_valid()) ? oc : nullptr;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
// Check the required resources one by one and return true if all
|
||||
// dependencies are met.
|
||||
bool AssembleViewDataPool::check_dependencies(AssembleViewDataID required) const
|
||||
{
|
||||
// This should iterate over currently required data. Each of them should
|
||||
// be asked about its dependencies and it must check that all dependencies
|
||||
// are also in required and before the current one.
|
||||
for (auto& [id, data] : m_data) {
|
||||
// in case we don't use this, the deps are irrelevant
|
||||
if (!(int(required) & int(AssembleViewDataID(id))))
|
||||
continue;
|
||||
|
||||
|
||||
AssembleViewDataID deps = data->get_dependencies();
|
||||
assert(int(deps) == (int(deps) & int(required)));
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif // NDEBUG
|
||||
|
||||
|
||||
|
||||
|
||||
void ModelObjectsInfo::on_update()
|
||||
{
|
||||
if (!get_pool()->get_canvas()->get_model()->objects.empty()) {
|
||||
m_model_objects = get_pool()->get_canvas()->get_model()->objects;
|
||||
}
|
||||
else {
|
||||
m_model_objects.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void ModelObjectsInfo::on_release()
|
||||
{
|
||||
m_model_objects.clear();
|
||||
}
|
||||
|
||||
//int ModelObjectsInfo::get_active_instance() const
|
||||
//{
|
||||
// const Selection& selection = get_pool()->get_canvas()->get_selection();
|
||||
// return selection.get_instance_idx();
|
||||
//}
|
||||
|
||||
|
||||
void ModelObjectsClipper::on_update()
|
||||
{
|
||||
const ModelObjectPtrs model_objects = get_pool()->model_objects_info()->model_objects();
|
||||
if (model_objects.empty())
|
||||
return;
|
||||
|
||||
// which mesh should be cut?
|
||||
std::vector<const TriangleMesh*> meshes;
|
||||
|
||||
if (meshes.empty())
|
||||
for (auto mo : model_objects) {
|
||||
for (const ModelVolume* mv : mo->volumes)
|
||||
meshes.push_back(&mv->mesh());
|
||||
}
|
||||
|
||||
if (meshes != m_old_meshes) {
|
||||
m_clippers.clear();
|
||||
for (const TriangleMesh* mesh : meshes) {
|
||||
m_clippers.emplace_back(new MeshClipper);
|
||||
m_clippers.back()->set_mesh(mesh->its);
|
||||
}
|
||||
m_old_meshes = meshes;
|
||||
|
||||
m_active_inst_bb_radius = get_pool()->get_canvas()->volumes_bounding_box().radius();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ModelObjectsClipper::on_release()
|
||||
{
|
||||
m_clippers.clear();
|
||||
m_old_meshes.clear();
|
||||
m_clp.reset();
|
||||
m_clp_ratio = 0.;
|
||||
|
||||
}
|
||||
|
||||
void ModelObjectsClipper::render_cut() const
|
||||
{
|
||||
if (m_clp_ratio == 0.)
|
||||
return;
|
||||
const ModelObjectPtrs model_objects = get_pool()->model_objects_info()->model_objects();
|
||||
|
||||
size_t clipper_id = 0;
|
||||
for (const ModelObject* mo : model_objects) {
|
||||
Geometry::Transformation assemble_objects_trafo = mo->instances[0]->get_assemble_transformation();
|
||||
auto offset_to_assembly = mo->instances[0]->get_offset_to_assembly();
|
||||
for (const ModelVolume* mv : mo->volumes) {
|
||||
Geometry::Transformation vol_trafo = mv->get_transformation();
|
||||
Geometry::Transformation trafo = assemble_objects_trafo * vol_trafo;
|
||||
trafo.set_offset(trafo.get_offset() + vol_trafo.get_offset() * (GLVolume::explosion_ratio - 1.0) + offset_to_assembly * (GLVolume::explosion_ratio - 1.0));
|
||||
|
||||
auto& clipper = m_clippers[clipper_id];
|
||||
clipper->set_plane(*m_clp);
|
||||
clipper->set_transformation(trafo);
|
||||
// BBS
|
||||
clipper->render_cut({0.25f, 0.25f, 0.25f, 1.0f});
|
||||
|
||||
++clipper_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ModelObjectsClipper::set_position(double pos, bool keep_normal)
|
||||
{
|
||||
Vec3d normal = (keep_normal && m_clp) ? m_clp->get_normal() : -wxGetApp().plater()->get_camera().get_dir_forward();
|
||||
const Vec3d& center = get_pool()->get_canvas()->volumes_bounding_box().center();
|
||||
float dist = normal.dot(center);
|
||||
|
||||
if (pos < 0.)
|
||||
pos = m_clp_ratio;
|
||||
|
||||
m_clp_ratio = pos;
|
||||
m_clp.reset(new ClippingPlane(normal, (dist - (-m_active_inst_bb_radius * GLVolume::explosion_ratio) - m_clp_ratio * 2 * m_active_inst_bb_radius * GLVolume::explosion_ratio)));
|
||||
get_pool()->get_canvas()->set_as_dirty();
|
||||
|
||||
}
|
||||
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
|
||||
@@ -52,7 +52,6 @@ enum class SLAGizmoEventType : unsigned char {
|
||||
|
||||
|
||||
class CommonGizmosDataBase;
|
||||
class AssembleViewDataBase;
|
||||
namespace CommonGizmosDataObjects {
|
||||
class SelectionInfo;
|
||||
class InstancesHider;
|
||||
@@ -62,11 +61,6 @@ namespace CommonGizmosDataObjects {
|
||||
class SupportsClipper;
|
||||
}
|
||||
|
||||
namespace AssembleViewDataObjects {
|
||||
class ModelObjectsInfo;
|
||||
class ModelObjectsClipper;
|
||||
}
|
||||
|
||||
// Some of the gizmos use the same data that need to be updated ocassionally.
|
||||
// It is also desirable that the data are not recalculated when the gizmos
|
||||
// are just switched, but on the other hand, they should be released when
|
||||
@@ -245,7 +239,7 @@ public:
|
||||
void set_position_to_init_layer();
|
||||
const ClippingPlane* get_clipping_plane(bool ignore_hide_clipped = false) const;
|
||||
void render_cut(const std::vector<size_t>* ignore_idxs = nullptr) const;
|
||||
void set_position_by_ratio(double pos, bool keep_normal, bool vertical_normal=false);
|
||||
void set_position_by_ratio(double pos, const Vec3d& normal);
|
||||
void set_range_and_pos(const Vec3d& cpl_normal, double cpl_offset, double pos);
|
||||
void set_behavior(bool hide_clipped, bool fill_cut, double contour_width);
|
||||
|
||||
@@ -272,121 +266,6 @@ private:
|
||||
} // namespace CommonGizmosDataObjects
|
||||
|
||||
|
||||
enum class AssembleViewDataID {
|
||||
None = 0,
|
||||
ModelObjectsInfo = 1 << 0,
|
||||
ModelObjectsClipper = 1 << 4,
|
||||
};
|
||||
|
||||
class AssembleViewDataPool {
|
||||
public:
|
||||
AssembleViewDataPool(GLCanvas3D* canvas);
|
||||
|
||||
// Update all resources and release what is not used.
|
||||
// Accepts a bitmask of currently required resources.
|
||||
void update(AssembleViewDataID required);
|
||||
|
||||
// Getters for the data that need to be accessed from the gizmos directly.
|
||||
AssembleViewDataObjects::ModelObjectsInfo* model_objects_info() const;
|
||||
AssembleViewDataObjects::ModelObjectsClipper* model_objects_clipper() const;
|
||||
|
||||
GLCanvas3D* get_canvas() const { return m_canvas; }
|
||||
|
||||
private:
|
||||
std::map<AssembleViewDataID, std::unique_ptr<AssembleViewDataBase>> m_data;
|
||||
GLCanvas3D* m_canvas;
|
||||
|
||||
#ifndef NDEBUG
|
||||
bool check_dependencies(AssembleViewDataID required) const;
|
||||
#endif
|
||||
};
|
||||
|
||||
// Base class for a wrapper object managing a single resource.
|
||||
// Each of the enum values above (safe None) will have an object of this kind.
|
||||
class AssembleViewDataBase {
|
||||
public:
|
||||
// Pass a backpointer to the pool, so the individual
|
||||
// objects can communicate with one another.
|
||||
explicit AssembleViewDataBase(AssembleViewDataPool* cgdp)
|
||||
: m_common{ cgdp } {}
|
||||
virtual ~AssembleViewDataBase() {}
|
||||
|
||||
// Update the resource.
|
||||
void update() { on_update(); m_is_valid = true; }
|
||||
|
||||
// Release any data that are stored internally.
|
||||
void release() { on_release(); m_is_valid = false; }
|
||||
|
||||
// Returns whether the resource is currently maintained.
|
||||
bool is_valid() const { return m_is_valid; }
|
||||
|
||||
#ifndef NDEBUG
|
||||
// Return a bitmask of all resources that this one relies on.
|
||||
// The dependent resource must have higher ID than the one
|
||||
// it depends on.
|
||||
virtual AssembleViewDataID get_dependencies() const { return AssembleViewDataID::None; }
|
||||
#endif // NDEBUG
|
||||
|
||||
protected:
|
||||
virtual void on_release() = 0;
|
||||
virtual void on_update() = 0;
|
||||
AssembleViewDataPool* get_pool() const { return m_common; }
|
||||
|
||||
|
||||
private:
|
||||
bool m_is_valid = false;
|
||||
AssembleViewDataPool* m_common = nullptr;
|
||||
};
|
||||
|
||||
namespace AssembleViewDataObjects
|
||||
{
|
||||
class ModelObjectsInfo : public AssembleViewDataBase
|
||||
{
|
||||
public:
|
||||
explicit ModelObjectsInfo(AssembleViewDataPool* cgdp)
|
||||
: AssembleViewDataBase(cgdp) {}
|
||||
|
||||
ModelObjectPtrs model_objects() const { return m_model_objects; }
|
||||
//int get_active_instance() const;
|
||||
float get_sla_shift() const { return m_z_shift; }
|
||||
|
||||
protected:
|
||||
void on_update() override;
|
||||
void on_release() override;
|
||||
|
||||
private:
|
||||
ModelObjectPtrs m_model_objects;
|
||||
float m_z_shift = 0.f;
|
||||
};
|
||||
|
||||
class ModelObjectsClipper : public AssembleViewDataBase
|
||||
{
|
||||
public:
|
||||
explicit ModelObjectsClipper(AssembleViewDataPool* cgdp)
|
||||
: AssembleViewDataBase(cgdp) {}
|
||||
#ifndef NDEBUG
|
||||
AssembleViewDataID get_dependencies() const override { return AssembleViewDataID::ModelObjectsInfo; }
|
||||
#endif // NDEBUG
|
||||
|
||||
void set_position(double pos, bool keep_normal);
|
||||
double get_position() const { return m_clp_ratio; }
|
||||
ClippingPlane* get_clipping_plane() const { return m_clp.get(); }
|
||||
void render_cut() const;
|
||||
|
||||
|
||||
protected:
|
||||
void on_update() override;
|
||||
void on_release() override;
|
||||
|
||||
private:
|
||||
std::vector<const TriangleMesh*> m_old_meshes;
|
||||
std::vector<std::unique_ptr<MeshClipper>> m_clippers;
|
||||
std::unique_ptr<ClippingPlane> m_clp;
|
||||
double m_clp_ratio = 0.;
|
||||
double m_active_inst_bb_radius = 0.;
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
|
||||
|
||||
@@ -278,8 +278,6 @@ bool GLGizmosManager::init()
|
||||
//m_gizmos.emplace_back(new GLGizmoHollow(m_parent, "hollow.svg", sprite_id++));
|
||||
|
||||
m_common_gizmos_data.reset(new CommonGizmosDataPool(&m_parent));
|
||||
if(!m_assemble_view_data)
|
||||
m_assemble_view_data.reset(new AssembleViewDataPool(&m_parent));
|
||||
|
||||
for (auto& gizmo : m_gizmos) {
|
||||
if (! gizmo->init()) {
|
||||
@@ -374,7 +372,17 @@ bool GLGizmosManager::init_icon_textures()
|
||||
icon_list.insert(std::make_pair((int) IC_CANVAS_ZOOM_DARK_HOVER, texture_id));
|
||||
else
|
||||
return false;
|
||||
|
||||
|
||||
for (const auto& [icon, name] : std::initializer_list<std::pair<MENU_ICON_NAME, const char*>>{
|
||||
{ IC_CANVAS_SECTION, "canvas_section" }, { IC_CANVAS_SECTION_HOVER, "canvas_section_hover" },
|
||||
{ IC_CANVAS_SECTION_DARK, "canvas_section_dark" }, { IC_CANVAS_SECTION_DARK_HOVER, "canvas_section_dark_hover" },
|
||||
{ IC_CANVAS_SECTION_ACTIVE, "canvas_section_active" }, { IC_CANVAS_SECTION_ACTIVE_HOVER, "canvas_section_active_hover" },
|
||||
{ IC_CANVAS_SECTION_ACTIVE_DARK, "canvas_section_active_dark" }, { IC_CANVAS_SECTION_ACTIVE_DARK_HOVER, "canvas_section_active_dark_hover" } }) {
|
||||
if (!IMTexture::load_from_svg_file(Slic3r::resources_dir() + "/images/" + name + ".svg", 72, 72, texture_id))
|
||||
return false;
|
||||
icon_list.insert(std::make_pair((int) icon, texture_id));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -476,14 +484,17 @@ void GLGizmosManager::set_hover_id(int id)
|
||||
m_gizmos[m_current]->set_hover_id(id);
|
||||
}
|
||||
|
||||
void GLGizmosManager::update_assemble_view_data()
|
||||
void GLGizmosManager::update_section_view()
|
||||
{
|
||||
if (m_assemble_view_data) {
|
||||
if (!wxGetApp().plater()->get_assmeble_canvas3D()->get_wxglcanvas()->IsShown())
|
||||
m_assemble_view_data->update(AssembleViewDataID(0));
|
||||
else
|
||||
m_assemble_view_data->update(AssembleViewDataID((int)AssembleViewDataID::ModelObjectsInfo | (int)AssembleViewDataID::ModelObjectsClipper));
|
||||
}
|
||||
if (m_current != FdmSupports && m_current != Seam && m_current != MmSegmentation && m_current != FuzzySkin && m_current != BrimEars)
|
||||
return;
|
||||
|
||||
CommonGizmosDataObjects::ObjectClipper* clipper = m_common_gizmos_data ? m_common_gizmos_data->object_clipper() : nullptr;
|
||||
if (clipper == nullptr)
|
||||
return;
|
||||
|
||||
// Brim ears always cut horizontally.
|
||||
clipper->set_position_by_ratio(m_parent.get_section_view_ratio(), m_current == BrimEars ? Vec3d::UnitZ() : m_parent.get_section_view_normal());
|
||||
}
|
||||
|
||||
void GLGizmosManager::update_data()
|
||||
@@ -496,6 +507,7 @@ void GLGizmosManager::update_data()
|
||||
m_common_gizmos_data->update(get_current()
|
||||
? get_current()->get_requirements()
|
||||
: CommonGizmosDataID(0));
|
||||
update_section_view();
|
||||
if (m_current != Undefined) m_gizmos[m_current]->data_changed(m_serializing);
|
||||
|
||||
// Orca: hack: Fix issue that flatten gizmo faces not updated after reload from disk
|
||||
@@ -609,11 +621,11 @@ bool GLGizmosManager::is_allow_select_all() {
|
||||
return false;
|
||||
}
|
||||
|
||||
ClippingPlane GLGizmosManager::get_clipping_plane() const
|
||||
std::optional<ClippingPlane> GLGizmosManager::get_clipping_plane() const
|
||||
{
|
||||
if (! m_common_gizmos_data
|
||||
|| ! m_common_gizmos_data->object_clipper()
|
||||
|| m_common_gizmos_data->object_clipper()->get_position() == 0.)
|
||||
if (! m_common_gizmos_data || ! m_common_gizmos_data->object_clipper())
|
||||
return std::nullopt;
|
||||
else if (m_common_gizmos_data->object_clipper()->get_position() == 0.)
|
||||
return ClippingPlane::ClipsNothing();
|
||||
else {
|
||||
const ClippingPlane& clp = *m_common_gizmos_data->object_clipper()->get_clipping_plane();
|
||||
@@ -621,18 +633,6 @@ ClippingPlane GLGizmosManager::get_clipping_plane() const
|
||||
}
|
||||
}
|
||||
|
||||
ClippingPlane GLGizmosManager::get_assemble_view_clipping_plane() const
|
||||
{
|
||||
if (!m_assemble_view_data
|
||||
|| !m_assemble_view_data->model_objects_clipper()
|
||||
|| m_assemble_view_data->model_objects_clipper()->get_position() == 0.)
|
||||
return ClippingPlane::ClipsNothing();
|
||||
else {
|
||||
const ClippingPlane& clp = *m_assemble_view_data->model_objects_clipper()->get_clipping_plane();
|
||||
return ClippingPlane(-clp.get_normal(), clp.get_data()[3]);
|
||||
}
|
||||
}
|
||||
|
||||
bool GLGizmosManager::wants_reslice_supports_on_undo() const
|
||||
{
|
||||
return false;
|
||||
@@ -665,12 +665,6 @@ void GLGizmosManager::render_painter_gizmo()
|
||||
gizmo->render_painter_gizmo();
|
||||
}
|
||||
|
||||
void GLGizmosManager::render_painter_assemble_view() const
|
||||
{
|
||||
if (m_assemble_view_data && m_assemble_view_data->model_objects_clipper())
|
||||
m_assemble_view_data->model_objects_clipper()->render_cut();
|
||||
}
|
||||
|
||||
// The icon bar, drawn with GL.
|
||||
void GLGizmosManager::render_overlay()
|
||||
{
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <cereal/specialize.hpp>
|
||||
#include <wx/timer.h>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
|
||||
//BBS: GUI refactor: to support top layout
|
||||
#define BBS_TOOLBAR_ON_TOP 1
|
||||
@@ -181,7 +182,6 @@ private:
|
||||
bool gizmos_toolbar_on_mouse(const wxMouseEvent &mouse_event);
|
||||
public:
|
||||
|
||||
std::unique_ptr<AssembleViewDataPool> m_assemble_view_data;
|
||||
enum MENU_ICON_NAME {
|
||||
IC_TOOLBAR_RESET = 0,
|
||||
IC_TOOLBAR_RESET_HOVER,
|
||||
@@ -198,6 +198,14 @@ public:
|
||||
IC_CANVAS_ZOOM_HOVER,
|
||||
IC_CANVAS_ZOOM_DARK,
|
||||
IC_CANVAS_ZOOM_DARK_HOVER,
|
||||
IC_CANVAS_SECTION,
|
||||
IC_CANVAS_SECTION_HOVER,
|
||||
IC_CANVAS_SECTION_DARK,
|
||||
IC_CANVAS_SECTION_DARK_HOVER,
|
||||
IC_CANVAS_SECTION_ACTIVE,
|
||||
IC_CANVAS_SECTION_ACTIVE_HOVER,
|
||||
IC_CANVAS_SECTION_ACTIVE_DARK,
|
||||
IC_CANVAS_SECTION_ACTIVE_DARK_HOVER,
|
||||
};
|
||||
|
||||
explicit GLGizmosManager(GLCanvas3D& parent);
|
||||
@@ -266,7 +274,8 @@ public:
|
||||
/// Should be called when selection changed
|
||||
/// </summary>
|
||||
void update_data();
|
||||
void update_assemble_view_data();
|
||||
// Passes the canvas' section view to the object clipper of the painting and brim ears gizmos.
|
||||
void update_section_view();
|
||||
|
||||
EType get_current_type() const { return m_current; }
|
||||
GLGizmoBase* get_current() const;
|
||||
@@ -297,8 +306,8 @@ public:
|
||||
|
||||
bool is_paint_gizmo();
|
||||
bool is_allow_select_all();
|
||||
ClippingPlane get_clipping_plane() const;
|
||||
ClippingPlane get_assemble_view_clipping_plane() const;
|
||||
// Empty when the open gizmo has no object clipper.
|
||||
std::optional<ClippingPlane> get_clipping_plane() const;
|
||||
bool wants_reslice_supports_on_undo() const;
|
||||
|
||||
bool is_in_editing_mode(bool error_notification = false) const;
|
||||
@@ -307,7 +316,6 @@ public:
|
||||
void on_change_color_mode(bool is_dark);
|
||||
void render_current_gizmo() const;
|
||||
void render_painter_gizmo();
|
||||
void render_painter_assemble_view() const;
|
||||
|
||||
void render_overlay();
|
||||
void render_overlay_input_window();
|
||||
|
||||
@@ -196,13 +196,13 @@ void KBShortcutsDialog::fill_pages()
|
||||
mouse(Section::Camera, _L("Middle mouse"), "middle_mouse_drag_action"),
|
||||
mouse(Section::Camera, _L("Right mouse"), "right_mouse_drag_action"),
|
||||
fixed(Section::Camera, { wheel }, L("Zoom View")),
|
||||
fixed(Section::Display, { alt, wheel }, L("Move section plane")),
|
||||
});
|
||||
|
||||
page(_L("Painting"), _L("Available while a painting gizmo is open: supports, seam, fuzzy skin or color painting."), ShortcutContext::Painting, {
|
||||
fixed(Section::Gizmos, { esc }, L("Deselect All")),
|
||||
fixed(Section::Gizmos, { shift, left_button }, L("Move: press to snap by 1mm")),
|
||||
fixed(Section::PaintingTools, { ctrl, wheel }, L("Support/Color Painting: adjust pen radius")),
|
||||
fixed(Section::PaintingTools, { alt, wheel }, L("Support/Color Painting: adjust section position")),
|
||||
});
|
||||
|
||||
page(_L("Objects list"), _L("Available while the object list has focus."), ShortcutContext::ObjectList, {
|
||||
@@ -216,6 +216,7 @@ void KBShortcutsDialog::fill_pages()
|
||||
page(_L("Preview"), _L("Available while the 3D view on the Preview tab has focus."), ShortcutContext::Preview, {
|
||||
fixed(Section::Sliders, { shift_ctrl, any_key }, L("Move slider 5x faster")),
|
||||
fixed(Section::Sliders, { shift_ctrl, wheel }, L("Scroll slider 5x faster")),
|
||||
fixed(Section::Display, { alt, wheel }, L("Move section plane")),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -7891,6 +7891,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
||||
view3D = new View3D(panel_3d, bed, &model, config, &background_process);
|
||||
//BBS: use partplater's gcode
|
||||
preview = new Preview(panel_3d, bed, &model, config, &background_process, partplate_list.get_current_slice_result(), [this]() { schedule_background_process(); });
|
||||
preview->get_canvas3d()->share_section_view(*view3D->get_canvas3d());
|
||||
|
||||
assemble_view = new AssembleView(panel_3d, bed, &model, config, &background_process);
|
||||
|
||||
|
||||