Allow rendering hidden solid edges using a distinct style.

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.
This commit is contained in:
EvilSpirit 2016-03-09 10:53:46 +06:00 committed by whitequark
parent d55300232f
commit d1a2eb6d18
12 changed files with 113 additions and 83 deletions

View File

@ -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);

View File

@ -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));

View File

@ -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);

View File

@ -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);

View File

@ -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);

Binary file not shown.

Before

(image error) Size: 26 KiB

After

(image error) Size: 544 B

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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);

View File

@ -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
};

View File

@ -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);

View File

@ -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) {