From 75a3936b6499f71227b0500fe745a7743d7c8347 Mon Sep 17 00:00:00 2001 From: whitequark Date: Thu, 26 Mar 2015 13:30:12 +0300 Subject: [PATCH] Add support for transparent solids. Some extra code is necessary to determine that the back faces should not be drawn in red for transparent solids. It is expected that the user will first ensure that the shell is watertight and then set the opacity; back faces are still drawn if the opacity is exactly 1. The savefile format is changed backwards-compatibly by stashing the alpha value in uppermost byte of 4-byte hex color value in Surface and Triangle clauses. The existing files have 00 in the high byte, so RgbColor::FromPackedInt treats that as "opaque". --- src/draw.cpp | 8 ------- src/dsc.h | 53 +++++++++++++++++++-------------------------- src/file.cpp | 20 ++++++++--------- src/glhelper.cpp | 17 +++++++++------ src/groupmesh.cpp | 15 +++++++++++-- src/mesh.cpp | 1 + src/polygon.h | 1 + src/solvespace.h | 3 ++- src/srf/surface.cpp | 6 ++--- src/style.cpp | 2 +- src/textscreens.cpp | 30 ++++++++++++++++++++++++- src/textwin.cpp | 6 ++--- src/ui.h | 2 ++ 13 files changed, 95 insertions(+), 69 deletions(-) diff --git a/src/draw.cpp b/src/draw.cpp index 47d04208..2c3d5f9e 100644 --- a/src/draw.cpp +++ b/src/draw.cpp @@ -604,14 +604,6 @@ void GraphicsWindow::Paint(void) { GLfloat ld1[4] = { (GLfloat)ld.x, (GLfloat)ld.y, (GLfloat)ld.z, 0 }; glLightfv(GL_LIGHT1, GL_POSITION, ld1); - if(SS.drawBackFaces) { - // For debugging, draw the backs of the triangles in red, so that we - // notice when a shell is open - glLightModelf(GL_LIGHT_MODEL_TWO_SIDE, 1); - } else { - glLightModelf(GL_LIGHT_MODEL_TWO_SIDE, 0); - } - GLfloat ambient[4] = { (float)SS.ambientIntensity, (float)SS.ambientIntensity, (float)SS.ambientIntensity, 1 }; diff --git a/src/dsc.h b/src/dsc.h index 278fc337..9da5b344 100644 --- a/src/dsc.h +++ b/src/dsc.h @@ -399,66 +399,57 @@ public: #define RGBi(r, g, b) RgbColor::From((r), (g), (b)) #define RGBf(r, g, b) RgbColor::FromFloat((float)(r), (float)(g), (float)(b)) -#define NULL_COLOR RgbColor::Default() // Note: sizeof(class RgbColor) should be exactly 4 // class RgbColor { - uint8_t useDefault; public: - uint8_t red, green, blue; + uint8_t red, green, blue, alpha; float redF(void) const { return (float)red / 255.0f; } float greenF(void) const { return (float)green / 255.0f; } float blueF(void) const { return (float)blue / 255.0f; } - - bool UseDefault(void) const { return useDefault != 0; } + float alphaF(void) const { return (float)alpha / 255.0f; } bool Equals(RgbColor c) const { - switch(c.useDefault + useDefault) { - case 0: return - c.red == red && - c.green == green && - c.blue == blue; - case 1: return false; - case 2: return true; - } - return false; + return + c.red == red && + c.green == green && + c.blue == blue && + c.alpha == alpha; } uint32_t ToPackedInt(void) const { - return red | (uint32_t)(green << 8) | (uint32_t)(blue << 16); + return + red | + (uint32_t)(green << 8) | + (uint32_t)(blue << 16) | + (uint32_t)((255 - alpha) << 24); } - static RgbColor Default(void) { + static RgbColor From(int r, int g, int b, int a = 255) { RgbColor c; - c.useDefault = 1; - // Leave r, g, b uninitialized so that Valgrind will notice - // if they are used inadvertently - return c; - } - - static RgbColor From(int r, int g, int b) { - RgbColor c; - c.useDefault = 0; c.red = (uint8_t)r; c.green = (uint8_t)g; c.blue = (uint8_t)b; + c.alpha = (uint8_t)a; return c; } - static RgbColor FromFloat(float r, float g, float b) { + static RgbColor FromFloat(float r, float g, float b, float a = 1.0) { return From( (int)(255.1f * r), (int)(255.1f * g), - (int)(255.1f * b)); + (int)(255.1f * b), + (int)(255.1f * a)); } - static RgbColor FromPackedInt(uint32_t bgr) { + static RgbColor FromPackedInt(uint32_t bgra) { return From( - (int)((bgr) & 0xff), - (int)((bgr >> 8) & 0xff), - (int)((bgr >> 16) & 0xff)); + (int)((bgra) & 0xff), + (int)((bgra >> 8) & 0xff), + (int)((bgra >> 16) & 0xff), + (int)(255 - ((bgra >> 24) & 0xff))); } }; diff --git a/src/file.cpp b/src/file.cpp index b06aca55..ba614b45 100644 --- a/src/file.cpp +++ b/src/file.cpp @@ -315,7 +315,7 @@ bool SolveSpaceUI::SaveToFile(const char *filename) { SMesh *m = &(SK.group.elem[SK.group.n-1].runningMesh); for(i = 0; i < m->l.n; i++) { STriangle *tr = &(m->l.elem[i]); - fprintf(fh, "Triangle %08x %08x " + fprintf(fh, "Triangle %08x %08x " "%.20f %.20f %.20f %.20f %.20f %.20f %.20f %.20f %.20f\n", tr->meta.face, tr->meta.color.ToPackedInt(), CO(tr->a), CO(tr->b), CO(tr->c)); @@ -555,27 +555,25 @@ bool SolveSpaceUI::LoadEntitiesFromFile(const char *file, EntityList *le, } else if(StrStartsWith(line, "Triangle ")) { STriangle tr; ZERO(&tr); - unsigned int rgb = 0; + unsigned int rgba = 0; if(sscanf(line, "Triangle %x %x " "%lf %lf %lf %lf %lf %lf %lf %lf %lf", - &(tr.meta.face), &rgb, + &(tr.meta.face), &rgba, &(tr.a.x), &(tr.a.y), &(tr.a.z), &(tr.b.x), &(tr.b.y), &(tr.b.z), - &(tr.c.x), &(tr.c.y), &(tr.c.z)) != 11) - { + &(tr.c.x), &(tr.c.y), &(tr.c.z)) != 11) { oops(); } - tr.meta.color = RgbColor::FromPackedInt((uint32_t)rgb); + tr.meta.color = RgbColor::FromPackedInt((uint32_t)rgba); m->AddTriangle(&tr); } else if(StrStartsWith(line, "Surface ")) { - unsigned int rgb = 0; + unsigned int rgba = 0; if(sscanf(line, "Surface %x %x %x %d %d", - &(srf.h.v), &rgb, &(srf.face), - &(srf.degm), &(srf.degn)) != 5) - { + &(srf.h.v), &rgba, &(srf.face), + &(srf.degm), &(srf.degn)) != 5) { oops(); } - srf.color = RgbColor::FromPackedInt((uint32_t)rgb); + srf.color = RgbColor::FromPackedInt((uint32_t)rgba); } else if(StrStartsWith(line, "SCtrl ")) { int i, j; Vector c; diff --git a/src/glhelper.cpp b/src/glhelper.cpp index adf6e487..348d9221 100644 --- a/src/glhelper.cpp +++ b/src/glhelper.cpp @@ -269,28 +269,31 @@ static void StippleTriangle(STriangle *tr, bool s, RgbColor rgb) glBegin(GL_TRIANGLES); } -void ssglFillMesh(RgbColor specColor, SMesh *m, uint32_t h, uint32_t s1, uint32_t s2) +void ssglFillMesh(bool useSpecColor, RgbColor specColor, + SMesh *m, uint32_t h, uint32_t s1, uint32_t s2) { RgbColor rgbHovered = Style::Color(Style::HOVERED), rgbSelected = Style::Color(Style::SELECTED); glEnable(GL_NORMALIZE); - RgbColor prevColor = NULL_COLOR; + bool hasMaterial = false; + RgbColor prevColor; glBegin(GL_TRIANGLES); for(int i = 0; i < m->l.n; i++) { STriangle *tr = &(m->l.elem[i]); RgbColor color; - if(specColor.UseDefault()) { - color = tr->meta.color; - } else { + if(useSpecColor) { color = specColor; + } else { + color = tr->meta.color; } - if(!color.Equals(prevColor)) { - GLfloat mpf[] = { color.redF(), color.greenF(), color.blueF(), 1.0f }; + if(!hasMaterial || !color.Equals(prevColor)) { + GLfloat mpf[] = { color.redF(), color.greenF(), color.blueF(), color.alphaF() }; glEnd(); glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, mpf); prevColor = color; + hasMaterial = true; glBegin(GL_TRIANGLES); } diff --git a/src/groupmesh.cpp b/src/groupmesh.cpp index 4bd392b3..9d0fa577 100644 --- a/src/groupmesh.cpp +++ b/src/groupmesh.cpp @@ -435,11 +435,13 @@ Group *Group::RunningMeshGroup(void) { void Group::DrawDisplayItems(int t) { RgbColor specColor; + bool useSpecColor; if(t == DRAWING_3D || t == DRAWING_WORKPLANE) { // force the color to something dim specColor = Style::Color(Style::DIM_SOLID); + useSpecColor = true; } else { - specColor = RgbColor::Default(); // use the model color + useSpecColor = false; // use the model color } // The back faces are drawn in red; should never seem them, since we // draw closed shells, so that's a debugging aid. @@ -458,10 +460,19 @@ void Group::DrawDisplayItems(int t) { if(gs.faces > 1) ms2 = gs.face[1].v; if(SS.GW.showShaded) { + if(SS.drawBackFaces && !displayMesh.isTransparent) { + // For debugging, draw the backs of the triangles in red, so that we + // notice when a shell is open + glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 1); + } else { + glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 0); + } + glEnable(GL_LIGHTING); - ssglFillMesh(specColor, &displayMesh, mh, ms1, ms2); + ssglFillMesh(useSpecColor, specColor, &displayMesh, mh, ms1, ms2); glDisable(GL_LIGHTING); } + if(SS.GW.showEdges) { ssglDepthRangeOffset(2); ssglColorRGB(Style::Color(Style::SOLID_EDGE)); diff --git a/src/mesh.cpp b/src/mesh.cpp index 7d157d93..81f31a5c 100644 --- a/src/mesh.cpp +++ b/src/mesh.cpp @@ -32,6 +32,7 @@ void SMesh::AddTriangle(STriMeta meta, Vector a, Vector b, Vector c) { AddTriangle(&t); } void SMesh::AddTriangle(STriangle *st) { + if(st->meta.color.alpha != 255) isTransparent = true; l.Add(st); } diff --git a/src/polygon.h b/src/polygon.h index 5583406a..9210c023 100644 --- a/src/polygon.h +++ b/src/polygon.h @@ -226,6 +226,7 @@ public: bool flipNormal; bool keepCoplanar; bool atLeastOneDiscarded; + bool isTransparent; void Clear(void); void AddTriangle(STriangle *st); diff --git a/src/solvespace.h b/src/solvespace.h index b66ce48d..2ad6afb2 100644 --- a/src/solvespace.h +++ b/src/solvespace.h @@ -300,7 +300,8 @@ void ssglAxisAlignedLineLoop(double l, double r, double t, double b); extern "C" { typedef void SSGL_CALLBACK ssglCallbackFptr(void); } void ssglTesselatePolygon(GLUtesselator *gt, SPolygon *p); void ssglFillPolygon(SPolygon *p); -void ssglFillMesh(RgbColor color, SMesh *m, uint32_t h, uint32_t s1, uint32_t s2); +void ssglFillMesh(bool useSpecColor, RgbColor color, + SMesh *m, uint32_t h, uint32_t s1, uint32_t s2); void ssglDebugPolygon(SPolygon *p); void ssglDrawEdges(SEdgeList *l, bool endpointsToo); void ssglDebugMesh(SMesh *m); diff --git a/src/srf/surface.cpp b/src/srf/surface.cpp index 9f98696b..ad789323 100644 --- a/src/srf/surface.cpp +++ b/src/srf/surface.cpp @@ -489,8 +489,7 @@ typedef struct { hSSurface hs; } TrimLine; -void SShell::MakeFromExtrusionOf(SBezierLoopSet *sbls, Vector t0, Vector t1, - RgbColor color) +void SShell::MakeFromExtrusionOf(SBezierLoopSet *sbls, Vector t0, Vector t1, RgbColor color) { // Make the extrusion direction consistent with respect to the normal // of the sketch we're extruding. @@ -609,8 +608,7 @@ typedef struct { hSSurface d[4]; } Revolved; -void SShell::MakeFromRevolutionOf(SBezierLoopSet *sbls, Vector pt, Vector axis, - RgbColor color) +void SShell::MakeFromRevolutionOf(SBezierLoopSet *sbls, Vector pt, Vector axis, RgbColor color) { SBezierLoop *sbl; diff --git a/src/style.cpp b/src/style.cpp index 8810a2b3..bf780d09 100644 --- a/src/style.cpp +++ b/src/style.cpp @@ -22,7 +22,7 @@ const Style::Default Style::Defaults[] = { { { ANALYZE }, "Analyze", RGBf(0.0, 1.0, 1.0), 1.0, }, { { DRAW_ERROR }, "DrawError", RGBf(1.0, 0.0, 0.0), 8.0, }, { { DIM_SOLID }, "DimSolid", RGBf(0.1, 0.1, 0.1), 1.0, }, - { { 0 }, NULL, NULL_COLOR, 0.0 } + { { 0 }, NULL, RGBf(0.0, 0.0, 0.0), 0.0 } }; char *Style::CnfColor(const char *prefix) { diff --git a/src/textscreens.cpp b/src/textscreens.cpp index 0e0af1e4..a3d2b554 100644 --- a/src/textscreens.cpp +++ b/src/textscreens.cpp @@ -225,6 +225,15 @@ void TextWindow::ScreenColor(int link, uint32_t v) { SS.TW.ShowEditControlWithColorPicker(v, 3, g->color); SS.TW.edit.meaning = EDIT_GROUP_COLOR; } +void TextWindow::ScreenOpacity(int link, uint32_t v) { + Group *g = SK.GetGroup(SS.TW.shown.group); + + char str[1024]; + sprintf(str, "%.2f", g->color.alphaF()); + SS.TW.ShowEditControl(22, 11, str); + SS.TW.edit.meaning = EDIT_GROUP_OPACITY; + SS.TW.edit.group.v = g->h.v; +} void TextWindow::ScreenChangeExprA(int link, uint32_t v) { Group *g = SK.GetGroup(SS.TW.shown.group); @@ -365,10 +374,13 @@ void TextWindow::ShowGroupInfo(void) { g->type == Group::LATHE) { Printf(false, - "%Bd %Ftcolor %E%Bz %Bd (%@, %@, %@) %f%D%Lf%Fl[change]%E", + "%Bd %Ftcolor %E%Bz %Bd (%@, %@, %@) %f%D%Lf%Fl[change]%E", &g->color, g->color.redF(), g->color.greenF(), g->color.blueF(), ScreenColor, top[rows-1] + 2); + Printf(false, "%Bd %Ftopacity%E %@ %f%Lf%Fl[change]%E", + g->color.alphaF(), + &TextWindow::ScreenOpacity); } else if(g->type == Group::IMPORTED) { bool sup = g->suppress; Printf(false, " %Fd%f%LP%c suppress this group's solid model", @@ -697,6 +709,22 @@ void TextWindow::EditControlDone(const char *s) { } break; } + case EDIT_GROUP_OPACITY: { + Expr *e = Expr::From(s, true); + if(e) { + double alpha = e->Eval(); + if(alpha < 0 || alpha > 1) { + Error("Opacity must be between zero and one."); + } else { + Group *g = SK.GetGroup(edit.group); + g->color.alpha = (int)(255.1f * alpha); + SS.MarkGroupDirty(g->h); + SS.ScheduleGenerateAll(); + SS.GW.ClearSuper(); + } + } + break; + } case EDIT_TTF_TEXT: { SS.UndoRemember(); Request *r = SK.request.FindByIdNoOops(edit.request); diff --git a/src/textwin.cpp b/src/textwin.cpp index 58b5c2e1..28cfa79b 100644 --- a/src/textwin.cpp +++ b/src/textwin.cpp @@ -18,14 +18,14 @@ const TextWindow::Color TextWindow::fgColors[] = { { 'i', RGBi( 0, 255, 255) }, { 'g', RGBi(160, 160, 160) }, { 'b', RGBi(200, 200, 200) }, - { 0, NULL_COLOR } + { 0, RGBi( 0, 0, 0) } }; const TextWindow::Color TextWindow::bgColors[] = { { 'd', RGBi( 0, 0, 0) }, { 't', RGBi( 34, 15, 15) }, { 'a', RGBi( 25, 25, 25) }, { 'r', RGBi(255, 255, 255) }, - { 0, NULL_COLOR } + { 0, RGBi( 0, 0, 0) } }; bool TextWindow::SPACER = false; @@ -132,7 +132,7 @@ void TextWindow::Printf(bool halfLine, const char *fmt, ...) { char fg = 'd'; char bg = 'd'; - RgbColor bgRgb = NULL_COLOR; + RgbColor bgRgb = RGBi(0, 0, 0); int link = NOT_A_LINK; uint32_t data = 0; LinkFunction *f = NULL, *h = NULL; diff --git a/src/ui.h b/src/ui.h index 0dc299f8..1012b93f 100644 --- a/src/ui.h +++ b/src/ui.h @@ -139,6 +139,7 @@ public: EDIT_GROUP_NAME = 2, EDIT_GROUP_SCALE = 3, EDIT_GROUP_COLOR = 4, + EDIT_GROUP_OPACITY = 5, // For the configuraiton screen EDIT_LIGHT_DIRECTION = 100, EDIT_LIGHT_INTENSITY = 101, @@ -254,6 +255,7 @@ public: static void ScreenChangeGroupOption(int link, uint32_t v); static void ScreenColor(int link, uint32_t v); + static void ScreenOpacity(int link, uint32_t v); static void ScreenShowListOfStyles(int link, uint32_t v); static void ScreenShowStyleInfo(int link, uint32_t v);