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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -226,6 +226,7 @@ public:
bool flipNormal;
bool keepCoplanar;
bool atLeastOneDiscarded;
bool isTransparent;
void Clear(void);
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); }
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);

View File

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

View File

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

View File

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

View File

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

View File

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