Add perspective, by a user-specified factor. So I have to apply

that perspective in the gl matrices, and also everywhere that I
check mouse pointer positions against the model, and for the zoom
to fit.

[git-p4: depot-paths = "//depot/solvespace/": change = 1796]
This commit is contained in:
Jonathan Westhues 2008-06-17 11:12:25 -08:00
parent b5c87291ea
commit e67ea9ca0f
10 changed files with 178 additions and 62 deletions

View File

@ -835,14 +835,25 @@ void GraphicsWindow::Paint(int w, int h) {
glScaled(scale*2.0/w, scale*2.0/h, scale*1.0/30000);
double tx = projRight.Dot(offset);
double ty = projUp.Dot(offset);
Vector n = projUp.Cross(projRight);
double tz = n.Dot(offset);
double mat[16];
MakeMatrix(mat, projRight.x, projRight.y, projRight.z, tx,
projUp.x, projUp.y, projUp.z, ty,
n.x, n.y, n.z, tz,
// Last thing before display is to apply the perspective
double clp = SS.cameraTangent*scale;
MakeMatrix(mat, 1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, clp, 1);
glMultMatrixd(mat);
// Before that, we apply the rotation
Vector n = projUp.Cross(projRight);
MakeMatrix(mat, projRight.x, projRight.y, projRight.z, 0,
projUp.x, projUp.y, projUp.z, 0,
n.x, n.y, n.z, 0,
0, 0, 0, 1);
glMultMatrixd(mat);
// And before that, the translation
MakeMatrix(mat, 1, 0, 0, offset.x,
0, 1, 0, offset.y,
0, 0, 1, offset.z,
0, 0, 0, 1);
glMultMatrixd(mat);

View File

@ -2,15 +2,13 @@
void Entity::LineDrawOrGetDistance(Vector a, Vector b) {
if(dogd.drawing) {
// glPolygonOffset works only on polys, not lines, so do it myself
Vector adj = SS.GW.projRight.Cross(SS.GW.projUp);
// Draw lines from active group in front of those from previous
int delta = (group.v == SS.GW.activeGroup.v) ? 10 : 5;
adj = adj.ScaledBy(delta/SS.GW.scale);
glxDepthRangeOffset((group.v == SS.GW.activeGroup.v) ? 4 : 2);
glBegin(GL_LINES);
glxVertex3v(a.Plus(adj));
glxVertex3v(b.Plus(adj));
glxVertex3v(a);
glxVertex3v(b);
glEnd();
glxDepthRangeOffset(0);
} else {
Point2d ap = SS.GW.ProjectPoint(a);
Point2d bp = SS.GW.ProjectPoint(b);
@ -40,7 +38,7 @@ void Entity::DrawAll(void) {
Vector r = SS.GW.projRight.ScaledBy(s);
Vector d = SS.GW.projUp.ScaledBy(s);
glxColor3d(0, 0.8, 0);
glPolygonOffset(-10, -10);
glxDepthRangeOffset(6);
glBegin(GL_QUADS);
for(i = 0; i < SS.entity.n; i++) {
Entity *e = &(SS.entity.elem[i]);
@ -56,7 +54,7 @@ void Entity::DrawAll(void) {
glxVertex3v(v.Minus(r).Plus (d));
}
glEnd();
glPolygonOffset(0, 0);
glxDepthRangeOffset(0);
}
glLineWidth(1.5);
@ -167,14 +165,14 @@ void Entity::DrawOrGetDistance(void) {
Vector d = SS.GW.projUp.ScaledBy(s/SS.GW.scale);
glxColor3d(0, 0.8, 0);
glPolygonOffset(-10, -10);
glxDepthRangeOffset(6);
glBegin(GL_QUADS);
glxVertex3v(v.Plus (r).Plus (d));
glxVertex3v(v.Plus (r).Minus(d));
glxVertex3v(v.Minus(r).Minus(d));
glxVertex3v(v.Minus(r).Plus (d));
glEnd();
glPolygonOffset(0, 0);
glxDepthRangeOffset(0);
} else {
Point2d pp = SS.GW.ProjectPoint(v);
dogd.dmin = pp.DistanceTo(dogd.mp) - 6;
@ -214,10 +212,9 @@ void Entity::DrawOrGetDistance(void) {
double s = SS.GW.scale;
double h = 60 - SS.GW.height/2;
double w = 60 - SS.GW.width/2;
Vector gn = SS.GW.projRight.Cross(SS.GW.projUp);
tail = SS.GW.projRight.ScaledBy(w/s).Plus(
SS.GW.projUp. ScaledBy(h/s)).Plus(
gn.ScaledBy(-4*w/s)).Minus(SS.GW.offset);
SS.GW.projUp. ScaledBy(h/s)).Minus(SS.GW.offset);
glxDepthRangeLockToFront(true);
glLineWidth(2);
}
@ -230,6 +227,7 @@ void Entity::DrawOrGetDistance(void) {
LineDrawOrGetDistance(tip,tip.Minus(v.RotatedAbout(axis, 0.6)));
LineDrawOrGetDistance(tip,tip.Minus(v.RotatedAbout(axis,-0.6)));
}
glxDepthRangeLockToFront(false);
glLineWidth(1.5);
break;
}

View File

@ -4,6 +4,7 @@
#include "font.table"
static bool ColorLocked;
static bool DepthOffsetLocked;
#define FONT_SCALE (0.55)
double glxStrWidth(char *str) {
@ -309,6 +310,7 @@ void glxDebugMesh(SMesh *m)
int i;
glLineWidth(1);
glPointSize(7);
glxDepthRangeOffset(1);
glxUnlockColor();
for(i = 0; i < m->l.n; i++) {
STriangle *t = &(m->l.elem[i]);
@ -321,9 +323,9 @@ void glxDebugMesh(SMesh *m)
glxVertex3v(t->b);
glxVertex3v(t->c);
glEnd();
glPolygonOffset(0, 0);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}
glxDepthRangeOffset(0);
}
void glxMarkPolygonNormal(SPolygon *p)
@ -358,3 +360,22 @@ void glxMarkPolygonNormal(SPolygon *p)
glEnable(GL_LIGHTING);
}
void glxDepthRangeOffset(int units) {
if(!DepthOffsetLocked) {
// The size of this step depends on the resolution of the Z buffer; for
// a 16-bit buffer, this should be fine.
double d = units/60000.0;
glDepthRange(0.1-d, 1-d);
}
}
void glxDepthRangeLockToFront(bool yes) {
if(yes) {
DepthOffsetLocked = true;
glDepthRange(0, 0);
} else {
DepthOffsetLocked = false;
glxDepthRangeOffset(0);
}
}

View File

@ -125,12 +125,38 @@ void GraphicsWindow::NormalizeProjectionVectors(void) {
projRight = projRight.ScaledBy(1/projRight.Magnitude());
}
//-----------------------------------------------------------------------------
// Project a point in model space to screen space, exactly as gl would; return
// units are pixels.
//-----------------------------------------------------------------------------
Point2d GraphicsWindow::ProjectPoint(Vector p) {
Vector p3 = ProjectPoint3(p);
Point2d p2 = { p3.x, p3.y };
return p2;
}
//-----------------------------------------------------------------------------
// Project a point in model space to screen space, exactly as gl would; return
// units are pixels. The z coordinate is also returned, also in pixels.
//-----------------------------------------------------------------------------
Vector GraphicsWindow::ProjectPoint3(Vector p) {
double w;
Vector r = ProjectPoint4(p, &w);
return r.ScaledBy(scale/w);
}
//-----------------------------------------------------------------------------
// Project a point in model space halfway into screen space. The scale is
// not applied, and the perspective divide isn't applied; instead the w
// coordinate is returned separately.
//-----------------------------------------------------------------------------
Vector GraphicsWindow::ProjectPoint4(Vector p, double *w) {
p = p.Plus(offset);
Point2d r;
r.x = p.Dot(projRight) * scale;
r.y = p.Dot(projUp) * scale;
Vector r;
r.x = p.Dot(projRight);
r.y = p.Dot(projUp);
r.z = p.Dot(projUp.Cross(projRight));
*w = 1 + r.z*SS.cameraTangent*scale;
return r;
}
@ -180,57 +206,86 @@ void GraphicsWindow::AnimateOntoWorkplane(void) {
}
void GraphicsWindow::HandlePointForZoomToFit(Vector p,
Point2d *pmax, Point2d *pmin)
Point2d *pmax, Point2d *pmin, double *wmin, bool div)
{
Point2d p2 = ProjectPoint(p);
pmax->x = max(pmax->x, p2.x);
pmax->y = max(pmax->y, p2.y);
pmin->x = min(pmin->x, p2.x);
pmin->y = min(pmin->y, p2.y);
double w;
Vector pp = ProjectPoint4(p, &w);
if(div) {
pp = pp.ScaledBy(1.0/w);
}
pmax->x = max(pmax->x, pp.x);
pmax->y = max(pmax->y, pp.y);
pmin->x = min(pmin->x, pp.x);
pmin->y = min(pmin->y, pp.y);
*wmin = min(*wmin, w);
}
void GraphicsWindow::ZoomToFit(void) {
void GraphicsWindow::LoopOverPoints(
Point2d *pmax, Point2d *pmin, double *wmin, bool div)
{
HandlePointForZoomToFit(Vector::From(0, 0, 0), pmax, pmin, wmin, div);
int i, j;
Point2d pmax = { -1e12, -1e12 }, pmin = { 1e12, 1e12 };
HandlePointForZoomToFit(Vector::From(0, 0, 0), &pmax, &pmin);
for(i = 0; i < SS.entity.n; i++) {
Entity *e = &(SS.entity.elem[i]);
if(!e->IsPoint()) continue;
if(!e->IsVisible()) continue;
HandlePointForZoomToFit(e->PointGetNum(), &pmax, &pmin);
HandlePointForZoomToFit(e->PointGetNum(), pmax, pmin, wmin, div);
}
Group *g = SS.GetGroup(activeGroup);
for(i = 0; i < g->mesh.l.n; i++) {
STriangle *tr = &(g->mesh.l.elem[i]);
HandlePointForZoomToFit(tr->a, &pmax, &pmin);
HandlePointForZoomToFit(tr->b, &pmax, &pmin);
HandlePointForZoomToFit(tr->c, &pmax, &pmin);
HandlePointForZoomToFit(tr->a, pmax, pmin, wmin, div);
HandlePointForZoomToFit(tr->b, pmax, pmin, wmin, div);
HandlePointForZoomToFit(tr->c, pmax, pmin, wmin, div);
}
for(i = 0; i < g->poly.l.n; i++) {
SContour *sc = &(g->poly.l.elem[i]);
for(j = 0; j < sc->l.n; j++) {
HandlePointForZoomToFit(sc->l.elem[j].p, &pmax, &pmin);
HandlePointForZoomToFit(sc->l.elem[j].p, pmax, pmin, wmin, div);
}
}
}
void GraphicsWindow::ZoomToFit(void) {
// On the first run, ignore perspective.
Point2d pmax = { -1e12, -1e12 }, pmin = { 1e12, 1e12 };
double wmin = 1;
LoopOverPoints(&pmax, &pmin, &wmin, false);
pmax = pmax.ScaledBy(1/scale);
pmin = pmin.ScaledBy(1/scale);
double xm = (pmax.x + pmin.x)/2, ym = (pmax.y + pmin.y)/2;
double dx = pmax.x - pmin.x, dy = pmax.y - pmin.y;
offset = offset.Plus(projRight.ScaledBy(-xm)).Plus(
projUp. ScaledBy(-ym));
// And based on this, we calculate the scale and offset
if(dx == 0 && dy == 0) {
scale = 5;
} else {
double scalex = 1e12, scaley = 1e12;
if(dx != 0) scalex = 0.9*width /dx;
if(dy != 0) scaley = 0.9*height/dy;
scale = min(100, min(scalex, scaley));
scale = min(scalex, scaley);
scale = min(100, scale);
scale = max(0.001, scale);
}
// Then do another run, considering the perspective.
pmax.x = -1e12; pmax.y = -1e12;
pmin.x = 1e12; pmin.y = 1e12;
wmin = 1;
LoopOverPoints(&pmax, &pmin, &wmin, true);
// Adjust the scale so that no points are behind the camera
if(wmin < 0.1) {
double k = SS.cameraTangent;
// w = 1+k*scale*z
double zmin = (wmin - 1)/(k*scale);
// 0.1 = 1 + k*scale*zmin
// (0.1 - 1)/(k*zmin) = scale
scale = min(scale, (0.1 - 1)/(k*zmin));
}
scale = max(0.001, scale);
}
void GraphicsWindow::MenuView(int id) {

View File

@ -314,9 +314,9 @@ void Group::Draw(void) {
glEnable(GL_DEPTH_TEST);
} else {
glxColor4d(0, 0.1, 0.1, 0.5);
glPolygonOffset(-1, -1);
glxDepthRangeOffset(1);
glxFillPolygon(&poly);
glPolygonOffset(0, 0);
glxDepthRangeOffset(0);
}
}

View File

@ -258,28 +258,29 @@ bool SMesh::MakeFromInterferenceCheck(SMesh *srca, SMesh *srcb, SMesh *error) {
}
DWORD SMesh::FirstIntersectionWith(Point2d mp) {
Vector gu = SS.GW.projRight, gv = SS.GW.projUp;
Vector gn = (gu.Cross(gv)).WithMagnitude(1);
Vector p0 = gu.ScaledBy(mp.x/SS.GW.scale).Plus(
gv.ScaledBy(mp.y/SS.GW.scale));
p0 = p0.Minus(SS.GW.offset);
Vector p0 = Vector::From(mp.x, mp.y, 0);
Vector gn = Vector::From(0, 0, 1);
double maxT = -1e12;
DWORD face = 0;
int i;
for(i = 0; i < l.n; i++) {
STriangle *tr = &(l.elem[i]);
Vector n = tr->Normal();
STriangle tr = l.elem[i];
tr.a = SS.GW.ProjectPoint3(tr.a);
tr.b = SS.GW.ProjectPoint3(tr.b);
tr.c = SS.GW.ProjectPoint3(tr.c);
Vector n = tr.Normal();
if(n.Dot(gn) < LENGTH_EPS) continue; // back-facing or on edge
if(tr->ContainsPointProjd(gn, p0)) {
if(tr.ContainsPointProjd(gn, p0)) {
// Let our line have the form r(t) = p0 + gn*t
double t = (n.Dot((tr->a).Minus(p0)))/(n.Dot(gn));
double t = -(n.Dot((tr.a).Minus(p0)))/(n.Dot(gn));
if(t > maxT) {
maxT = t;
face = tr->meta.face;
face = tr.meta.face;
}
}
}
@ -691,7 +692,7 @@ void SBsp3::DebugDraw(void) {
glDisable(GL_LIGHTING);
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glPolygonOffset(-1, 0);
glxDepthRangeOffset(2);
glBegin(GL_TRIANGLES);
glxVertex3v(tri.a);
glxVertex3v(tri.b);
@ -701,14 +702,14 @@ void SBsp3::DebugDraw(void) {
glDisable(GL_LIGHTING);
glPolygonMode(GL_FRONT_AND_BACK, GL_POINT);
glPointSize(10);
glPolygonOffset(-1, 0);
glxDepthRangeOffset(2);
glBegin(GL_TRIANGLES);
glxVertex3v(tri.a);
glxVertex3v(tri.b);
glxVertex3v(tri.c);
glEnd();
glPolygonOffset(0, 0);
glxDepthRangeOffset(0);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
more->DebugDraw();

View File

@ -27,6 +27,8 @@ void SolveSpace::Init(char *cmdLine) {
meshTol = ((int)CnfThawDWORD(1000, "MeshTolerance"))/1000.0;
// View units
viewUnits = (Unit)CnfThawDWORD((DWORD)UNIT_MM, "ViewUnits");
// Camera tangent (determines perspective)
cameraTangent = ((int)CnfThawDWORD(0, "CameraTangent"))/1e6;
// Recent files menus
for(i = 0; i < MAX_RECENT; i++) {
char name[100];
@ -77,6 +79,8 @@ void SolveSpace::Exit(void) {
CnfFreezeDWORD((int)(meshTol*1000), "MeshTolerance");
// Display/entry units
CnfFreezeDWORD((int)viewUnits, "ViewUnits");
// Camera tangent (determines perspective)
CnfFreezeDWORD((int)(cameraTangent*1e6), "CameraTangent");
ExitNow();
}

View File

@ -127,6 +127,8 @@ void glxLockColorTo(double r, double g, double b);
void glxUnlockColor(void);
void glxColor3d(double r, double g, double b);
void glxColor4d(double r, double g, double b, double a);
void glxDepthRangeOffset(int units);
void glxDepthRangeLockToFront(bool yes);
#define arraylen(x) (sizeof((x))/sizeof((x)[0]))
@ -253,6 +255,7 @@ public:
Vector lightDir[2];
double lightIntensity[2];
double meshTol;
double cameraTangent;
int CircleSides(double r);
typedef enum {
UNIT_MM = 0,

View File

@ -877,6 +877,12 @@ void TextWindow::ScreenChangeMeshTolerance(int link, DWORD v) {
ShowTextEditControl(37, 3, str);
SS.TW.edit.meaning = EDIT_MESH_TOLERANCE;
}
void TextWindow::ScreenChangeCameraTangent(int link, DWORD v) {
char str[1024];
sprintf(str, "%.3f", 1000*SS.cameraTangent);
ShowTextEditControl(43, 3, str);
SS.TW.edit.meaning = EDIT_CAMERA_TANGENT;
}
void TextWindow::ShowConfiguration(void) {
int i;
Printf(true, "%Ft material color-(r, g, b)");
@ -908,6 +914,12 @@ void TextWindow::ShowConfiguration(void) {
SS.meshTol,
&ScreenChangeMeshTolerance, 0,
SS.group.elem[SS.group.n-1].mesh.l.n);
Printf(false, "");
Printf(false, "%Ft perspective factor (0 for isometric)%E");
Printf(false, "%Ba %3 %Fl%Ll%f%D[change]%E",
SS.cameraTangent*1000,
&ScreenChangeCameraTangent, 0);
}
void TextWindow::EditControlDone(char *s) {
@ -974,6 +986,11 @@ void TextWindow::EditControlDone(char *s) {
SS.GenerateAll(0, INT_MAX);
break;
}
case EDIT_CAMERA_TANGENT: {
SS.cameraTangent = (min(2, max(0, atof(s))))/1000.0;
InvalidateGraphics();
break;
}
}
SS.later.showTW = true;
HideTextEditControl();

8
ui.h
View File

@ -63,6 +63,7 @@ public:
static const int EDIT_LIGHT_INTENSITY = 4;
static const int EDIT_COLOR = 5;
static const int EDIT_MESH_TOLERANCE = 6;
static const int EDIT_CAMERA_TANGENT = 7;
struct {
int meaning;
int i;
@ -115,6 +116,7 @@ public:
static void ScreenChangeLightIntensity(int link, DWORD v);
static void ScreenChangeColor(int link, DWORD v);
static void ScreenChangeMeshTolerance(int link, DWORD v);
static void ScreenChangeCameraTangent(int link, DWORD v);
void EditControlDone(char *s);
};
@ -217,9 +219,13 @@ public:
void NormalizeProjectionVectors(void);
Point2d ProjectPoint(Vector p);
Vector ProjectPoint3(Vector p);
Vector ProjectPoint4(Vector p, double *w);
void AnimateOntoWorkplane(void);
Vector VectorFromProjs(Vector rightUpForward);
void HandlePointForZoomToFit(Vector p, Point2d *pmax, Point2d *pmin);
void HandlePointForZoomToFit(Vector p, Point2d *pmax, Point2d *pmin,
double *wmin, bool div);
void LoopOverPoints(Point2d *pmax, Point2d *pmin, double *wmin, bool div);
void ZoomToFit(void);
hGroup activeGroup;