From d1a2eb6d187cd57ecb31f1c0f8ecb3428d116e41 Mon Sep 17 00:00:00 2001 From: EvilSpirit Date: Wed, 9 Mar 2016 10:53:46 +0600 Subject: [PATCH] Allow rendering hidden solid edges using a distinct style. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before this change, the two buttons "Show/hide shaded model" (S) and "Show/hide hidden lines" (H) resulted in drawing the following elements in the following styles: Button | Non-occluded | Non-occluded | Occluded | Occluded state | solid edges | entities | solid edges | entities --------+--------------+--------------+-------------+-------------- !S !H | | | solid-edge | entity style --------+ | +-------------+-------------- S !H | | | invisible --------+ solid-edge | entity style +-------------+-------------- !S H | | | | --------+ | | solid-edge | entity style S H | | | | --------+--------------+--------------+-------------+-------------- After this change, they are drawn as follows: Button | Non-occluded | Non-occluded | Occluded | Occluded state | solid edges | entities | solid edges | entities --------+--------------+--------------+-------------+-------------- !S !H | | | solid-edge | entity style --------+ | +-------------+-------------- S !H | | | invisible --------+ solid-edge | entity style +-------------+-------------- !S H | | | | --------+ | | hidden-edge | stippled¹ S H | | | | --------+--------------+--------------+-------------+-------------- ¹ entity style, but the stipple parameters taken from hidden-edge In SolveSpace's true WYSIWYG tradition, the 2d view export follows the rendered view exactly. Also, it is now possible to edit the stipple parameters of built-in styles, so that by changing the hidden-edge style to non-stippled it is possible to regain the old behavior. --- src/draw.cpp | 18 ++++++---- src/drawentity.cpp | 23 ++++++------ src/export.cpp | 25 +++++++------ src/glhelper.cpp | 14 +++++--- src/groupmesh.cpp | 18 +++++++--- src/icons/hidden-lines.png | Bin 26946 -> 544 bytes src/mesh.cpp | 15 ++++---- src/polygon.cpp | 2 +- src/polygon.h | 4 +-- src/sketch.h | 5 +-- src/solvespace.h | 2 +- src/style.cpp | 70 ++++++++++++++++++------------------- 12 files changed, 113 insertions(+), 83 deletions(-) diff --git a/src/draw.cpp b/src/draw.cpp index 9652d55..268e5a4 100644 --- a/src/draw.cpp +++ b/src/draw.cpp @@ -34,7 +34,7 @@ void GraphicsWindow::Selection::Draw(void) { Vector refp = Vector::From(0, 0, 0); if(entity.v) { Entity *e = SK.GetEntity(entity); - e->Draw(); + e->Draw(/*drawAsHidden=*/false); if(emphasized) refp = e->GetReferencePos(); } if(constraint.v) { @@ -686,9 +686,15 @@ nogrid:; // Draw the active group; this does stuff like the mesh and edges. (SK.GetGroup(activeGroup))->Draw(); - // Now draw the entities - if(showHdnLines) glDisable(GL_DEPTH_TEST); - Entity::DrawAll(); + // Now draw the entities. + if(SS.GW.showHdnLines) { + ssglDepthRangeOffset(2); + glDepthFunc(GL_GREATER); + Entity::DrawAll(/*drawAsHidden=*/true); + glDepthFunc(GL_LEQUAL); + } + ssglDepthRangeOffset(0); + Entity::DrawAll(/*drawAsHidden=*/false); // Draw filled paths in all groups, when those filled paths were requested // specially by assigning a style with a fill color, or when the filled @@ -737,9 +743,7 @@ nogrid:; glEnd(); // And the naked edges, if the user did Analyze -> Show Naked Edges. - ssglLineWidth(Style::Width(Style::DRAW_ERROR)); - ssglColorRGB(Style::Color(Style::DRAW_ERROR)); - ssglDrawEdges(&(SS.nakedEdges), true); + ssglDrawEdges(&(SS.nakedEdges), true, { Style::DRAW_ERROR }); // Then redraw whatever the mouse is hovering over, highlighted. glDisable(GL_DEPTH_TEST); diff --git a/src/drawentity.cpp b/src/drawentity.cpp index 6aab2b9..82dce99 100644 --- a/src/drawentity.cpp +++ b/src/drawentity.cpp @@ -40,10 +40,9 @@ void Entity::LineDrawOrGetDistance(Vector a, Vector b, bool maybeFat, int data) dogd.refp = (a.Plus(b)).ScaledBy(0.5); } -void Entity::DrawAll(void) { - // This handles points and line segments as a special case, because I - // seem to be able to get a huge speedup that way, by consolidating - // stuff to gl. +void Entity::DrawAll(bool drawAsHidden) { + // This handles points as a special case, because I seem to be able + // to get a huge speedup that way, by consolidating stuff to gl. int i; if(SS.GW.showPoints) { double s = 3.5/SS.GW.scale; @@ -98,19 +97,23 @@ void Entity::DrawAll(void) { for(i = 0; i < SK.entity.n; i++) { Entity *e = &(SK.entity.elem[i]); - if(e->IsPoint()) - { + if(e->IsPoint()) { continue; // already handled } - e->Draw(); + e->Draw(drawAsHidden); } } -void Entity::Draw(void) { +void Entity::Draw(bool drawAsHidden) { hStyle hs = Style::ForEntity(h); dogd.lineWidth = Style::Width(hs); - dogd.stippleType = Style::PatternType(hs); - dogd.stippleScale = Style::StippleScaleMm(hs); + if(drawAsHidden) { + dogd.stippleType = Style::PatternType({ Style::HIDDEN_EDGE }); + dogd.stippleScale = Style::StippleScaleMm({ Style::HIDDEN_EDGE }); + } else { + dogd.stippleType = Style::PatternType(hs); + dogd.stippleScale = Style::StippleScaleMm(hs); + } ssglLineWidth((float)dogd.lineWidth); ssglColorRGB(Style::Color(hs)); diff --git a/src/export.cpp b/src/export.cpp index ea7d599..1e39627 100644 --- a/src/export.cpp +++ b/src/export.cpp @@ -122,7 +122,7 @@ void SolveSpaceUI::ExportViewOrWireframeTo(const std::string &filename, bool wir GenerateAll(GENERATE_ALL); SMesh *sm = NULL; - if(SS.GW.showShaded) { + if(SS.GW.showShaded || SS.GW.showHdnLines) { Group *g = SK.GetGroup(SS.GW.activeGroup); g->GenerateDisplayItems(); sm = &(g->displayMesh); @@ -136,8 +136,7 @@ void SolveSpaceUI::ExportViewOrWireframeTo(const std::string &filename, bool wir if(!e->IsVisible()) continue; if(e->construction) continue; - if(SS.exportPwlCurves || (sm && !SS.GW.showHdnLines) || - fabs(SS.exportOffset) > LENGTH_EPS) + if(SS.exportPwlCurves || sm || fabs(SS.exportOffset) > LENGTH_EPS) { // We will be doing hidden line removal, which we can't do on // exact curves; so we need things broken down to pwls. Same @@ -322,7 +321,7 @@ void SolveSpaceUI::ExportLinesAndMesh(SEdgeList *sel, SBezierList *sbl, SMesh *s // And now we perform hidden line removal if requested SEdgeList hlrd = {}; - if(sm && !SS.GW.showHdnLines) { + if(sm) { SKdNode *root = SKdNode::From(&smp); // Generate the edges where a curved surface turns from front-facing @@ -344,19 +343,19 @@ void SolveSpaceUI::ExportLinesAndMesh(SEdgeList *sel, SBezierList *sbl, SMesh *s continue; } - SEdgeList out = {}; + SEdgeList edges = {}; // Split the original edge against the mesh - out.AddEdge(se->a, se->b, se->auxA); - root->OcclusionTestLine(*se, &out, cnt); + edges.AddEdge(se->a, se->b, se->auxA); + root->OcclusionTestLine(*se, &edges, cnt, /*removeHidden=*/!SS.GW.showHdnLines); // the occlusion test splits unnecessarily; so fix those - out.MergeCollinearSegments(se->a, se->b); + edges.MergeCollinearSegments(se->a, se->b); cnt++; // And add the results to our output SEdge *sen; - for(sen = out.l.First(); sen; sen = out.l.NextAfter(sen)) { + for(sen = edges.l.First(); sen; sen = edges.l.NextAfter(sen)) { hlrd.AddEdge(sen->a, sen->b, sen->auxA); } - out.Clear(); + edges.Clear(); } sel = &hlrd; @@ -516,6 +515,12 @@ void SolveSpaceUI::ExportLinesAndMesh(SEdgeList *sel, SBezierList *sbl, SMesh *s sblss.AddOpenPath(b); } + // We need the mesh for occlusion testing, but if we don't export it, + // erase it now. + if(!SS.GW.showShaded) { + sms.Clear(); + } + // Now write the lines and triangles to the output file out->OutputLinesAndMesh(&sblss, &sms); diff --git a/src/glhelper.cpp b/src/glhelper.cpp index d3406e1..8f706ee 100644 --- a/src/glhelper.cpp +++ b/src/glhelper.cpp @@ -562,15 +562,19 @@ void ssglDebugPolygon(SPolygon *p) } } -void ssglDrawEdges(SEdgeList *el, bool endpointsToo) +void ssglDrawEdges(SEdgeList *el, bool endpointsToo, hStyle hs) { + double lineWidth = Style::Width(hs); + int stippleType = Style::PatternType(hs); + double stippleScale = Style::StippleScaleMm(hs); + ssglLineWidth(float(lineWidth)); + ssglColorRGB(Style::Color(hs)); + SEdge *se; - glBegin(GL_LINES); for(se = el->l.First(); se; se = el->l.NextAfter(se)) { - ssglVertex3v(se->a); - ssglVertex3v(se->b); + ssglStippledLine(se->a, se->b, lineWidth, stippleType, stippleScale, + /*maybeFat=*/true); } - glEnd(); if(endpointsToo) { glPointSize(12); diff --git a/src/groupmesh.cpp b/src/groupmesh.cpp index 35d9684..8889b24 100644 --- a/src/groupmesh.cpp +++ b/src/groupmesh.cpp @@ -476,7 +476,7 @@ void Group::DrawDisplayItems(int t) { if(gs.faces > 0) ms1 = gs.face[0].v; if(gs.faces > 1) ms2 = gs.face[1].v; - if(SS.GW.showShaded) { + if(SS.GW.showShaded || SS.GW.showHdnLines) { if(SS.drawBackFaces && !displayMesh.isTransparent) { // For debugging, draw the backs of the triangles in red, so that we // notice when a shell is open @@ -485,16 +485,26 @@ void Group::DrawDisplayItems(int t) { glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 0); } + // Draw the shaded solid into the depth buffer for hidden line removal, + // and if we're actually going to display it, to the color buffer too. glEnable(GL_LIGHTING); + if(!SS.GW.showShaded) glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); ssglFillMesh(useSpecColor, specColor, &displayMesh, mh, ms1, ms2); + if(!SS.GW.showShaded) glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glDisable(GL_LIGHTING); } if(SS.GW.showEdges) { + glDepthMask(GL_FALSE); + if(SS.GW.showHdnLines) { + ssglDepthRangeOffset(0); + glDepthFunc(GL_GREATER); + ssglDrawEdges(&displayEdges, false, { Style::HIDDEN_EDGE }); + glDepthFunc(GL_LEQUAL); + } ssglDepthRangeOffset(2); - ssglColorRGB(Style::Color(Style::SOLID_EDGE)); - ssglLineWidth(Style::Width(Style::SOLID_EDGE)); - ssglDrawEdges(&displayEdges, false); + ssglDrawEdges(&displayEdges, false, { Style::SOLID_EDGE }); + glDepthMask(GL_TRUE); } if(SS.GW.showMesh) ssglDebugMesh(&displayMesh); diff --git a/src/icons/hidden-lines.png b/src/icons/hidden-lines.png index cf43afbde50f5aa87a343ad69bcca0905557ecd8..35ea04b21340a6976600fa67b3bab7ce86f1eaa0 100644 GIT binary patch delta 501 zcmV$0O(SIcv2V0Z<5GB`*{TT(8%ZQnFkw z-}6R+D2f=zF-N0OhJWO8xfscVAfS|zN~sL_qky=pkRS=Ib!Gw_$3c=L#tCyA2SSLa zJjqPnw5>ae#iI3yY(3T;m{yY)ww`qU>m8E+Z56KTepbNqJnOgf`TTc*)9D1QHJZ&P zeBZ}*yM@*o)oK+5zqu1+Y%+wJztacWGP-C!_yzRBrg-}kAM zV!z+d^`Uf6+Uy?cvc;a~5dcf2(n|*=X*4^lz|HK2!(pz;rF%?fXB9A+oee<{kfhP< rtO8nVUa!};F`Z6dE=2tMSH<@LcKtt#{ZgLD00000NkvXXu0mjf6kPGW literal 26946 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 Z?dtdKFi^Gr{C9u3a>?dm@%h_N{|_q!YU}_2 diff --git a/src/mesh.cpp b/src/mesh.cpp index 51fa0d3..b7f14d1 100644 --- a/src/mesh.cpp +++ b/src/mesh.cpp @@ -653,7 +653,7 @@ void SKdNode::SnapToMesh(SMesh *m) { // them for occlusion. Keep only the visible segments. sel is both our input // and our output. //----------------------------------------------------------------------------- -void SKdNode::SplitLinesAgainstTriangle(SEdgeList *sel, STriangle *tr) { +void SKdNode::SplitLinesAgainstTriangle(SEdgeList *sel, STriangle *tr, bool removeHidden) { SEdgeList seln = {}; Vector tn = tr->Normal().WithMagnitude(1); @@ -754,8 +754,11 @@ void SKdNode::SplitLinesAgainstTriangle(SEdgeList *sel, STriangle *tr) { if(n[i].Dot(pt) - d[i] > LENGTH_EPS) se->tag = 0; } } + if(!removeHidden && se->tag == 1) + se->auxA = Style::HIDDEN_EDGE; } - sel->l.RemoveTagged(); + if(removeHidden) + sel->l.RemoveTagged(); } } @@ -763,7 +766,7 @@ void SKdNode::SplitLinesAgainstTriangle(SEdgeList *sel, STriangle *tr) { // Given an edge orig, occlusion test it against our mesh. We output an edge // list in sel, containing the visible portions of that edge. //----------------------------------------------------------------------------- -void SKdNode::OcclusionTestLine(SEdge orig, SEdgeList *sel, int cnt) { +void SKdNode::OcclusionTestLine(SEdge orig, SEdgeList *sel, int cnt, bool removeHidden) { if(gt && lt) { double ac = (orig.a).Element(which), bc = (orig.b).Element(which); @@ -773,13 +776,13 @@ void SKdNode::OcclusionTestLine(SEdge orig, SEdgeList *sel, int cnt) { bc < c + KDTREE_EPS || which == 2) { - lt->OcclusionTestLine(orig, sel, cnt); + lt->OcclusionTestLine(orig, sel, cnt, removeHidden); } if(ac > c - KDTREE_EPS || bc > c - KDTREE_EPS || which == 2) { - gt->OcclusionTestLine(orig, sel, cnt); + gt->OcclusionTestLine(orig, sel, cnt, removeHidden); } } else { STriangleLl *ll; @@ -788,7 +791,7 @@ void SKdNode::OcclusionTestLine(SEdge orig, SEdgeList *sel, int cnt) { if(tr->tag == cnt) continue; - SplitLinesAgainstTriangle(sel, tr); + SplitLinesAgainstTriangle(sel, tr, removeHidden); tr->tag = cnt; } } diff --git a/src/polygon.cpp b/src/polygon.cpp index 2bedbbe..7e1cfb7 100644 --- a/src/polygon.cpp +++ b/src/polygon.cpp @@ -453,7 +453,7 @@ void SEdgeList::MergeCollinearSegments(Vector a, Vector b) { SEdge *prev = &(l.elem[i-1]), *now = &(l.elem[i]); - if((prev->b).Equals(now->a)) { + if((prev->b).Equals(now->a) && prev->auxA == now->auxA) { // The previous segment joins up to us; so merge it into us. prev->tag = 1; now->a = prev->a; diff --git a/src/polygon.h b/src/polygon.h index 930994e..4249e7a 100644 --- a/src/polygon.h +++ b/src/polygon.h @@ -306,8 +306,8 @@ public: void MakeCertainEdgesInto(SEdgeList *sel, int how, bool coplanarIsInter, bool *inter, bool *leaky); - void OcclusionTestLine(SEdge orig, SEdgeList *sel, int cnt); - void SplitLinesAgainstTriangle(SEdgeList *sel, STriangle *tr); + void OcclusionTestLine(SEdge orig, SEdgeList *sel, int cnt, bool removeHidden); + void SplitLinesAgainstTriangle(SEdgeList *sel, STriangle *tr, bool removeHidden); void SnapToMesh(SMesh *m); void SnapToVertex(Vector v, SMesh *extras); diff --git a/src/sketch.h b/src/sketch.h index e700c48..e1c31aa 100644 --- a/src/sketch.h +++ b/src/sketch.h @@ -505,8 +505,8 @@ public: void GenerateBezierCurves(SBezierList *sbl); void GenerateEdges(SEdgeList *el, bool includingConstruction=false); - static void DrawAll(void); - void Draw(void); + static void DrawAll(bool drawAsHidden); + void Draw(bool drawAsHidden); double GetDistance(Point2d mp); Vector GetReferencePos(void); @@ -767,6 +767,7 @@ public: ANALYZE = 11, DRAW_ERROR = 12, DIM_SOLID = 13, + HIDDEN_EDGE = 14, FIRST_CUSTOM = 0x100 }; diff --git a/src/solvespace.h b/src/solvespace.h index a3fa2a9..729c708 100644 --- a/src/solvespace.h +++ b/src/solvespace.h @@ -345,7 +345,7 @@ void ssglFillPolygon(SPolygon *p); void ssglFillMesh(bool useSpecColor, RgbaColor color, SMesh *m, uint32_t h, uint32_t s1, uint32_t s2); void ssglDebugPolygon(SPolygon *p); -void ssglDrawEdges(SEdgeList *l, bool endpointsToo); +void ssglDrawEdges(SEdgeList *l, bool endpointsToo, hStyle hs); void ssglDebugMesh(SMesh *m); void ssglMarkPolygonNormal(SPolygon *p); typedef void ssglLineFn(void *data, Vector a, Vector b); diff --git a/src/style.cpp b/src/style.cpp index f7d8622..24b1998 100644 --- a/src/style.cpp +++ b/src/style.cpp @@ -24,6 +24,7 @@ const Style::Default Style::Defaults[] = { { { ANALYZE }, "Analyze", RGBf(0.0, 1.0, 1.0), 1.0, 0 }, { { DRAW_ERROR }, "DrawError", RGBf(1.0, 0.0, 0.0), 8.0, 0 }, { { DIM_SOLID }, "DimSolid", RGBf(0.1, 0.1, 0.1), 1.0, 0 }, + { { HIDDEN_EDGE }, "HiddenEdge", RGBf(0.8, 0.8, 0.8), 2.0, 1 }, { { 0 }, NULL, RGBf(0.0, 0.0, 0.0), 0.0, 0 } }; @@ -94,7 +95,8 @@ void Style::FillDefaultStyle(Style *s, const Default *d) { s->exportable = true; s->filled = false; s->fillColor = RGBf(0.3, 0.3, 0.3); - s->stippleType = Style::STIPPLE_CONTINUOUS; + s->stippleType = (d->h.v == Style::HIDDEN_EDGE) ? Style::STIPPLE_DASH + : Style::STIPPLE_CONTINUOUS; s->stippleScale = 15.0; s->zIndex = d->zIndex; } @@ -828,43 +830,41 @@ void TextWindow::ShowStyleInfo(void) { SS.UnitName()); } - if(s->h.v >= Style::FIRST_CUSTOM) { - Printf(false,"%Ba %Ftstipple type:%E"); + Printf(false,"%Ba %Ftstipple type:%E"); - const size_t patternCount = Style::LAST_STIPPLE + 1; - const char *patternsSource[patternCount] = { - "___________", - "- - - - - -", - "__ __ __ __", - "-.-.-.-.-.-", - "..-..-..-..", - "...........", - "~~~~~~~~~~~", - "__~__~__~__" - }; - std::string patterns[patternCount]; + const size_t patternCount = Style::LAST_STIPPLE + 1; + const char *patternsSource[patternCount] = { + "___________", + "- - - - - -", + "__ __ __ __", + "-.-.-.-.-.-", + "..-..-..-..", + "...........", + "~~~~~~~~~~~", + "__~__~__~__" + }; + std::string patterns[patternCount]; - for(int i = 0; i <= Style::LAST_STIPPLE; i++) { - const char *str = patternsSource[i]; - do { - switch(*str) { - case ' ': patterns[i] += " "; break; - case '.': patterns[i] += "\xEE\x80\x84"; break; - case '_': patterns[i] += "\xEE\x80\x85"; break; - case '-': patterns[i] += "\xEE\x80\x86"; break; - case '~': patterns[i] += "\xEE\x80\x87"; break; - default: oops(); - } - } while(*(++str)); - } + for(int i = 0; i <= Style::LAST_STIPPLE; i++) { + const char *str = patternsSource[i]; + do { + switch(*str) { + case ' ': patterns[i] += " "; break; + case '.': patterns[i] += "\xEE\x80\x84"; break; + case '_': patterns[i] += "\xEE\x80\x85"; break; + case '-': patterns[i] += "\xEE\x80\x86"; break; + case '~': patterns[i] += "\xEE\x80\x87"; break; + default: oops(); + } + } while(*(++str)); + } - for(int i = 0; i <= Style::LAST_STIPPLE; i++) { - const char *radio = s->stippleType == i ? RADIO_TRUE : RADIO_FALSE; - Printf(false, "%Bp %D%f%Lp%s %s%E", - (i % 2 == 0) ? 'd' : 'a', - s->h.v, &ScreenChangeStylePatternType, - i + 1, radio, patterns[i].c_str()); - } + for(int i = 0; i <= Style::LAST_STIPPLE; i++) { + const char *radio = s->stippleType == i ? RADIO_TRUE : RADIO_FALSE; + Printf(false, "%Bp %D%f%Lp%s %s%E", + (i % 2 == 0) ? 'd' : 'a', + s->h.v, &ScreenChangeStylePatternType, + i + 1, radio, patterns[i].c_str()); } if(s->h.v >= Style::FIRST_CUSTOM) {