mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-05 14:51:06 +00:00
Design tab: studio lighting and body edges in the 3D view
Reported: once extruded, a part is hard to read; the lighting says little about its shape. Both lights of the object shaders sit near the camera, so the sides of a part come out in almost the same tone, and nothing marks where one face ends and the next begins. - The phong shader gains a studio lighting model, chosen by a new lighting_model uniform: a sky/ground hemisphere in world space (up faces cool and bright, down faces warm and dark), a key light from the upper left and a weak fill from the right, a plastic-like highlight, and a darker base with a faint sheen toward the silhouette so curved faces read as round. GLCanvas3D::set_studio_lighting() makes a canvas draw its objects with it whatever the realistic-view preferences; only the Design canvas turns it on. Every canvas sets the uniform on each use, 0 for the slicer's, so they render as before. - Every B-rep edge of a body is drawn as a thin dark line over it, depth tested and pulled a few pixels toward the eye so it wins against the faces meeting at it and hides behind the faces in front. The seam of a closed surface and degenerate edges are left out (GeometryEngine::display_edges). Edges are sampled once per shape and kept across recomputes that leave a body unchanged; bodies faded by body focus get fainter edges, and a dress-up previewing its result alone hides them with the bodies. Tests: display_edges gives a box its 12 edges at their lengths, a cylinder its two round rims without the seam, a cone its base rim without the seam or the apex. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QK4VgguuCAk2hZLWgcjJb9
This commit is contained in:
@@ -362,6 +362,13 @@ the plane, `F` flips which half is kept. **Place on Face** (`F`, when section is
|
||||
picked face flat on the bed. Origin planes (`P`) and world axes (`A`) can be toggled on while
|
||||
you orient yourself.
|
||||
|
||||
Bodies are lit as in a studio, whatever the realistic-view preferences of the slicer: faces
|
||||
facing up read cooler and brighter than faces facing down, a key light from the upper left
|
||||
separates the sides of a part, and curved faces darken toward their outline. Every edge of a
|
||||
body is drawn as a thin dark line, except the seam OCCT puts down the side of a cylinder or
|
||||
cone; the edges of bodies faded by body focus are fainter, and a dress-up previewing its
|
||||
result alone hides them with the bodies.
|
||||
|
||||
---
|
||||
|
||||
## Known limitations
|
||||
|
||||
@@ -64,6 +64,10 @@ uniform PrintVolumeDetection print_volume;
|
||||
uniform float z_far;
|
||||
uniform float z_near;
|
||||
uniform bool enable_ssao;
|
||||
// 1 = the Design tab's studio lighting (studio_shade below); 0 = the two-light model above, which
|
||||
// every other canvas keeps. world_up_eye is world +Z in eye space, for the hemisphere ambient.
|
||||
uniform int lighting_model;
|
||||
uniform vec3 world_up_eye;
|
||||
|
||||
// Depth-based shadow map (object-on-object and self shadows). shadow_intensity == 0 disables it.
|
||||
uniform sampler2D shadow_map;
|
||||
@@ -206,6 +210,27 @@ float shadow_shade()
|
||||
return 1.0 - shadow_intensity * (sum / 25.0);
|
||||
}
|
||||
|
||||
// Studio lighting for the Design tab. The default model lights every face from near the camera,
|
||||
// so the sides of a part come out in nearly the same tone and its form is hard to read. This one
|
||||
// separates faces by their orientation in the WORLD (a sky/ground hemisphere: up-facing faces
|
||||
// cool and bright, down-facing ones warm and dark), keeps a strong key light from the upper left
|
||||
// and a weak fill from the right, gives a plastic-like highlight, and darkens the base colour
|
||||
// toward the silhouette while adding a faint sheen there, so curved faces read as round.
|
||||
vec3 studio_shade(vec3 base, vec3 n, vec3 v)
|
||||
{
|
||||
vec3 key = normalize(vec3(-0.45, 0.60, 0.66));
|
||||
vec3 fill = normalize(vec3(0.70, -0.15, 0.70));
|
||||
float hemi = 0.5 + 0.5 * dot(n, normalize(world_up_eye));
|
||||
vec3 ambient = mix(vec3(0.16, 0.15, 0.14), vec3(0.40, 0.42, 0.46), hemi);
|
||||
float kd = max(dot(n, key), 0.0);
|
||||
float fd = max(dot(n, fill), 0.0);
|
||||
vec3 diffuse = ambient + vec3(0.60) * kd + vec3(0.20) * fd;
|
||||
float spec = 0.28 * pow(max(dot(n, normalize(key + v)), 0.0), 48.0)
|
||||
+ 0.06 * pow(max(dot(n, normalize(fill + v)), 0.0), 24.0);
|
||||
float rim = pow(1.0 - clamp(dot(n, v), 0.0, 1.0), 3.0);
|
||||
return base * diffuse * (1.0 - 0.30 * rim) + vec3(spec + 0.08 * rim);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
if (any(lessThan(clipping_planes_dots, ZERO)))
|
||||
@@ -262,9 +287,11 @@ void main()
|
||||
// SSAO is applied in post-process pass. Keep base lighting unchanged here.
|
||||
|
||||
float shade = shadow_shade();
|
||||
vec3 lit = (lighting_model == 1) ? studio_shade(color.rgb, normal, view_dir)
|
||||
: (vec3(specular) + window_reflection + color.rgb * diffuse) * PHONG_BRIGHTNESS;
|
||||
|
||||
if (is_outline) {
|
||||
vec3 shaded_rgb = (vec3(specular) + window_reflection + color.rgb * diffuse) * PHONG_BRIGHTNESS * shade;
|
||||
vec3 shaded_rgb = lit * shade;
|
||||
vec4 shaded_color = vec4(clamp(shaded_rgb, vec3(0.0), vec3(1.0)), color.a);
|
||||
vec2 fragCoord = gl_FragCoord.xy;
|
||||
float s = DetectSilho(fragCoord);
|
||||
@@ -282,5 +309,5 @@ void main()
|
||||
gl_FragColor = vec4(clamp((0.45 * texture2D(environment_tex, normalize(eye_normal).xy * 0.5 + 0.5).xyz + window_reflection + 0.8 * color.rgb * diffuse) * PHONG_BRIGHTNESS * shade, vec3(0.0), vec3(1.0)), color.a);
|
||||
#endif
|
||||
else
|
||||
gl_FragColor = vec4(clamp((vec3(specular) + window_reflection + color.rgb * diffuse) * PHONG_BRIGHTNESS * shade, vec3(0.0), vec3(1.0)), color.a);
|
||||
gl_FragColor = vec4(clamp(lit * shade, vec3(0.0), vec3(1.0)), color.a);
|
||||
}
|
||||
@@ -74,6 +74,10 @@ uniform PrintVolumeDetection print_volume;
|
||||
uniform float z_far;
|
||||
uniform float z_near;
|
||||
uniform bool enable_ssao;
|
||||
// 1 = the Design tab's studio lighting (studio_shade below); 0 = the two-light model above, which
|
||||
// every other canvas keeps. world_up_eye is world +Z in eye space, for the hemisphere ambient.
|
||||
uniform int lighting_model;
|
||||
uniform vec3 world_up_eye;
|
||||
|
||||
// Depth-based shadow map (object-on-object and self shadows). shadow_intensity == 0 disables it.
|
||||
uniform sampler2D shadow_map;
|
||||
@@ -263,6 +267,27 @@ float shadow_shade()
|
||||
return 1.0 - shadow_intensity * (sum / 25.0);
|
||||
}
|
||||
|
||||
// Studio lighting for the Design tab. The default model lights every face from near the camera,
|
||||
// so the sides of a part come out in nearly the same tone and its form is hard to read. This one
|
||||
// separates faces by their orientation in the WORLD (a sky/ground hemisphere: up-facing faces
|
||||
// cool and bright, down-facing ones warm and dark), keeps a strong key light from the upper left
|
||||
// and a weak fill from the right, gives a plastic-like highlight, and darkens the base colour
|
||||
// toward the silhouette while adding a faint sheen there, so curved faces read as round.
|
||||
vec3 studio_shade(vec3 base, vec3 n, vec3 v)
|
||||
{
|
||||
vec3 key = normalize(vec3(-0.45, 0.60, 0.66));
|
||||
vec3 fill = normalize(vec3(0.70, -0.15, 0.70));
|
||||
float hemi = 0.5 + 0.5 * dot(n, normalize(world_up_eye));
|
||||
vec3 ambient = mix(vec3(0.16, 0.15, 0.14), vec3(0.40, 0.42, 0.46), hemi);
|
||||
float kd = max(dot(n, key), 0.0);
|
||||
float fd = max(dot(n, fill), 0.0);
|
||||
vec3 diffuse = ambient + vec3(0.60) * kd + vec3(0.20) * fd;
|
||||
float spec = 0.28 * pow(max(dot(n, normalize(key + v)), 0.0), 48.0)
|
||||
+ 0.06 * pow(max(dot(n, normalize(fill + v)), 0.0), 24.0);
|
||||
float rim = pow(1.0 - clamp(dot(n, v), 0.0, 1.0), 3.0);
|
||||
return base * diffuse * (1.0 - 0.30 * rim) + vec3(spec + 0.08 * rim);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
if (any(lessThan(clipping_planes_dots, ZERO)))
|
||||
@@ -319,9 +344,11 @@ void main()
|
||||
// SSAO is applied in post-process pass. Keep base lighting unchanged here.
|
||||
|
||||
float shade = shadow_shade();
|
||||
vec3 lit = (lighting_model == 1) ? studio_shade(color.rgb, normal, view_dir)
|
||||
: (vec3(specular) + window_reflection + color.rgb * diffuse) * PHONG_BRIGHTNESS;
|
||||
|
||||
if (is_outline) {
|
||||
vec3 shaded_rgb = (vec3(specular) + window_reflection + color.rgb * diffuse) * PHONG_BRIGHTNESS * shade;
|
||||
vec3 shaded_rgb = lit * shade;
|
||||
vec4 shaded_color = vec4(clamp(shaded_rgb, vec3(0.0), vec3(1.0)), color.a);
|
||||
float s = DetectSilho(gl_FragCoord.xy);
|
||||
if (s < 0.01)
|
||||
@@ -333,5 +360,5 @@ void main()
|
||||
out_color = vec4(clamp((0.45 * texture(environment_tex, normalize(eye_normal).xy * 0.5 + 0.5).xyz + window_reflection + 0.8 * color.rgb * diffuse) * PHONG_BRIGHTNESS * shade, vec3(0.0), vec3(1.0)), color.a);
|
||||
#endif
|
||||
else
|
||||
out_color = vec4(clamp((vec3(specular) + window_reflection + color.rgb * diffuse) * PHONG_BRIGHTNESS * shade, vec3(0.0), vec3(1.0)), color.a);
|
||||
out_color = vec4(clamp(lit * shade, vec3(0.0), vec3(1.0)), color.a);
|
||||
}
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <TopTools_IndexedMapOfShape.hxx>
|
||||
#include <TopTools_ListOfShape.hxx>
|
||||
#include <TopTools_IndexedDataMapOfShapeListOfShape.hxx>
|
||||
#include <TopTools_ListIteratorOfListOfShape.hxx>
|
||||
#include <Poly_Triangulation.hxx>
|
||||
#include <gp_Ax2.hxx>
|
||||
#include <gp_Dir.hxx>
|
||||
@@ -611,6 +612,27 @@ std::vector<Vec3d> GeometryEngine::sample_edge_world(const TopoDS_Edge& edge, do
|
||||
return pts;
|
||||
}
|
||||
|
||||
std::vector<std::vector<Vec3d>> GeometryEngine::display_edges(const TopoDS_Shape& shape, double chord_tol)
|
||||
{
|
||||
TopTools_IndexedDataMapOfShapeListOfShape faces_of_edge;
|
||||
TopExp::MapShapesAndAncestors(shape, TopAbs_EDGE, TopAbs_FACE, faces_of_edge);
|
||||
std::vector<std::vector<Vec3d>> out;
|
||||
for (int i = 1; i <= faces_of_edge.Extent(); ++i) {
|
||||
const TopoDS_Edge& edge = TopoDS::Edge(faces_of_edge.FindKey(i));
|
||||
if (BRep_Tool::Degenerated(edge))
|
||||
continue;
|
||||
bool seam = false;
|
||||
for (TopTools_ListIteratorOfListOfShape it(faces_of_edge.FindFromIndex(i)); it.More() && !seam; it.Next())
|
||||
seam = BRep_Tool::IsClosed(edge, TopoDS::Face(it.Value()));
|
||||
if (seam)
|
||||
continue;
|
||||
std::vector<Vec3d> pts = sample_edge_world(edge, chord_tol);
|
||||
if (pts.size() >= 2)
|
||||
out.push_back(std::move(pts));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
Vec3d GeometryEngine::face_centroid_world(const TopoDS_Face& face)
|
||||
{
|
||||
GProp_GProps props;
|
||||
|
||||
@@ -149,6 +149,10 @@ public:
|
||||
static Vec3d face_normal_world(const TopoDS_Face& face);
|
||||
// Sample an edge into a world-space polyline (>=2 pts) for pick-distance + highlight.
|
||||
static std::vector<Vec3d> sample_edge_world(const TopoDS_Edge& edge, double chord_tol = 0.05);
|
||||
// The edges a viewer draws over a body, each as a polyline: every edge of the shape once,
|
||||
// without degenerate edges (a cone apex) and without the seam of a closed surface (the line
|
||||
// down a cylinder's side), which is where OCCT closes the parameter space, not a real edge.
|
||||
static std::vector<std::vector<Vec3d>> display_edges(const TopoDS_Shape& shape, double chord_tol);
|
||||
// 0-based edge index into TopExp::MapShapes(shape, TopAbs_EDGE, map).
|
||||
static int edge_count(const TopoDS_Shape& shape);
|
||||
static TopoDS_Edge edge_by_index(const TopoDS_Shape& shape, int index);
|
||||
|
||||
@@ -46,6 +46,7 @@ DesignCanvas::DesignCanvas(wxWindow* parent)
|
||||
// cannot be null. Borrow the plater's, as the editor canvases do.
|
||||
m_canvas->set_process(&wxGetApp().plater()->background_process());
|
||||
m_canvas->set_type(GLCanvas3D::ECanvasType::CanvasView3D);
|
||||
m_canvas->set_studio_lighting(true); // see GLCanvas3D::m_studio_lighting and phong.fs
|
||||
|
||||
// CAD navigation, this canvas only: left-drag sweeps a selection rubber band, so orbit
|
||||
// moves to middle-drag and pan to right-drag. Design is a different modality from
|
||||
@@ -1347,6 +1348,7 @@ void DesignCanvas::set_body_hidden(bool on)
|
||||
{
|
||||
if (m_body_hidden == on) return;
|
||||
m_body_hidden = on;
|
||||
m_sketch_tool.set_body_edges_hidden(on);
|
||||
reload(true); // hides/show base bodies + flips the ghost opaque/faint for preview-only mode
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,10 @@
|
||||
#include "slic3r/GUI/3DScene.hpp"
|
||||
#include "slic3r/GUI/GLShader.hpp"
|
||||
#include "libslic3r/CAD/GeometryEngine.hpp"
|
||||
|
||||
#include <BRepBndLib.hxx>
|
||||
#include <Bnd_Box.hxx>
|
||||
#include <Standard_Failure.hxx>
|
||||
#include "libslic3r/TriangleMesh.hpp"
|
||||
|
||||
#include <GL/glew.h>
|
||||
@@ -3424,9 +3428,94 @@ void DesignSketchTool::set_solid_pick(const std::vector<CadBody>* bodies, const
|
||||
m_solid_bodies = bodies; m_solid_mesh = mesh; m_solid_tri_face = tri_face; m_solid_tri_body = tri_body;
|
||||
m_solid_visible = visible; m_solid_xform = xform;
|
||||
}
|
||||
refresh_body_edges();
|
||||
clear_solid_selection();
|
||||
}
|
||||
|
||||
void DesignSketchTool::refresh_body_edges()
|
||||
{
|
||||
const size_t n = m_solid_bodies != nullptr ? m_solid_bodies->size() : 0;
|
||||
m_body_edges.resize(n);
|
||||
m_body_edges_key.resize(n, nullptr);
|
||||
for (size_t b = 0; b < n; ++b) {
|
||||
const TopoDS_Shape& shape = (*m_solid_bodies)[b].shape;
|
||||
const void* key = shape.IsNull() ? nullptr : shape.TShape().get();
|
||||
if (key == m_body_edges_key[b] && key != nullptr)
|
||||
continue;
|
||||
m_body_edges_key[b] = key;
|
||||
m_body_edges[b].clear();
|
||||
if (key == nullptr)
|
||||
continue;
|
||||
// A thousandth of the body's size: round edges stay round at any zoom that shows the
|
||||
// whole body, without sampling a large import into millions of segments.
|
||||
Bnd_Box box;
|
||||
BRepBndLib::Add(shape, box);
|
||||
const double diag = box.IsVoid() ? 1.0 : std::sqrt(box.SquareExtent());
|
||||
try {
|
||||
m_body_edges[b] = GeometryEngine::display_edges(shape, std::max(1e-3 * diag, 0.005));
|
||||
} catch (const Standard_Failure&) {
|
||||
m_body_edges[b].clear(); // an unsampleable edge costs its body the lines, nothing else
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DesignSketchTool::render_body_edges()
|
||||
{
|
||||
if (m_body_edges_hidden || m_solid_bodies == nullptr)
|
||||
return;
|
||||
using EPT = GLModel::Geometry::EPrimitiveType;
|
||||
using EVL = GLModel::Geometry::EVertexLayout;
|
||||
const Camera& cam = wxGetApp().plater()->get_camera();
|
||||
const Vec3d vd = cam.get_dir_forward();
|
||||
const double px = 1.0 / std::max(cam.get_zoom(), 1e-6);
|
||||
const double hw = 0.75 * px; // ~1.5 px wide
|
||||
// Pulled toward the eye by a few pixels, so the line wins the depth test against the two
|
||||
// faces meeting at the edge while a face in front of it still hides it.
|
||||
const Vec3d pull = -vd * (3.0 * px);
|
||||
// Two passes: the edges of a body faded by body focus are fainter, like the body itself.
|
||||
for (int pass = 0; pass < 2; ++pass) {
|
||||
GLModel::Geometry g; g.format = { EPT::Triangles, EVL::P3 };
|
||||
unsigned int base = 0;
|
||||
for (int b = 0; b < int(m_body_edges.size()); ++b) {
|
||||
if (m_solid_visible != nullptr && b < int(m_solid_visible->size()) && !(*m_solid_visible)[b])
|
||||
continue;
|
||||
const bool faded = m_pick_only_body >= 0 && m_pick_only_body < int(m_body_edges.size())
|
||||
&& b != m_pick_only_body;
|
||||
if (faded != (pass == 1))
|
||||
continue;
|
||||
for (const std::vector<Vec3d>& pl : m_body_edges[b])
|
||||
for (size_t s = 1; s < pl.size(); ++s) {
|
||||
const Vec3d a = body_xform_pt(b, pl[s - 1]) + pull, c = body_xform_pt(b, pl[s]) + pull;
|
||||
Vec3d dir = c - a; if (dir.norm() < 1e-9) continue; dir.normalize();
|
||||
Vec3d off = dir.cross(vd);
|
||||
if (off.norm() < 1e-9) continue; // edge seen end-on: a point, nothing to draw
|
||||
off = off.normalized() * hw;
|
||||
g.add_vertex((Vec3f)(a + off).cast<float>());
|
||||
g.add_vertex((Vec3f)(c + off).cast<float>());
|
||||
g.add_vertex((Vec3f)(c - off).cast<float>());
|
||||
g.add_vertex((Vec3f)(a - off).cast<float>());
|
||||
g.add_triangle(base, base + 1, base + 2);
|
||||
g.add_triangle(base, base + 2, base + 3); base += 4;
|
||||
}
|
||||
}
|
||||
if (base == 0)
|
||||
continue;
|
||||
glsafe(::glEnable(GL_DEPTH_TEST));
|
||||
glsafe(::glDepthFunc(GL_LEQUAL));
|
||||
glsafe(::glDepthMask(GL_FALSE));
|
||||
glsafe(::glEnable(GL_BLEND));
|
||||
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
|
||||
m_body_edges_model.reset();
|
||||
m_body_edges_model.init_from(std::move(g));
|
||||
m_body_edges_model.set_color(ColorRGBA(0.08f, 0.09f, 0.11f, pass == 0 ? 0.85f : 0.25f));
|
||||
m_body_edges_model.render();
|
||||
glsafe(::glDepthMask(GL_TRUE));
|
||||
glsafe(::glDepthFunc(GL_LESS));
|
||||
glsafe(::glDisable(GL_BLEND));
|
||||
glsafe(::glDisable(GL_DEPTH_TEST));
|
||||
}
|
||||
}
|
||||
|
||||
// Map a point sampled from the (untransformed) OCCT body shape through the body's display
|
||||
// transform, so edge picking/highlight track a moved body. The pick MESH is already
|
||||
// transformed by the host; only OCCT-sampled edges need this.
|
||||
@@ -8929,6 +9018,8 @@ void DesignSketchTool::render(GLCanvas3D& canvas)
|
||||
shader->set_uniform("view_model_matrix", camera.get_view_matrix());
|
||||
shader->set_uniform("projection_matrix", camera.get_projection_matrix());
|
||||
|
||||
render_body_edges();
|
||||
|
||||
// Persistent committed sketches (e.g. an un-consumed sketch left visible after its
|
||||
// extrude is removed): faces translucent, outlines orange. Each uses its own plane.
|
||||
if (!m_display_sketches.empty()) {
|
||||
|
||||
@@ -198,6 +198,9 @@ public:
|
||||
// another solid is reachable without hiding anything. -1 = no restriction.
|
||||
// Survives set_solid_pick() — it is owned by the panel, not by the mesh feed.
|
||||
void set_pick_only_body(int b) { m_pick_only_body = b; }
|
||||
// Off while the bodies themselves are hidden (a dress-up previewing its result alone), so
|
||||
// their edges do not float over the preview.
|
||||
void set_body_edges_hidden(bool h) { m_body_edges_hidden = h; }
|
||||
void clear_solid_selection();
|
||||
bool has_solid_selection() const { return m_solid_sel != SolidSel::None; }
|
||||
// Every picked edge when the selection is an edge set (Shift/Ctrl+click adds and removes
|
||||
@@ -1204,6 +1207,14 @@ private:
|
||||
int m_pick_only_body{-1}; // >=0: only this body catches clicks (body-focus x-ray for CoordSys picking)
|
||||
const std::vector<Transform3d>* m_solid_xform{nullptr}; // per-body display transform (for edge sampling)
|
||||
Vec3d body_xform_pt(int body, const Vec3d& p) const; // map an OCCT-shape point through the body xform
|
||||
// The bodies' B-rep edges, drawn as dark lines over the solids so faces and features read
|
||||
// apart. One polyline set per body in its own shape coordinates, resampled only for a body
|
||||
// whose shape changed (keyed by the TShape), since set_solid_pick runs on every recompute.
|
||||
std::vector<std::vector<std::vector<Vec3d>>> m_body_edges;
|
||||
std::vector<const void*> m_body_edges_key;
|
||||
bool m_body_edges_hidden{false};
|
||||
void refresh_body_edges();
|
||||
void render_body_edges();
|
||||
bool body_pickable(int b) const; // false when the body is explicitly hidden
|
||||
SolidSel m_solid_sel{SolidSel::None};
|
||||
int m_sel_body{-1}; // which body the face/edge selection is on
|
||||
@@ -1269,6 +1280,7 @@ private:
|
||||
GLModel m_mc_fill_model; // the face treatment's shaded facets
|
||||
GLModel m_solid_face_model;
|
||||
GLModel m_solid_edge_model;
|
||||
GLModel m_body_edges_model;
|
||||
GLModel m_solid_vertex_model;
|
||||
int m_display_pick_region{-1}; // selected closed-region index within that feature (-1 none)
|
||||
|
||||
|
||||
@@ -8489,10 +8489,11 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with
|
||||
|
||||
const bool realistic_mode = _is_realistic_view_enabled();
|
||||
const bool realistic_phong = wxGetApp().app_config != nullptr && wxGetApp().app_config->get_bool(SETTING_OPENGL_REALISTIC_PHONG);
|
||||
const std::string shader_name = (realistic_mode && realistic_phong) ? "phong" : "gouraud";
|
||||
const std::string shader_name = (m_studio_lighting || (realistic_mode && realistic_phong)) ? "phong" : "gouraud";
|
||||
GLShaderProgram* shader = wxGetApp().get_shader(shader_name);
|
||||
if (shader == nullptr && shader_name != "gouraud")
|
||||
shader = wxGetApp().get_shader("gouraud");
|
||||
const bool studio = m_studio_lighting && shader != nullptr && shader->get_name() == "phong";
|
||||
ECanvasType canvas_type = this->m_canvas_type;
|
||||
bool partly_inside_enable = canvas_type == ECanvasType::CanvasAssembleView ? false : true;
|
||||
// The edited printer's per-extruder printable heights feed the object shader's
|
||||
@@ -8505,6 +8506,13 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with
|
||||
|
||||
const bool phong_ssao = wxGetApp().app_config != nullptr && wxGetApp().app_config->get_bool(SETTING_OPENGL_PHONG_SSAO);
|
||||
shader->set_uniform("enable_ssao", phong_ssao);
|
||||
// Set on every use: the program is shared, so a canvas that leaves it unset would inherit
|
||||
// the last canvas's choice.
|
||||
shader->set_uniform("lighting_model", studio ? 1 : 0);
|
||||
if (studio) {
|
||||
const Transform3d& view = wxGetApp().plater()->get_camera().get_view_matrix();
|
||||
shader->set_uniform("world_up_eye", Vec3f((view.matrix().block<3, 3>(0, 0) * Vec3d::UnitZ()).cast<float>()));
|
||||
}
|
||||
|
||||
// Object-on-object and self shadows: sample the depth map built in _render_shadow_map_pass().
|
||||
// shadow_intensity == 0 disables the effect entirely (unchanged behavior when off / unsupported).
|
||||
|
||||
@@ -590,6 +590,9 @@ private:
|
||||
|
||||
//BBS: add canvas type for assemble view usage
|
||||
ECanvasType m_canvas_type;
|
||||
// Objects drawn with the phong shader's studio lighting whatever the realistic-view settings
|
||||
// (the Design tab's canvas). Off for every canvas of the slicer, which render as before.
|
||||
bool m_studio_lighting{false};
|
||||
std::array<ClippingPlane, 2> m_clipping_planes;
|
||||
ClippingPlane m_camera_clipping_plane;
|
||||
bool m_use_clipping_planes;
|
||||
@@ -815,6 +818,7 @@ public:
|
||||
|
||||
void set_context(wxGLContext* context) { m_context = context; }
|
||||
void set_type(ECanvasType type) { m_canvas_type = type; }
|
||||
void set_studio_lighting(bool on) { m_studio_lighting = on; }
|
||||
ECanvasType get_canvas_type() { return m_canvas_type; }
|
||||
|
||||
wxGLCanvas* get_wxglcanvas() { return m_canvas; }
|
||||
|
||||
@@ -32,6 +32,8 @@
|
||||
#include <BRepGProp.hxx>
|
||||
#include <GProp_GProps.hxx>
|
||||
#include <BRepPrimAPI_MakeBox.hxx>
|
||||
#include <BRepPrimAPI_MakeCone.hxx>
|
||||
#include <BRepPrimAPI_MakeCylinder.hxx>
|
||||
#include <BRepBuilderAPI_Transform.hxx>
|
||||
#include <gp_Trsf.hxx>
|
||||
#include <gp_Vec.hxx>
|
||||
@@ -8920,3 +8922,29 @@ TEST_CASE("CadDocument: a profile on a body face touches it, one in free space d
|
||||
CHECK(doc.body_touching_sketch(-1) == -1);
|
||||
CHECK(doc.body_touching_sketch(base + 1) == -1); // the extrude: not a sketch
|
||||
}
|
||||
|
||||
TEST_CASE("display edges: every real edge once, no seams, no degenerate apex", "[CadDocument][display]")
|
||||
{
|
||||
const auto box = GeometryEngine::display_edges(BRepPrimAPI_MakeBox(10., 20., 30.).Shape(), 0.01);
|
||||
CHECK(box.size() == 12);
|
||||
for (const auto& pl : box) {
|
||||
REQUIRE(pl.size() >= 2);
|
||||
const double len = (pl.back() - pl.front()).norm();
|
||||
CHECK((std::abs(len - 10.) < 1e-6 || std::abs(len - 20.) < 1e-6 || std::abs(len - 30.) < 1e-6));
|
||||
}
|
||||
|
||||
// A cylinder has three edges in OCCT: the two rims and the seam down its side. Only the rims
|
||||
// are drawn, each sampled finely enough to look round and closed.
|
||||
const auto cyl = GeometryEngine::display_edges(BRepPrimAPI_MakeCylinder(5., 8.).Shape(), 0.01);
|
||||
REQUIRE(cyl.size() == 2);
|
||||
for (const auto& pl : cyl) {
|
||||
CHECK(pl.size() > 16);
|
||||
CHECK((pl.front() - pl.back()).norm() < 1e-6);
|
||||
for (const Vec3d& p : pl)
|
||||
CHECK(std::abs(std::hypot(p.x(), p.y()) - 5.) < 0.02);
|
||||
}
|
||||
|
||||
// A cone keeps its base rim; the apex is a degenerate edge and the side has a seam.
|
||||
const auto cone = GeometryEngine::display_edges(BRepPrimAPI_MakeCone(5., 0., 8.).Shape(), 0.01);
|
||||
CHECK(cone.size() == 1);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user