Unify displayEdges and displayOutlines.
This has the following benefits: * Less geometry to generate; we can do both in one pass; * Less geometry to draw; * Eliminate overdraw of outlines on top of emphasized edges; * In future, being able to seamlessly stitch stippled lines. The contour edges are now also drawn before emphasized edges; this makes intersections of contour and emphasized edges look better as the thinner emphasized edge doesn't clobber the depth buffer.pull/33/head
parent
e7c8c1c8f2
commit
7f411d1593
|
@ -143,7 +143,7 @@ public:
|
|||
bool DrawBeziers(const SBezierList &bl, hStroke hcs) override {
|
||||
ssassert(false, "Not implemented");
|
||||
}
|
||||
void DrawOutlines(const SOutlineList &ol, hStroke hcs) override {
|
||||
void DrawOutlines(const SOutlineList &ol, hStroke hcs, DrawOutlinesAs drawAs) override {
|
||||
ssassert(false, "Not implemented");
|
||||
}
|
||||
void DrawPoint(const Vector &o, double d, hFill hcf) override {
|
||||
|
@ -209,11 +209,7 @@ void SolveSpaceUI::ExportViewOrWireframeTo(const std::string &filename, bool exp
|
|||
if(SS.GW.showEdges) {
|
||||
Group *g = SK.GetGroup(SS.GW.activeGroup);
|
||||
g->GenerateDisplayItems();
|
||||
SEdgeList *selr = &(g->displayEdges);
|
||||
SEdge *se;
|
||||
for(se = selr->l.First(); se; se = selr->l.NextAfter(se)) {
|
||||
edges.AddEdge(se->a, se->b, Style::SOLID_EDGE);
|
||||
}
|
||||
g->displayOutlines.ListTaggedInto(&edges, Style::SOLID_EDGE);
|
||||
}
|
||||
|
||||
if(SS.GW.showConstraints) {
|
||||
|
@ -812,7 +808,7 @@ void SolveSpaceUI::ExportMeshTo(const std::string &filename) {
|
|||
ExportMeshAsObjTo(f, m);
|
||||
} else if(FilenameHasExtension(filename, ".js") ||
|
||||
FilenameHasExtension(filename, ".html")) {
|
||||
SEdgeList *e = &(SK.GetGroup(SS.GW.activeGroup)->displayEdges);
|
||||
SOutlineList *e = &(SK.GetGroup(SS.GW.activeGroup)->displayOutlines);
|
||||
ExportMeshAsThreeJsTo(f, filename, m, e);
|
||||
} else {
|
||||
Error("Can't identify output file type from file extension of "
|
||||
|
@ -899,11 +895,10 @@ void SolveSpaceUI::ExportMeshAsObjTo(FILE *f, SMesh *sm) {
|
|||
// Export the mesh as a JavaScript script, which is compatible with Three.js.
|
||||
//-----------------------------------------------------------------------------
|
||||
void SolveSpaceUI::ExportMeshAsThreeJsTo(FILE *f, const std::string &filename,
|
||||
SMesh *sm, SEdgeList *sel)
|
||||
SMesh *sm, SOutlineList *sol)
|
||||
{
|
||||
SPointList spl = {};
|
||||
STriangle *tr;
|
||||
SEdge *e;
|
||||
Vector bndl, bndh;
|
||||
const char htmlbegin[] = R"(
|
||||
<!DOCTYPE html>
|
||||
|
@ -1055,14 +1050,15 @@ void SolveSpaceUI::ExportMeshAsThreeJsTo(FILE *f, const std::string &filename,
|
|||
fputs(" ],\n"
|
||||
" edges: [\n", f);
|
||||
// Output edges. Assume user's model colors do not obscure white edges.
|
||||
for(e = sel->l.First(); e; e = sel->l.NextAfter(e)) {
|
||||
for(const SOutline &so : sol->l) {
|
||||
if(so.tag == 0) continue;
|
||||
fprintf(f, " [[%f, %f, %f], [%f, %f, %f]],\n",
|
||||
e->a.x / SS.exportScale,
|
||||
e->a.y / SS.exportScale,
|
||||
e->a.z / SS.exportScale,
|
||||
e->b.x / SS.exportScale,
|
||||
e->b.y / SS.exportScale,
|
||||
e->b.z / SS.exportScale);
|
||||
so.a.x / SS.exportScale,
|
||||
so.a.y / SS.exportScale,
|
||||
so.a.z / SS.exportScale,
|
||||
so.b.x / SS.exportScale,
|
||||
so.b.y / SS.exportScale,
|
||||
so.b.z / SS.exportScale);
|
||||
}
|
||||
|
||||
fputs(" ]\n};\n", f);
|
||||
|
|
|
@ -28,7 +28,6 @@ void Group::Clear() {
|
|||
thisShell.Clear();
|
||||
runningShell.Clear();
|
||||
displayMesh.Clear();
|
||||
displayEdges.Clear();
|
||||
displayOutlines.Clear();
|
||||
impMesh.Clear();
|
||||
impShell.Clear();
|
||||
|
|
|
@ -377,14 +377,8 @@ void Group::GenerateDisplayItems() {
|
|||
displayMesh.Clear();
|
||||
displayMesh.MakeFromCopyOf(&(pg->displayMesh));
|
||||
|
||||
displayEdges.Clear();
|
||||
displayOutlines.Clear();
|
||||
if(SS.GW.showEdges) {
|
||||
SEdge *se;
|
||||
SEdgeList *src = &(pg->displayEdges);
|
||||
for(se = src->l.First(); se; se = src->l.NextAfter(se)) {
|
||||
displayEdges.l.Add(se);
|
||||
}
|
||||
displayOutlines.MakeFromCopyOf(&pg->displayOutlines);
|
||||
}
|
||||
} else {
|
||||
|
@ -402,17 +396,14 @@ void Group::GenerateDisplayItems() {
|
|||
displayMesh.AddTriangle(&trn);
|
||||
}
|
||||
|
||||
displayEdges.Clear();
|
||||
displayOutlines.Clear();
|
||||
|
||||
if(SS.GW.showEdges) {
|
||||
if(runningMesh.l.n > 0) {
|
||||
// Triangle mesh only; no shell or emphasized edges.
|
||||
runningMesh.MakeCertainEdgesAndOutlinesInto(
|
||||
&displayEdges, &displayOutlines, EdgeKind::EMPHASIZED);
|
||||
runningMesh.MakeOutlinesInto(&displayOutlines, EdgeKind::EMPHASIZED);
|
||||
} else {
|
||||
displayMesh.MakeCertainEdgesAndOutlinesInto(
|
||||
&displayEdges, &displayOutlines, EdgeKind::SHARP);
|
||||
displayMesh.MakeOutlinesInto(&displayOutlines, EdgeKind::SHARP);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -536,13 +527,27 @@ void Group::Draw(Canvas *canvas) {
|
|||
DrawMesh(DrawMeshAs::DEFAULT, canvas);
|
||||
|
||||
if(SS.GW.showEdges) {
|
||||
if(SS.GW.showOutlines) {
|
||||
Canvas::Stroke strokeOutline = {};
|
||||
strokeOutline.zIndex = 1;
|
||||
strokeOutline.color = Style::Color(Style::OUTLINE);
|
||||
strokeOutline.width = Style::Width(Style::OUTLINE);
|
||||
Canvas::hStroke hcsOutline = canvas->GetStroke(strokeOutline);
|
||||
|
||||
canvas->DrawOutlines(displayOutlines, hcsOutline,
|
||||
Canvas::DrawOutlinesAs::CONTOUR_ONLY);
|
||||
}
|
||||
|
||||
Canvas::Stroke strokeEdge = {};
|
||||
strokeEdge.zIndex = 1;
|
||||
strokeEdge.color = Style::Color(Style::SOLID_EDGE);
|
||||
strokeEdge.width = Style::Width(Style::SOLID_EDGE);
|
||||
Canvas::hStroke hcsEdge = canvas->GetStroke(strokeEdge);
|
||||
|
||||
canvas->DrawEdges(displayEdges, hcsEdge);
|
||||
canvas->DrawOutlines(displayOutlines, hcsEdge,
|
||||
SS.GW.showOutlines
|
||||
? Canvas::DrawOutlinesAs::EMPHASIZED_WITHOUT_CONTOUR
|
||||
: Canvas::DrawOutlinesAs::EMPHASIZED_AND_CONTOUR);
|
||||
|
||||
if(SS.GW.showHdnLines) {
|
||||
Canvas::Stroke strokeHidden = strokeEdge;
|
||||
|
@ -552,19 +557,8 @@ void Group::Draw(Canvas *canvas) {
|
|||
strokeHidden.stippleScale = Style::StippleScaleMm({ Style::HIDDEN_EDGE });
|
||||
Canvas::hStroke hcsHidden = canvas->GetStroke(strokeHidden);
|
||||
|
||||
canvas->DrawEdges(displayEdges, hcsHidden);
|
||||
canvas->DrawOutlines(displayOutlines, hcsHidden);
|
||||
}
|
||||
|
||||
if(SS.GW.showOutlines) {
|
||||
Canvas::Stroke strokeOutline = strokeEdge;
|
||||
strokeOutline.color = Style::Color(Style::OUTLINE);
|
||||
strokeOutline.width = Style::Width(Style::OUTLINE);
|
||||
Canvas::hStroke hcsOutline = canvas->GetStroke(strokeOutline);
|
||||
|
||||
canvas->DrawOutlines(displayOutlines, hcsOutline);
|
||||
} else {
|
||||
canvas->DrawOutlines(displayOutlines, hcsEdge);
|
||||
canvas->DrawOutlines(displayOutlines, hcsHidden,
|
||||
Canvas::DrawOutlinesAs::EMPHASIZED_AND_CONTOUR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
45
src/mesh.cpp
45
src/mesh.cpp
|
@ -91,10 +91,9 @@ void SMesh::MakeEdgesInPlaneInto(SEdgeList *sel, Vector n, double d) {
|
|||
m.Clear();
|
||||
}
|
||||
|
||||
void SMesh::MakeCertainEdgesAndOutlinesInto(SEdgeList *sel, SOutlineList *sol, EdgeKind type) {
|
||||
void SMesh::MakeOutlinesInto(SOutlineList *sol, EdgeKind edgeKind) {
|
||||
SKdNode *root = SKdNode::From(this);
|
||||
root->MakeCertainEdgesInto(sel, type, /*coplanarIsInter=*/false, NULL, NULL);
|
||||
root->MakeOutlinesInto(sol);
|
||||
root->MakeOutlinesInto(sol, edgeKind);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -1010,7 +1009,7 @@ void SKdNode::MakeCertainEdgesInto(SEdgeList *sel, EdgeKind how, bool coplanarIs
|
|||
}
|
||||
}
|
||||
|
||||
void SKdNode::MakeOutlinesInto(SOutlineList *sol) const
|
||||
void SKdNode::MakeOutlinesInto(SOutlineList *sol, EdgeKind edgeKind) const
|
||||
{
|
||||
std::vector<STriangle *> tris;
|
||||
ClearTags();
|
||||
|
@ -1030,13 +1029,37 @@ void SKdNode::MakeOutlinesInto(SOutlineList *sol) const
|
|||
if(CheckAndAddTrianglePair(&edgeTris, tr, info.tr))
|
||||
continue;
|
||||
|
||||
int tag = 0;
|
||||
switch(edgeKind) {
|
||||
case EdgeKind::EMPHASIZED:
|
||||
if(tr->meta.face != info.tr->meta.face) {
|
||||
tag = 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case EdgeKind::SHARP: {
|
||||
Vector na0 = tr->normals[j].WithMagnitude(1.0);
|
||||
Vector nb0 = tr->normals[(j + 1) % 3].WithMagnitude(1.0);
|
||||
Vector na1 = info.tr->normals[info.ai].WithMagnitude(1.0);
|
||||
Vector nb1 = info.tr->normals[info.bi].WithMagnitude(1.0);
|
||||
if(!((na0.Equals(na1) && nb0.Equals(nb1)) ||
|
||||
(na0.Equals(nb1) && nb0.Equals(na1)))) {
|
||||
tag = 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
ssassert(false, "Unexpected edge kind");
|
||||
}
|
||||
|
||||
Vector nl = tr->Normal().WithMagnitude(1.0);
|
||||
Vector nr = info.tr->Normal().WithMagnitude(1.0);
|
||||
|
||||
// We don't add edges with the same left and right
|
||||
// normals because they can't produce outlines.
|
||||
if(nl.Equals(nr)) continue;
|
||||
sol->AddEdge(a, b, nl, nr);
|
||||
if(tag == 0 && nl.Equals(nr)) continue;
|
||||
sol->AddEdge(a, b, nl, nr, tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1052,15 +1075,23 @@ void SOutlineList::Clear() {
|
|||
l.Clear();
|
||||
}
|
||||
|
||||
void SOutlineList::AddEdge(Vector a, Vector b, Vector nl, Vector nr) {
|
||||
void SOutlineList::AddEdge(Vector a, Vector b, Vector nl, Vector nr, int tag) {
|
||||
SOutline so = {};
|
||||
so.a = a;
|
||||
so.b = b;
|
||||
so.nl = nl;
|
||||
so.nr = nr;
|
||||
so.tag = tag;
|
||||
l.Add(&so);
|
||||
}
|
||||
|
||||
void SOutlineList::ListTaggedInto(SEdgeList *el, int auxA, int auxB) {
|
||||
for(const SOutline &so : l) {
|
||||
if(so.tag == 0) continue;
|
||||
el->AddEdge(so.a, so.b, auxA, auxB);
|
||||
}
|
||||
}
|
||||
|
||||
void SOutlineList::MakeFromCopyOf(SOutlineList *sol) {
|
||||
for(SOutline *so = sol->l.First(); so; so = sol->l.NextAfter(so)) {
|
||||
l.Add(so);
|
||||
|
|
|
@ -269,7 +269,7 @@ public:
|
|||
void MakeFromAssemblyOf(SMesh *a, SMesh *b);
|
||||
|
||||
void MakeEdgesInPlaneInto(SEdgeList *sel, Vector n, double d);
|
||||
void MakeCertainEdgesAndOutlinesInto(SEdgeList *sel, SOutlineList *sol, EdgeKind type);
|
||||
void MakeOutlinesInto(SOutlineList *sol, EdgeKind type);
|
||||
|
||||
bool IsEmpty() const;
|
||||
void RemapFaces(Group *g, int remap);
|
||||
|
@ -300,7 +300,8 @@ public:
|
|||
List<SOutline> l;
|
||||
|
||||
void Clear();
|
||||
void AddEdge(Vector a, Vector b, Vector nl, Vector nr);
|
||||
void AddEdge(Vector a, Vector b, Vector nl, Vector nr, int tag = 0);
|
||||
void ListTaggedInto(SEdgeList *el, int auxA = 0, int auxB = 0);
|
||||
|
||||
void MakeFromCopyOf(SOutlineList *ol);
|
||||
};
|
||||
|
@ -336,7 +337,7 @@ public:
|
|||
void FindEdgeOn(Vector a, Vector b, int cnt, bool coplanarIsInter, EdgeOnInfo *info) const;
|
||||
void MakeCertainEdgesInto(SEdgeList *sel, EdgeKind how, bool coplanarIsInter,
|
||||
bool *inter, bool *leaky, int auxA = 0) const;
|
||||
void MakeOutlinesInto(SOutlineList *sel) const;
|
||||
void MakeOutlinesInto(SOutlineList *sel, EdgeKind tagKind) const;
|
||||
|
||||
void OcclusionTestLine(SEdge orig, SEdgeList *sel, int cnt, bool removeHidden) const;
|
||||
void SplitLinesAgainstTriangle(SEdgeList *sel, STriangle *tr, bool removeHidden) const;
|
||||
|
|
|
@ -297,7 +297,7 @@ void ObjectPicker::DrawEdges(const SEdgeList &el, hStroke hcs) {
|
|||
}
|
||||
}
|
||||
|
||||
void ObjectPicker::DrawOutlines(const SOutlineList &ol, hStroke hcs) {
|
||||
void ObjectPicker::DrawOutlines(const SOutlineList &ol, hStroke hcs, DrawOutlinesAs drawAs) {
|
||||
ssassert(false, "Not implemented");
|
||||
}
|
||||
|
||||
|
|
|
@ -70,6 +70,17 @@ public:
|
|||
FRONT, // Always drawn above all other geometry
|
||||
};
|
||||
|
||||
// The outlines are the collection of all edges that may be drawn.
|
||||
// Outlines can be classified as emphasized or not; emphasized outlines indicate an abrupt
|
||||
// change in the surface curvature. These are indicated by the SOutline tag.
|
||||
// Outlines can also be classified as contour or not; contour outlines indicate the boundary
|
||||
// of the filled mesh. Whether an outline is a part of contour or not depends on point of view.
|
||||
enum class DrawOutlinesAs {
|
||||
EMPHASIZED_AND_CONTOUR, // Both emphasized and contour outlines
|
||||
EMPHASIZED_WITHOUT_CONTOUR, // Emphasized outlines except those also belonging to contour
|
||||
CONTOUR_ONLY // Contour outlines only
|
||||
};
|
||||
|
||||
class Stroke {
|
||||
public:
|
||||
hStroke h;
|
||||
|
@ -113,7 +124,7 @@ public:
|
|||
virtual void DrawLine(const Vector &a, const Vector &b, hStroke hcs) = 0;
|
||||
virtual void DrawEdges(const SEdgeList &el, hStroke hcs) = 0;
|
||||
virtual bool DrawBeziers(const SBezierList &bl, hStroke hcs) = 0;
|
||||
virtual void DrawOutlines(const SOutlineList &ol, hStroke hcs) = 0;
|
||||
virtual void DrawOutlines(const SOutlineList &ol, hStroke hcs, DrawOutlinesAs drawAs) = 0;
|
||||
virtual void DrawVectorText(const std::string &text, double height,
|
||||
const Vector &o, const Vector &u, const Vector &v,
|
||||
hStroke hcs) = 0;
|
||||
|
@ -169,7 +180,7 @@ public:
|
|||
void DrawLine(const Vector &a, const Vector &b, hStroke hcs) override;
|
||||
void DrawEdges(const SEdgeList &el, hStroke hcs) override;
|
||||
bool DrawBeziers(const SBezierList &bl, hStroke hcs) override { return false; }
|
||||
void DrawOutlines(const SOutlineList &ol, hStroke hcs) override;
|
||||
void DrawOutlines(const SOutlineList &ol, hStroke hcs, DrawOutlinesAs drawAs) override;
|
||||
void DrawVectorText(const std::string &text, double height,
|
||||
const Vector &o, const Vector &u, const Vector &v,
|
||||
hStroke hcs) override;
|
||||
|
@ -216,7 +227,7 @@ public:
|
|||
void DrawLine(const Vector &a, const Vector &b, hStroke hcs) override;
|
||||
void DrawEdges(const SEdgeList &el, hStroke hcs) override;
|
||||
bool DrawBeziers(const SBezierList &bl, hStroke hcs) override { return false; }
|
||||
void DrawOutlines(const SOutlineList &ol, hStroke hcs) override;
|
||||
void DrawOutlines(const SOutlineList &ol, hStroke hcs, DrawOutlinesAs drawAs) override;
|
||||
void DrawVectorText(const std::string &text, double height,
|
||||
const Vector &o, const Vector &u, const Vector &v,
|
||||
hStroke hcs) override;
|
||||
|
|
|
@ -408,11 +408,32 @@ void OpenGl1Renderer::DrawEdges(const SEdgeList &el, hStroke hcs) {
|
|||
}
|
||||
}
|
||||
|
||||
void OpenGl1Renderer::DrawOutlines(const SOutlineList &ol, hStroke hcs) {
|
||||
void OpenGl1Renderer::DrawOutlines(const SOutlineList &ol, hStroke hcs, DrawOutlinesAs drawAs) {
|
||||
Vector projDir = camera.projRight.Cross(camera.projUp);
|
||||
for(const SOutline *o = ol.l.First(); o; o = ol.l.NextAfter(o)) {
|
||||
if(!o->IsVisible(projDir)) continue;
|
||||
DoStippledLine(o->a, o->b, hcs);
|
||||
switch(drawAs) {
|
||||
case DrawOutlinesAs::EMPHASIZED_AND_CONTOUR:
|
||||
for(const SOutline &o : ol.l) {
|
||||
if(o.IsVisible(projDir) || o.tag != 0) {
|
||||
DoStippledLine(o.a, o.b, hcs);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case DrawOutlinesAs::EMPHASIZED_WITHOUT_CONTOUR:
|
||||
for(const SOutline &o : ol.l) {
|
||||
if(!o.IsVisible(projDir) && o.tag != 0) {
|
||||
DoStippledLine(o.a, o.b, hcs);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case DrawOutlinesAs::CONTOUR_ONLY:
|
||||
for(const SOutline &o : ol.l) {
|
||||
if(o.IsVisible(projDir)) {
|
||||
DoStippledLine(o.a, o.b, hcs);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -192,7 +192,6 @@ public:
|
|||
|
||||
bool displayDirty;
|
||||
SMesh displayMesh;
|
||||
SEdgeList displayEdges;
|
||||
SOutlineList displayOutlines;
|
||||
|
||||
enum class CombineAs : uint32_t {
|
||||
|
|
|
@ -798,7 +798,7 @@ public:
|
|||
void ExportMeshAsStlTo(FILE *f, SMesh *sm);
|
||||
void ExportMeshAsObjTo(FILE *f, SMesh *sm);
|
||||
void ExportMeshAsThreeJsTo(FILE *f, const std::string &filename,
|
||||
SMesh *sm, SEdgeList *sel);
|
||||
SMesh *sm, SOutlineList *sol);
|
||||
void ExportViewOrWireframeTo(const std::string &filename, bool exportWireframe);
|
||||
void ExportSectionTo(const std::string &filename);
|
||||
void ExportWireframeCurves(SEdgeList *sel, SBezierList *sbl,
|
||||
|
|
|
@ -63,7 +63,6 @@ void SolveSpaceUI::PushFromCurrentOnto(UndoStack *uk) {
|
|||
dest.thisShell = {};
|
||||
dest.runningShell = {};
|
||||
dest.displayMesh = {};
|
||||
dest.displayEdges = {};
|
||||
dest.displayOutlines = {};
|
||||
|
||||
dest.remap = {};
|
||||
|
|
Loading…
Reference in New Issue