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".
pull/3/head
whitequark 2015-03-26 13:30:12 +03:00
parent 1b69032d99
commit 75a3936b64
13 changed files with 95 additions and 69 deletions

View File

@ -604,14 +604,6 @@ void GraphicsWindow::Paint(void) {
GLfloat ld1[4] = { (GLfloat)ld.x, (GLfloat)ld.y, (GLfloat)ld.z, 0 }; GLfloat ld1[4] = { (GLfloat)ld.x, (GLfloat)ld.y, (GLfloat)ld.z, 0 };
glLightfv(GL_LIGHT1, GL_POSITION, ld1); 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, GLfloat ambient[4] = { (float)SS.ambientIntensity,
(float)SS.ambientIntensity, (float)SS.ambientIntensity,
(float)SS.ambientIntensity, 1 }; (float)SS.ambientIntensity, 1 };

View File

@ -399,66 +399,57 @@ public:
#define RGBi(r, g, b) RgbColor::From((r), (g), (b)) #define RGBi(r, g, b) RgbColor::From((r), (g), (b))
#define RGBf(r, g, b) RgbColor::FromFloat((float)(r), (float)(g), (float)(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 // Note: sizeof(class RgbColor) should be exactly 4
// //
class RgbColor { class RgbColor {
uint8_t useDefault;
public: public:
uint8_t red, green, blue; uint8_t red, green, blue, alpha;
float redF(void) const { return (float)red / 255.0f; } float redF(void) const { return (float)red / 255.0f; }
float greenF(void) const { return (float)green / 255.0f; } float greenF(void) const { return (float)green / 255.0f; }
float blueF(void) const { return (float)blue / 255.0f; } float blueF(void) const { return (float)blue / 255.0f; }
float alphaF(void) const { return (float)alpha / 255.0f; }
bool UseDefault(void) const { return useDefault != 0; }
bool Equals(RgbColor c) const { bool Equals(RgbColor c) const {
switch(c.useDefault + useDefault) { return
case 0: return
c.red == red && c.red == red &&
c.green == green && c.green == green &&
c.blue == blue; c.blue == blue &&
case 1: return false; c.alpha == alpha;
case 2: return true;
}
return false;
} }
uint32_t ToPackedInt(void) const { 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; 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.red = (uint8_t)r;
c.green = (uint8_t)g; c.green = (uint8_t)g;
c.blue = (uint8_t)b; c.blue = (uint8_t)b;
c.alpha = (uint8_t)a;
return c; 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( return From(
(int)(255.1f * r), (int)(255.1f * r),
(int)(255.1f * g), (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( return From(
(int)((bgr) & 0xff), (int)((bgra) & 0xff),
(int)((bgr >> 8) & 0xff), (int)((bgra >> 8) & 0xff),
(int)((bgr >> 16) & 0xff)); (int)((bgra >> 16) & 0xff),
(int)(255 - ((bgra >> 24) & 0xff)));
} }
}; };

View File

@ -555,27 +555,25 @@ bool SolveSpaceUI::LoadEntitiesFromFile(const char *file, EntityList *le,
} else if(StrStartsWith(line, "Triangle ")) { } else if(StrStartsWith(line, "Triangle ")) {
STriangle tr; ZERO(&tr); STriangle tr; ZERO(&tr);
unsigned int rgb = 0; unsigned int rgba = 0;
if(sscanf(line, "Triangle %x %x " if(sscanf(line, "Triangle %x %x "
"%lf %lf %lf %lf %lf %lf %lf %lf %lf", "%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.a.x), &(tr.a.y), &(tr.a.z),
&(tr.b.x), &(tr.b.y), &(tr.b.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(); oops();
} }
tr.meta.color = RgbColor::FromPackedInt((uint32_t)rgb); tr.meta.color = RgbColor::FromPackedInt((uint32_t)rgba);
m->AddTriangle(&tr); m->AddTriangle(&tr);
} else if(StrStartsWith(line, "Surface ")) { } else if(StrStartsWith(line, "Surface ")) {
unsigned int rgb = 0; unsigned int rgba = 0;
if(sscanf(line, "Surface %x %x %x %d %d", if(sscanf(line, "Surface %x %x %x %d %d",
&(srf.h.v), &rgb, &(srf.face), &(srf.h.v), &rgba, &(srf.face),
&(srf.degm), &(srf.degn)) != 5) &(srf.degm), &(srf.degn)) != 5) {
{
oops(); oops();
} }
srf.color = RgbColor::FromPackedInt((uint32_t)rgb); srf.color = RgbColor::FromPackedInt((uint32_t)rgba);
} else if(StrStartsWith(line, "SCtrl ")) { } else if(StrStartsWith(line, "SCtrl ")) {
int i, j; int i, j;
Vector c; Vector c;

View File

@ -269,28 +269,31 @@ static void StippleTriangle(STriangle *tr, bool s, RgbColor rgb)
glBegin(GL_TRIANGLES); 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), RgbColor rgbHovered = Style::Color(Style::HOVERED),
rgbSelected = Style::Color(Style::SELECTED); rgbSelected = Style::Color(Style::SELECTED);
glEnable(GL_NORMALIZE); glEnable(GL_NORMALIZE);
RgbColor prevColor = NULL_COLOR; bool hasMaterial = false;
RgbColor prevColor;
glBegin(GL_TRIANGLES); glBegin(GL_TRIANGLES);
for(int i = 0; i < m->l.n; i++) { for(int i = 0; i < m->l.n; i++) {
STriangle *tr = &(m->l.elem[i]); STriangle *tr = &(m->l.elem[i]);
RgbColor color; RgbColor color;
if(specColor.UseDefault()) { if(useSpecColor) {
color = tr->meta.color;
} else {
color = specColor; color = specColor;
} else {
color = tr->meta.color;
} }
if(!color.Equals(prevColor)) { if(!hasMaterial || !color.Equals(prevColor)) {
GLfloat mpf[] = { color.redF(), color.greenF(), color.blueF(), 1.0f }; GLfloat mpf[] = { color.redF(), color.greenF(), color.blueF(), color.alphaF() };
glEnd(); glEnd();
glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, mpf); glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, mpf);
prevColor = color; prevColor = color;
hasMaterial = true;
glBegin(GL_TRIANGLES); glBegin(GL_TRIANGLES);
} }

View File

@ -435,11 +435,13 @@ Group *Group::RunningMeshGroup(void) {
void Group::DrawDisplayItems(int t) { void Group::DrawDisplayItems(int t) {
RgbColor specColor; RgbColor specColor;
bool useSpecColor;
if(t == DRAWING_3D || t == DRAWING_WORKPLANE) { if(t == DRAWING_3D || t == DRAWING_WORKPLANE) {
// force the color to something dim // force the color to something dim
specColor = Style::Color(Style::DIM_SOLID); specColor = Style::Color(Style::DIM_SOLID);
useSpecColor = true;
} else { } 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 // The back faces are drawn in red; should never seem them, since we
// draw closed shells, so that's a debugging aid. // 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(gs.faces > 1) ms2 = gs.face[1].v;
if(SS.GW.showShaded) { 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); glEnable(GL_LIGHTING);
ssglFillMesh(specColor, &displayMesh, mh, ms1, ms2); ssglFillMesh(useSpecColor, specColor, &displayMesh, mh, ms1, ms2);
glDisable(GL_LIGHTING); glDisable(GL_LIGHTING);
} }
if(SS.GW.showEdges) { if(SS.GW.showEdges) {
ssglDepthRangeOffset(2); ssglDepthRangeOffset(2);
ssglColorRGB(Style::Color(Style::SOLID_EDGE)); ssglColorRGB(Style::Color(Style::SOLID_EDGE));

View File

@ -32,6 +32,7 @@ void SMesh::AddTriangle(STriMeta meta, Vector a, Vector b, Vector c) {
AddTriangle(&t); AddTriangle(&t);
} }
void SMesh::AddTriangle(STriangle *st) { void SMesh::AddTriangle(STriangle *st) {
if(st->meta.color.alpha != 255) isTransparent = true;
l.Add(st); l.Add(st);
} }

View File

@ -226,6 +226,7 @@ public:
bool flipNormal; bool flipNormal;
bool keepCoplanar; bool keepCoplanar;
bool atLeastOneDiscarded; bool atLeastOneDiscarded;
bool isTransparent;
void Clear(void); void Clear(void);
void AddTriangle(STriangle *st); void AddTriangle(STriangle *st);

View File

@ -300,7 +300,8 @@ void ssglAxisAlignedLineLoop(double l, double r, double t, double b);
extern "C" { typedef void SSGL_CALLBACK ssglCallbackFptr(void); } extern "C" { typedef void SSGL_CALLBACK ssglCallbackFptr(void); }
void ssglTesselatePolygon(GLUtesselator *gt, SPolygon *p); void ssglTesselatePolygon(GLUtesselator *gt, SPolygon *p);
void ssglFillPolygon(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 ssglDebugPolygon(SPolygon *p);
void ssglDrawEdges(SEdgeList *l, bool endpointsToo); void ssglDrawEdges(SEdgeList *l, bool endpointsToo);
void ssglDebugMesh(SMesh *m); void ssglDebugMesh(SMesh *m);

View File

@ -489,8 +489,7 @@ typedef struct {
hSSurface hs; hSSurface hs;
} TrimLine; } TrimLine;
void SShell::MakeFromExtrusionOf(SBezierLoopSet *sbls, Vector t0, Vector t1, void SShell::MakeFromExtrusionOf(SBezierLoopSet *sbls, Vector t0, Vector t1, RgbColor color)
RgbColor color)
{ {
// Make the extrusion direction consistent with respect to the normal // Make the extrusion direction consistent with respect to the normal
// of the sketch we're extruding. // of the sketch we're extruding.
@ -609,8 +608,7 @@ typedef struct {
hSSurface d[4]; hSSurface d[4];
} Revolved; } Revolved;
void SShell::MakeFromRevolutionOf(SBezierLoopSet *sbls, Vector pt, Vector axis, void SShell::MakeFromRevolutionOf(SBezierLoopSet *sbls, Vector pt, Vector axis, RgbColor color)
RgbColor color)
{ {
SBezierLoop *sbl; SBezierLoop *sbl;

View File

@ -22,7 +22,7 @@ const Style::Default Style::Defaults[] = {
{ { ANALYZE }, "Analyze", RGBf(0.0, 1.0, 1.0), 1.0, }, { { ANALYZE }, "Analyze", RGBf(0.0, 1.0, 1.0), 1.0, },
{ { DRAW_ERROR }, "DrawError", RGBf(1.0, 0.0, 0.0), 8.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, }, { { 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) { char *Style::CnfColor(const char *prefix) {

View File

@ -225,6 +225,15 @@ void TextWindow::ScreenColor(int link, uint32_t v) {
SS.TW.ShowEditControlWithColorPicker(v, 3, g->color); SS.TW.ShowEditControlWithColorPicker(v, 3, g->color);
SS.TW.edit.meaning = EDIT_GROUP_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) { void TextWindow::ScreenChangeExprA(int link, uint32_t v) {
Group *g = SK.GetGroup(SS.TW.shown.group); Group *g = SK.GetGroup(SS.TW.shown.group);
@ -369,6 +378,9 @@ void TextWindow::ShowGroupInfo(void) {
&g->color, &g->color,
g->color.redF(), g->color.greenF(), g->color.blueF(), g->color.redF(), g->color.greenF(), g->color.blueF(),
ScreenColor, top[rows-1] + 2); 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) { } else if(g->type == Group::IMPORTED) {
bool sup = g->suppress; bool sup = g->suppress;
Printf(false, " %Fd%f%LP%c suppress this group's solid model", Printf(false, " %Fd%f%LP%c suppress this group's solid model",
@ -697,6 +709,22 @@ void TextWindow::EditControlDone(const char *s) {
} }
break; 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: { case EDIT_TTF_TEXT: {
SS.UndoRemember(); SS.UndoRemember();
Request *r = SK.request.FindByIdNoOops(edit.request); Request *r = SK.request.FindByIdNoOops(edit.request);

View File

@ -18,14 +18,14 @@ const TextWindow::Color TextWindow::fgColors[] = {
{ 'i', RGBi( 0, 255, 255) }, { 'i', RGBi( 0, 255, 255) },
{ 'g', RGBi(160, 160, 160) }, { 'g', RGBi(160, 160, 160) },
{ 'b', RGBi(200, 200, 200) }, { 'b', RGBi(200, 200, 200) },
{ 0, NULL_COLOR } { 0, RGBi( 0, 0, 0) }
}; };
const TextWindow::Color TextWindow::bgColors[] = { const TextWindow::Color TextWindow::bgColors[] = {
{ 'd', RGBi( 0, 0, 0) }, { 'd', RGBi( 0, 0, 0) },
{ 't', RGBi( 34, 15, 15) }, { 't', RGBi( 34, 15, 15) },
{ 'a', RGBi( 25, 25, 25) }, { 'a', RGBi( 25, 25, 25) },
{ 'r', RGBi(255, 255, 255) }, { 'r', RGBi(255, 255, 255) },
{ 0, NULL_COLOR } { 0, RGBi( 0, 0, 0) }
}; };
bool TextWindow::SPACER = false; bool TextWindow::SPACER = false;
@ -132,7 +132,7 @@ void TextWindow::Printf(bool halfLine, const char *fmt, ...) {
char fg = 'd'; char fg = 'd';
char bg = 'd'; char bg = 'd';
RgbColor bgRgb = NULL_COLOR; RgbColor bgRgb = RGBi(0, 0, 0);
int link = NOT_A_LINK; int link = NOT_A_LINK;
uint32_t data = 0; uint32_t data = 0;
LinkFunction *f = NULL, *h = NULL; LinkFunction *f = NULL, *h = NULL;

View File

@ -139,6 +139,7 @@ public:
EDIT_GROUP_NAME = 2, EDIT_GROUP_NAME = 2,
EDIT_GROUP_SCALE = 3, EDIT_GROUP_SCALE = 3,
EDIT_GROUP_COLOR = 4, EDIT_GROUP_COLOR = 4,
EDIT_GROUP_OPACITY = 5,
// For the configuraiton screen // For the configuraiton screen
EDIT_LIGHT_DIRECTION = 100, EDIT_LIGHT_DIRECTION = 100,
EDIT_LIGHT_INTENSITY = 101, EDIT_LIGHT_INTENSITY = 101,
@ -254,6 +255,7 @@ public:
static void ScreenChangeGroupOption(int link, uint32_t v); static void ScreenChangeGroupOption(int link, uint32_t v);
static void ScreenColor(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 ScreenShowListOfStyles(int link, uint32_t v);
static void ScreenShowStyleInfo(int link, uint32_t v); static void ScreenShowStyleInfo(int link, uint32_t v);