summaryrefslogtreecommitdiff
path: root/Build/source/utils/asymptote/glrender.cc
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2009-05-16 00:19:13 +0000
committerKarl Berry <karl@freefriends.org>2009-05-16 00:19:13 +0000
commitbab45528d65eaafe68a705dbb2a57075c7b7cbd8 (patch)
tree10b4ae2b5195c8dede153ab89359ec00f55f325f /Build/source/utils/asymptote/glrender.cc
parent8643d90372e9c31e0f461c15c596b60a545bd7d3 (diff)
asymptote 1.72 sources (not integrated into build yet)
git-svn-id: svn://tug.org/texlive/trunk@13110 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Build/source/utils/asymptote/glrender.cc')
-rw-r--r--Build/source/utils/asymptote/glrender.cc1306
1 files changed, 1306 insertions, 0 deletions
diff --git a/Build/source/utils/asymptote/glrender.cc b/Build/source/utils/asymptote/glrender.cc
new file mode 100644
index 00000000000..29136c36358
--- /dev/null
+++ b/Build/source/utils/asymptote/glrender.cc
@@ -0,0 +1,1306 @@
+/*****
+ * glrender.cc
+ * John Bowman and Orest Shardt
+ * Render 3D Bezier paths and surfaces.
+ *****/
+
+#include <stdlib.h>
+#include <fstream>
+#include <cstring>
+#include <sys/time.h>
+#include <signal.h>
+
+#include "common.h"
+
+namespace gl {
+bool glthread=false;
+bool initialize=true;
+}
+
+#ifdef HAVE_LIBGL
+
+// For CYGWIN
+#ifndef FGAPI
+#define FGAPI GLUTAPI
+#endif
+#ifndef FGAPIENTRY
+#define FGAPIENTRY APIENTRY
+#endif
+
+#define GLUT_BUILDING_LIB
+
+#include "picture.h"
+#include "arcball.h"
+#include "bbox3.h"
+#include "drawimage.h"
+#include "interact.h"
+#include "tr.h"
+
+#ifdef FREEGLUT
+#include <GL/freeglut_ext.h>
+#endif
+
+namespace gl {
+
+using camp::picture;
+using camp::drawImage;
+using camp::transform;
+using camp::pair;
+using camp::triple;
+using vm::array;
+using camp::bbox3;
+using settings::getSetting;
+using settings::Setting;
+
+const double moveFactor=1.0;
+const double zoomFactor=1.05;
+const double zoomFactorStep=0.1;
+const double spinStep=60.0; // Degrees per second
+const double arcballRadius=750.0;
+const double resizeStep=1.2;
+
+double Aspect;
+bool View;
+bool Iconify=false;
+int Oldpid;
+string Prefix;
+const picture* Picture;
+string Format;
+int Width,Height;
+int fullWidth,fullHeight;
+int oldWidth,oldHeight;
+double oWidth,oHeight;
+int screenWidth,screenHeight;
+int maxWidth;
+int maxHeight;
+int maxTileWidth;
+int maxTileHeight;
+
+double *T;
+
+bool Xspin,Yspin,Zspin;
+bool Menu;
+bool Motion;
+bool ignorezoom;
+
+int Fitscreen;
+int Mode;
+
+double H;
+double xmin,xmax;
+double ymin,ymax;
+double zmin,zmax;
+
+double Xmin,Xmax;
+double Ymin,Ymax;
+double X,Y;
+
+int minimumsize=50; // Minimum initial rendering window width and height
+
+const double degrees=180.0/M_PI;
+const double radians=1.0/degrees;
+
+size_t Nlights;
+triple *Lights;
+double *Diffuse;
+double *Ambient;
+double *Specular;
+bool ViewportLighting;
+bool queueExport=false;
+bool antialias;
+
+int x0,y0;
+int mod;
+
+double lastangle;
+
+double Zoom;
+double lastzoom;
+
+GLfloat Rotate[16];
+Arcball arcball;
+
+GLUnurbs *nurb;
+
+int window;
+
+void *glrenderWrapper(void *a);
+
+#ifdef HAVE_LIBPTHREAD
+pthread_t mainthread;
+
+pthread_cond_t initSignal=PTHREAD_COND_INITIALIZER;
+pthread_mutex_t initLock=PTHREAD_MUTEX_INITIALIZER;
+
+pthread_cond_t quitSignal=PTHREAD_COND_INITIALIZER;
+pthread_mutex_t quitLock=PTHREAD_MUTEX_INITIALIZER;
+
+pthread_cond_t readySignal=PTHREAD_COND_INITIALIZER;
+pthread_mutex_t readyLock=PTHREAD_MUTEX_INITIALIZER;
+#endif
+
+inline void lock()
+{
+#ifdef HAVE_LIBPTHREAD
+ if(glthread)
+ pthread_mutex_lock(&readyLock);
+#endif
+}
+
+inline void unlock()
+{
+#ifdef HAVE_LIBPTHREAD
+ if(glthread)
+ pthread_mutex_unlock(&readyLock);
+#endif
+
+}
+
+template<class T>
+inline T min(T a, T b)
+{
+ return (a < b) ? a : b;
+}
+
+template<class T>
+inline T max(T a, T b)
+{
+ return (a > b) ? a : b;
+}
+
+void lighting()
+{
+ for(size_t i=0; i < Nlights; ++i) {
+ GLenum index=GL_LIGHT0+i;
+ triple Lighti=Lights[i];
+ GLfloat position[]={Lighti.getx(),Lighti.gety(),Lighti.getz(),0.0};
+ glLightfv(index,GL_POSITION,position);
+ }
+}
+
+void initlighting()
+{
+ glEnable(GL_LIGHTING);
+ glLightModeli(GL_LIGHT_MODEL_TWO_SIDE,getSetting<bool>("twosided"));
+
+ for(size_t i=0; i < Nlights; ++i) {
+ GLenum index=GL_LIGHT0+i;
+ glEnable(index);
+
+ size_t i4=4*i;
+
+ GLfloat diffuse[]={Diffuse[i4],Diffuse[i4+1],Diffuse[i4+2],Diffuse[i4+3]};
+ glLightfv(index,GL_DIFFUSE,diffuse);
+
+ GLfloat ambient[]={Ambient[i4],Ambient[i4+1],Ambient[i4+2],Ambient[i4+3]};
+ glLightfv(index,GL_AMBIENT,ambient);
+
+ GLfloat specular[]={Specular[i4],Specular[i4+1],Specular[i4+2],
+ Specular[i4+3]};
+ glLightfv(index,GL_SPECULAR,specular);
+ }
+
+ static size_t lastNlights=0;
+ for(size_t i=Nlights; i < lastNlights; ++i) {
+ GLenum index=GL_LIGHT0+i;
+ glDisable(index);
+ }
+ lastNlights=Nlights;
+
+ if(ViewportLighting)
+ lighting();
+}
+
+void setDimensions(int Width, int Height, double X, double Y)
+{
+ double Aspect=((double) Width)/Height;
+ double X0=X*(xmax-xmin)/(lastzoom*Width);
+ double Y0=Y*(ymax-ymin)/(lastzoom*Height);
+ if(H == 0.0) {
+ double xsize=Xmax-Xmin;
+ double ysize=Ymax-Ymin;
+ if(xsize < ysize*Aspect) {
+ double r=0.5*ysize*Zoom*Aspect;
+ xmin=-r-X0;
+ xmax=r-X0;
+ ymin=Ymin*Zoom-Y0;
+ ymax=Ymax*Zoom-Y0;
+ } else {
+ double r=0.5*xsize*Zoom/Aspect;
+ xmin=Xmin*Zoom-X0;
+ xmax=Xmax*Zoom-X0;
+ ymin=-r-Y0;
+ ymax=r-Y0;
+ }
+ } else {
+ double r=H*Zoom;
+ xmin=-r*Aspect-X0;
+ xmax=r*Aspect-X0;
+ ymin=-r-Y0;
+ ymax=r-Y0;
+ }
+}
+
+void setProjection()
+{
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ setDimensions(Width,Height,X,Y);
+ if(H == 0.0)
+ glOrtho(xmin,xmax,ymin,ymax,-zmax,-zmin);
+ else
+ glFrustum(xmin,xmax,ymin,ymax,-zmax,-zmin);
+ glMatrixMode(GL_MODELVIEW);
+ arcball.set_params(vec2(0.5*Width,0.5*Height),arcballRadius/Zoom);
+}
+
+bool capsize(int& width, int& height)
+{
+ bool resize=false;
+ if(width > maxWidth) {
+ width=maxWidth;
+ resize=true;
+ }
+ if(height > maxHeight) {
+ height=maxHeight;
+ resize=true;
+ }
+ return resize;
+}
+
+void reshape0(int width, int height)
+{
+ X=X/Width*width;
+ Y=Y/Height*height;
+
+ Width=width;
+ Height=height;
+
+ setProjection();
+ glViewport(0,0,Width,Height);
+}
+
+void update()
+{
+ lastzoom=Zoom;
+ glLoadIdentity();
+ double cz=0.5*(zmin+zmax);
+ glTranslatef(0,0,cz);
+ glMultMatrixf(Rotate);
+ glTranslatef(0,0,-cz);
+ setProjection();
+ glutPostRedisplay();
+}
+
+void drawscene(double Width, double Height)
+{
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ if(!ViewportLighting)
+ lighting();
+
+ triple m(xmin,ymin,zmin);
+ triple M(xmax,ymax,zmax);
+ double perspective=H == 0.0 ? 0.0 : 1.0/zmax;
+
+ double size2=hypot(Width,Height);
+
+ glEnable(GL_BLEND);
+ // Render opaque objects
+ Picture->render(nurb,size2,m,M,perspective,false);
+
+ // Enable transparency
+ glEnable(GL_BLEND);
+ glDepthMask(GL_FALSE);
+
+ // Render transparent objects
+ Picture->render(nurb,size2,m,M,perspective,true);
+ glDepthMask(GL_TRUE);
+ glDisable(GL_BLEND);
+}
+
+// Return x divided by y rounded up to the nearest integer.
+int Quotient(int x, int y)
+{
+ return (x+y-1)/y;
+}
+
+void Export()
+{
+ glReadBuffer(GL_BACK_LEFT);
+ glPixelStorei(GL_PACK_ALIGNMENT,1);
+ glFinish();
+ try {
+ size_t ndata=3*fullWidth*fullHeight;
+ unsigned char *data=new unsigned char[ndata];
+ if(data) {
+ TRcontext *tr=trNew();
+ int width=Quotient(fullWidth,Quotient(fullWidth,min(maxTileWidth,Width)));
+ int height=Quotient(fullHeight,Quotient(fullHeight,
+ min(maxTileHeight,Height)));
+ if(settings::verbose > 1)
+ cout << "Exporting " << Prefix << " as " << fullWidth << "x"
+ << fullHeight << " image" << " using tiles of size "
+ << width << "x" << height << endl;
+
+ trTileSize(tr,width,height,0);
+ trImageSize(tr,fullWidth,fullHeight);
+ trImageBuffer(tr,GL_RGB,GL_UNSIGNED_BYTE,data);
+
+ setDimensions(fullWidth,fullHeight,X/Width*fullWidth,Y/Width*fullWidth);
+ if(H == 0.0)
+ trOrtho(tr,xmin,xmax,ymin,ymax,-zmax,-zmin);
+ else
+ trFrustum(tr,xmin,xmax,ymin,ymax,-zmax,-zmin);
+
+ size_t count=0;
+ do {
+ trBeginTile(tr);
+ drawscene(fullWidth,fullHeight);
+ ++count;
+ } while (trEndTile(tr));
+ if(settings::verbose > 1)
+ cout << count << " tile" << (count > 1 ? "s" : "") << " drawn" << endl;
+ trDelete(tr);
+
+ picture pic;
+ double w=oWidth;
+ double h=oHeight;
+ double Aspect=((double) fullWidth)/fullHeight;
+ if(w > h*Aspect) w=(int) (h*Aspect+0.5);
+ else h=(int) (w/Aspect+0.5);
+ // Render an antialiased image.
+ drawImage *Image=new drawImage(data,fullWidth,fullHeight,
+ transform(0.0,0.0,w,0.0,0.0,h),antialias);
+ pic.append(Image);
+ pic.shipout(NULL,Prefix,Format,0.0,false,View);
+ delete Image;
+ delete[] data;
+ }
+ } catch(handled_error) {
+ } catch(std::bad_alloc&) {
+ outOfMemory();
+ }
+ setProjection();
+}
+
+void LockedExport()
+{
+ if(interact::interactive)
+ lock();
+ Export();
+ if(interact::interactive)
+ unlock();
+}
+
+void windowposition(int& x, int& y, int width=Width, int height=Height)
+{
+ pair z=getSetting<pair>("position");
+ x=(int) z.getx();
+ y=(int) z.gety();
+ if(x < 0) {
+ x += screenWidth-width;
+ if(x < 0) x=0;
+ }
+ if(y < 0) {
+ y += screenHeight-height;
+ if(y < 0) y=0;
+ }
+}
+
+void setsize(int w, int h, int minsize=0)
+{
+ int x,y;
+
+ if(minsize) {
+ if(w < minsize) {
+ h=(int) (h*(double) minsize/w+0.5);
+ w=minsize;
+ }
+
+ if(h < minsize) {
+ w=(int) (w*(double) minsize/h+0.5);
+ h=minsize;
+ }
+ }
+
+ capsize(w,h);
+ windowposition(x,y,w,h);
+ glutPositionWindow(x,y);
+ glutReshapeWindow(w,h);
+ reshape0(w,h);
+ glutPostRedisplay();
+}
+
+void fullscreen()
+{
+ Width=screenWidth;
+ Height=screenHeight;
+#ifdef __CYGWIN__
+ glutFullScreen();
+#else
+ glutPositionWindow(0,0);
+ glutReshapeWindow(Width,Height);
+ reshape0(Width,Height);
+ glutPostRedisplay();
+#endif
+}
+
+void fitscreen()
+{
+ switch(Fitscreen) {
+ case 0: // Original size
+ {
+ setsize(oldWidth,oldHeight,minimumsize);
+ break;
+ }
+ case 1: // Fit to screen in one dimension
+ {
+ oldWidth=Width;
+ oldHeight=Height;
+ int w=screenWidth;
+ int h=screenHeight;
+ if(w >= h*Aspect) w=(int) (h*Aspect+0.5);
+ else h=(int) (w/Aspect+0.5);
+ setsize(w,h,minimumsize);
+ break;
+ }
+ case 2: // Full screen
+ {
+ fullscreen();
+ break;
+ }
+ }
+}
+
+void togglefitscreen()
+{
+ ++Fitscreen;
+ if(Fitscreen > 2) Fitscreen=0;
+ fitscreen();
+}
+
+void updateHandler(int)
+{
+ initlighting();
+ update();
+ if(interact::interactive) {
+ glutShowWindow();
+ glutShowWindow(); // Call twice to work around apparent freeglut bug.
+ } else if(glthread) fitscreen();
+}
+
+void autoExport()
+{
+ if(!Iconify)
+ glutShowWindow();
+ Export();
+ if(!Iconify)
+ glutHideWindow();
+}
+
+void exportHandler(int)
+{
+#ifdef HAVE_LIBPTHREAD
+ if(glthread)
+ wait(readySignal,readyLock);
+#endif
+ initlighting();
+ autoExport();
+}
+
+void reshape(int width, int height)
+{
+ if(glthread) {
+ static bool initialize=true;
+ if(initialize) {
+ initialize=false;
+ signal(SIGUSR1,updateHandler);
+ signal(SIGUSR2,exportHandler);
+ unlock();
+ }
+ }
+
+ if(capsize(width,height))
+ glutReshapeWindow(width,height);
+
+ reshape0(width,height);
+}
+
+#ifdef HAVE_LIBPTHREAD
+void endwait(pthread_cond_t& signal, pthread_mutex_t& lock)
+{
+ pthread_cond_signal(&signal);
+}
+void wait(pthread_cond_t& signal, pthread_mutex_t& lock)
+{
+ pthread_mutex_lock(&lock);
+ pthread_cond_signal(&signal);
+ pthread_cond_wait(&signal,&lock);
+ endwait(signal,lock);
+ pthread_mutex_unlock(&lock);
+}
+
+#endif
+
+void quit()
+{
+ if(glthread) {
+ if(interact::interactive) {
+ glutHideWindow();
+ } else {
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ glutSwapBuffers();
+ }
+#ifdef HAVE_LIBPTHREAD
+ if(!interact::interactive) {
+ pthread_mutex_lock(&quitLock);
+ pthread_cond_signal(&quitSignal);
+ pthread_mutex_unlock(&quitLock);
+ }
+#endif
+ } else {
+ glutDestroyWindow(window);
+ exit(0);
+ }
+}
+
+void display()
+{
+ if(interact::interactive)
+ lock();
+ drawscene(Width,Height);
+ glutSwapBuffers();
+ if(queueExport) {
+ Export();
+ queueExport=false;
+ }
+ if(interact::interactive)
+ unlock();
+ if(!glthread) {
+ if(Oldpid != 0 && waitpid(Oldpid,NULL,WNOHANG) != Oldpid) {
+ kill(Oldpid,SIGHUP);
+ Oldpid=0;
+ }
+ }
+}
+
+void move(int x, int y)
+{
+ if(x > 0 && y > 0) {
+ X += (x-x0)*Zoom;
+ Y += (y0-y)*Zoom;
+ x0=x; y0=y;
+ update();
+ }
+}
+
+void capzoom()
+{
+ static double maxzoom=sqrt(DBL_MAX);
+ static double minzoom=1/maxzoom;
+ if(Zoom <= minzoom) Zoom=minzoom;
+ if(Zoom >= maxzoom) Zoom=maxzoom;
+
+}
+
+void disableMenu()
+{
+ glutDetachMenu(GLUT_RIGHT_BUTTON);
+ Menu=false;
+}
+
+void zoom(int x, int y)
+{
+ if(ignorezoom) {ignorezoom=false; y0=y; return;}
+ if(x > 0 && y > 0) {
+ if(Menu) {
+ disableMenu();
+ y0=y;
+ return;
+ }
+ Motion=true;
+ static const double limit=log(0.1*DBL_MAX)/log(zoomFactor);
+ lastzoom=Zoom;
+ double s=zoomFactorStep*(y-y0);
+ if(fabs(s) < limit) {
+ Zoom *= pow(zoomFactor,s);
+ capzoom();
+ y0=y;
+ setProjection();
+ glutPostRedisplay();
+ }
+ }
+}
+
+void mousewheel(int wheel, int direction, int x, int y)
+{
+ lastzoom=Zoom;
+ if(direction > 0)
+ Zoom /= zoomFactor;
+ else
+ Zoom *= zoomFactor;
+
+ capzoom();
+ setProjection();
+ glutPostRedisplay();
+}
+
+void rotate(int x, int y)
+{
+ if(x > 0 && y > 0) {
+ if(Menu) {
+ disableMenu();
+ arcball.mouse_down(x,Height-y);
+ return;
+ }
+ Motion=true;
+ arcball.mouse_motion(x,Height-y,0,
+ mod == GLUT_ACTIVE_SHIFT, // X rotation only
+ mod == GLUT_ACTIVE_CTRL); // Y rotation only
+
+ for(int i=0; i < 4; ++i) {
+ const vec4& roti=arcball.rot[i];
+ int i4=4*i;
+ for(int j=0; j < 4; ++j)
+ Rotate[i4+j]=roti[j];
+ }
+
+ update();
+ }
+}
+
+double Degrees(int x, int y)
+{
+ return atan2(0.5*Height-y-Y,x-0.5*Width-X)*degrees;
+}
+
+void updateArcball()
+{
+ for(int i=0; i < 4; ++i) {
+ int i4=4*i;
+ vec4& roti=arcball.rot[i];
+ for(int j=0; j < 4; ++j)
+ roti[j]=Rotate[i4+j];
+ }
+ update();
+}
+
+void rotateX(double step)
+{
+ glLoadIdentity();
+ glRotatef(step,1,0,0);
+ glMultMatrixf(Rotate);
+ glGetFloatv(GL_MODELVIEW_MATRIX,Rotate);
+ updateArcball();
+}
+
+void rotateY(double step)
+{
+ glLoadIdentity();
+ glRotatef(step,0,1,0);
+ glMultMatrixf(Rotate);
+ glGetFloatv(GL_MODELVIEW_MATRIX,Rotate);
+ updateArcball();
+}
+
+void rotateZ(double step)
+{
+ glLoadIdentity();
+ glRotatef(step,0,0,1);
+ glMultMatrixf(Rotate);
+ glGetFloatv(GL_MODELVIEW_MATRIX,Rotate);
+ updateArcball();
+}
+
+void rotateZ(int x, int y)
+{
+ if(x > 0 && y > 0) {
+ if(Menu) {
+ disableMenu();
+ x=x0; y=y0;
+ return;
+ }
+ Motion=true;
+ double angle=Degrees(x,y);
+ rotateZ(angle-lastangle);
+ lastangle=angle;
+ }
+}
+
+#ifndef GLUT_WHEEL_UP
+#define GLUT_WHEEL_UP 3
+#endif
+
+#ifndef GLUT_WHEEL_DOWN
+#define GLUT_WHEEL_DOWN 4
+#endif
+
+// Mouse bindings.
+// LEFT: rotate
+// SHIFT LEFT: zoom
+// CTRL LEFT: shift
+// MIDDLE: menu
+// RIGHT: zoom
+// SHIFT RIGHT: rotateX
+// CTRL RIGHT: rotateY
+// ALT RIGHT: rotateZ
+void mouse(int button, int state, int x, int y)
+{
+ if(button == GLUT_WHEEL_UP) {
+ mousewheel(0,1,x,y);
+ return;
+ }
+ if(button == GLUT_WHEEL_DOWN) {
+ mousewheel(0,-1,x,y);
+ return;
+ }
+
+ mod=glutGetModifiers();
+
+ if(button == GLUT_RIGHT_BUTTON) {
+ if(state == GLUT_UP && !Motion) {
+ glutAttachMenu(GLUT_RIGHT_BUTTON);
+ Menu=true;
+ return;
+ }
+ }
+
+ if(Menu) disableMenu();
+ else Motion=false;
+
+ if(state == GLUT_DOWN) {
+ if(button == GLUT_LEFT_BUTTON && mod == GLUT_ACTIVE_CTRL) {
+ x0=x; y0=y;
+ glutMotionFunc(move);
+ return;
+ }
+ if((button == GLUT_LEFT_BUTTON && mod == GLUT_ACTIVE_SHIFT) ||
+ (button == GLUT_RIGHT_BUTTON && mod == 0)) {
+ y0=y;
+ glutMotionFunc(zoom);
+ return;
+ }
+ if(button == GLUT_RIGHT_BUTTON && mod == GLUT_ACTIVE_ALT) {
+ lastangle=Degrees(x,y);
+ glutMotionFunc(rotateZ);
+ return;
+ }
+ arcball.mouse_down(x,Height-y);
+ glutMotionFunc(rotate);
+ } else
+ arcball.mouse_up();
+}
+
+timeval lasttime;
+
+double spinstep()
+{
+ timeval tv;
+ gettimeofday(&tv,NULL);
+ double step=spinStep*(tv.tv_sec-lasttime.tv_sec+
+ ((double) tv.tv_usec-lasttime.tv_usec)/1000000.0);
+ lasttime=tv;
+ return step;
+}
+
+void xspin()
+{
+ rotateX(spinstep());
+}
+
+void yspin()
+{
+ rotateY(spinstep());
+}
+
+void zspin()
+{
+ rotateZ(spinstep());
+}
+
+void initTimer()
+{
+ gettimeofday(&lasttime,NULL);
+}
+
+void expand()
+{
+ setsize((int) (Width*resizeStep+0.5),(int) (Height*resizeStep+0.5));
+}
+
+void shrink()
+{
+ setsize(max((int) (Width/resizeStep+0.5),1),
+ max((int) (Height/resizeStep+0.5),1));
+}
+
+void idleFunc(void (*f)())
+{
+ initTimer();
+ glutIdleFunc(f);
+}
+
+void mode()
+{
+ switch(Mode) {
+ case 0:
+ for(size_t i=0; i < Nlights; ++i)
+ glEnable(GL_LIGHT0+i);
+ glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
+ gluNurbsProperty(nurb,GLU_DISPLAY_MODE,GLU_FILL);
+ ++Mode;
+ break;
+ case 1:
+ for(size_t i=0; i < Nlights; ++i)
+ glDisable(GL_LIGHT0+i);
+ gluNurbsProperty(nurb,GLU_DISPLAY_MODE,GLU_OUTLINE_POLYGON);
+ glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
+ ++Mode;
+ break;
+ case 2:
+ gluNurbsProperty(nurb,GLU_DISPLAY_MODE,GLU_OUTLINE_PATCH);
+ Mode=0;
+ break;
+ }
+ glutPostRedisplay();
+}
+
+void idle()
+{
+ glutIdleFunc(NULL);
+ Xspin=Yspin=Zspin=false;
+}
+
+void spinx()
+{
+ if(Xspin)
+ idle();
+ else {
+ idleFunc(xspin);
+ Xspin=true;
+ Yspin=Zspin=false;
+ }
+}
+
+void spiny()
+{
+ if(Yspin)
+ idle();
+ else {
+ idleFunc(yspin);
+ Yspin=true;
+ Xspin=Zspin=false;
+ }
+}
+
+void spinz()
+{
+ if(Zspin)
+ idle();
+ else {
+ idleFunc(zspin);
+ Zspin=true;
+ Xspin=Yspin=false;
+ }
+}
+
+void home()
+{
+ idle();
+ X=Y=0.0;
+ arcball.init();
+ glLoadIdentity();
+ glGetFloatv(GL_MODELVIEW_MATRIX,Rotate);
+ lastzoom=Zoom=1.0;
+}
+
+void write(const char *text, const double *v)
+{
+ cout << text << "=(" << v[0] << "," << v[1] << "," << v[2] << ")";
+}
+
+void camera()
+{
+ double cz=0.5*(zmin+zmax);
+ camp::Triple vCamera,vTarget,vUp;
+
+ for(int i=0; i < 3; ++i) {
+ double sumCamera=0.0, sumTarget=0.0, sumUp=0.0;
+ for(int j=0; j < 4; ++j) {
+ sumCamera += T[4*i+j]*(Rotate[4*j+3]-cz*Rotate[4*j+2]);
+ sumUp += T[4*i+j]*(Rotate[4*j+3]+Rotate[4*j+1]);
+ sumTarget += T[4*i+j]*Rotate[4*j+3];
+ }
+ vCamera[i]=sumCamera;
+ vUp[i]=sumUp-sumTarget;
+ vTarget[i]=sumTarget;
+ }
+
+ triple Camera=triple(vCamera);
+ triple Up=triple(vUp);
+ triple Target=triple(vTarget);
+
+ cout << "camera=" << Camera << "," << endl
+ << "up=" << Up << "," << endl
+ << "target=" << Target << endl << endl;
+}
+
+void keyboard(unsigned char key, int x, int y)
+{
+ switch(key) {
+ case 'h':
+ home();
+ update();
+ break;
+ case 'f':
+ togglefitscreen();
+ break;
+ case 'x':
+ spinx();
+ break;
+ case 'y':
+ spiny();
+ break;
+ case 'z':
+ spinz();
+ break;
+ case 's':
+ idle();
+ break;
+ case 'm':
+ mode();
+ break;
+ case 'e':
+ LockedExport();
+ break;
+ case 'c':
+ camera();
+ break;
+ case '+':
+ case '=':
+ expand();
+ break;
+ case '-':
+ case '_':
+ shrink();
+ break;
+ case 17: // Ctrl-q
+ case 'q':
+ if(!Format.empty()) LockedExport();
+ quit();
+ break;
+ }
+}
+
+enum Menu {HOME,FITSCREEN,XSPIN,YSPIN,ZSPIN,STOP,MODE,EXPORT,CAMERA,QUIT};
+
+void menu(int choice)
+{
+ disableMenu();
+ ignorezoom=true;
+ Motion=true;
+ switch (choice) {
+ case HOME: // Home
+ home();
+ update();
+ break;
+ case FITSCREEN:
+ togglefitscreen();
+ break;
+ case XSPIN:
+ spinx();
+ break;
+ case YSPIN:
+ spiny();
+ break;
+ case ZSPIN:
+ spinz();
+ break;
+ case STOP:
+ idle();
+ break;
+ case MODE:
+ mode();
+ break;
+ case EXPORT:
+ queueExport=true;
+ break;
+ case CAMERA:
+ camera();
+ break;
+ case QUIT:
+ quit();
+ break;
+ }
+}
+
+void setosize()
+{
+ oldWidth=(int) ceil(oWidth);
+ oldHeight=(int) ceil(oHeight);
+}
+
+void init()
+{
+ string options=string(settings::argv0)+" ";
+ if(Iconify)
+ options += "-iconic ";
+ options += getSetting<string>("glOptions");
+ char **argv=args(options.c_str(),true);
+ int argc=0;
+ while(argv[argc] != NULL)
+ ++argc;
+
+ glutInit(&argc,argv);
+
+ screenWidth=glutGet(GLUT_SCREEN_WIDTH);
+ screenHeight=glutGet(GLUT_SCREEN_HEIGHT);
+}
+
+// angle=0 means orthographic.
+void glrender(const string& prefix, const picture *pic, const string& format,
+ double width, double height,
+ double angle, const triple& m, const triple& M, double *t,
+ size_t nlights, triple *lights, double *diffuse,
+ double *ambient, double *specular, bool Viewportlighting,
+ bool view, int oldpid)
+{
+#ifndef __CYGWIN__
+ Iconify=getSetting<bool>("iconify");
+#endif
+
+ width=max(width,1.0);
+ height=max(height,1.0);
+
+ static bool initializedView=false;
+
+ if(view || initializedView) lock();
+
+ Prefix=prefix;
+ Picture=pic;
+ Format=format;
+ T=t;
+ Nlights=min(nlights,(size_t) GL_MAX_LIGHTS);
+ Lights=lights;
+ Diffuse=diffuse;
+ Ambient=ambient;
+ Specular=specular;
+ ViewportLighting=Viewportlighting;
+ View=view;
+ Oldpid=oldpid;
+
+ static bool initialized=false;
+
+ if(!initialized) {
+ init();
+ Fitscreen=1;
+ }
+
+ Xmin=m.getx();
+ Xmax=M.getx();
+ Ymin=m.gety();
+ Ymax=M.gety();
+ zmin=m.getz();
+ zmax=M.getz();
+ H=angle != 0.0 ? -tan(0.5*angle*radians)*zmax : 0.0;
+
+ Menu=false;
+ Motion=true;
+ ignorezoom=false;
+ Mode=0;
+
+ if(!initialized || !interact::interactive) {
+ antialias=getSetting<Int>("antialias") > 1;
+ double expand=getSetting<double>("render");
+ if(expand < 0)
+ expand *= (Format.empty() || Format == "eps" || Format == "pdf")
+ ? -2.0 : -1.0;
+ if(antialias) expand *= 2.0;
+
+ // Force a hard viewport limit to work around direct rendering bugs.
+ // Alternatively, one can use -glOptions=-indirect (with a performance
+ // penalty).
+ pair maxViewport=getSetting<pair>("maxviewport");
+ maxWidth=(int) ceil(maxViewport.getx());
+ maxHeight=(int) ceil(maxViewport.gety());
+ if(maxWidth <= 0) maxWidth=max(maxHeight,2);
+ if(maxHeight <= 0) maxHeight=max(maxWidth,2);
+ if(screenWidth <= 0) screenWidth=maxWidth;
+ if(screenHeight <= 0) screenHeight=maxHeight;
+
+ oWidth=width;
+ oHeight=height;
+ Aspect=width/height;
+
+ fullWidth=(int) ceil(expand*width);
+ fullHeight=(int) ceil(expand*height);
+
+ Width=min(fullWidth,screenWidth);
+ Height=min(fullHeight,screenHeight);
+
+ if(Width > Height*Aspect)
+ Width=min((int) (ceil(Height*Aspect)),screenWidth);
+ else
+ Height=min((int) (ceil(Width/Aspect)),screenHeight);
+
+ Aspect=((double) Width)/Height;
+
+ pair maxtile=getSetting<pair>("maxtile");
+ maxTileWidth=(int) maxtile.getx();
+ maxTileHeight=(int) maxtile.gety();
+ if(maxTileWidth <= 0) maxTileWidth=screenWidth;
+ if(maxTileHeight <= 0) maxTileHeight=screenHeight;
+
+ setosize();
+
+ if(View && settings::verbose > 1)
+ cout << "Rendering " << prefix << " as " << Width << "x" << Height
+ << " image" << endl;
+ }
+
+#ifdef HAVE_LIBPTHREAD
+ if(initializedView && glthread) {
+ if(View)
+ pthread_kill(mainthread,SIGUSR1);
+ else
+ pthread_kill(mainthread,SIGUSR2);
+ unlock();
+ return;
+ }
+#endif
+
+ unsigned int displaymode=GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH;
+
+ bool havewindow=initialized && glthread;
+
+ if(View) {
+ int x,y;
+ if(havewindow)
+ glutDestroyWindow(window);
+
+ windowposition(x,y);
+ glutInitWindowPosition(x,y);
+ glutInitWindowSize(1,1);
+ Int multisample=getSetting<Int>("multisample");
+ if(multisample <= 1) multisample=0;
+ if(multisample)
+ displaymode |= GLUT_MULTISAMPLE;
+ glutInitDisplayMode(displaymode);
+
+ ostringstream buf;
+ int samples;
+#ifdef FREEGLUT
+#ifdef GLUT_INIT_MAJOR_VERSION
+ while(true) {
+ if(multisample > 0)
+ glutSetOption(GLUT_MULTISAMPLE,multisample);
+#endif
+#endif
+ string title=string(settings::PROGRAM)+": "+prefix+
+ " [Double click right button for menu]";
+ window=glutCreateWindow(title.c_str());
+ GLint samplebuf[1];
+ glGetIntegerv(GL_SAMPLES,samplebuf);
+ samples=samplebuf[0];
+#ifdef FREEGLUT
+#ifdef GLUT_INIT_MAJOR_VERSION
+ if(samples < multisample) {
+ --multisample;
+ if(multisample > 1) {
+ glutDestroyWindow(window);
+ continue;
+ }
+ }
+ break;
+ }
+#endif
+#endif
+ if(samples > 1) {
+ if(settings::verbose > 1 && samples > 1)
+ cout << "Multisampling enabled with sample width " << samples << endl;
+ }
+ glutShowWindow();
+ } else if(!havewindow) {
+ glutInitWindowSize(maxTileWidth,maxTileHeight);
+ glutInitDisplayMode(displaymode);
+ window=glutCreateWindow("");
+ glutHideWindow();
+ }
+
+ initialized=true;
+
+ glClearColor(1.0,1.0,1.0,1.0);
+
+ glMatrixMode(GL_MODELVIEW);
+
+ home();
+
+ if(View) {
+ if(!getSetting<bool>("fitscreen"))
+ Fitscreen=0;
+ fitscreen();
+ setosize();
+ }
+
+ glEnable(GL_DEPTH_TEST);
+ glEnable(GL_MAP1_VERTEX_3);
+ glEnable(GL_MAP2_VERTEX_3);
+ glEnable(GL_MAP2_COLOR_4);
+
+ glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
+
+ nurb=gluNewNurbsRenderer();
+ if(nurb == NULL)
+ outOfMemory();
+ gluNurbsProperty(nurb,GLU_SAMPLING_METHOD,GLU_PARAMETRIC_ERROR);
+ gluNurbsProperty(nurb,GLU_SAMPLING_TOLERANCE,0.5);
+ gluNurbsProperty(nurb,GLU_PARAMETRIC_TOLERANCE,1.0);
+ gluNurbsProperty(nurb,GLU_CULLING,GLU_TRUE);
+
+ // The callback tesselation algorithm avoids artifacts at degenerate control
+ // points.
+ gluNurbsProperty(nurb,GLU_NURBS_MODE,GLU_NURBS_TESSELLATOR);
+ gluNurbsCallback(nurb,GLU_NURBS_BEGIN,(_GLUfuncptr) glBegin);
+ gluNurbsCallback(nurb,GLU_NURBS_VERTEX,(_GLUfuncptr) glVertex3fv);
+ gluNurbsCallback(nurb,GLU_NURBS_END,(_GLUfuncptr) glEnd);
+ gluNurbsCallback(nurb,GLU_NURBS_COLOR,(_GLUfuncptr) glColor4fv);
+ mode();
+
+ initlighting();
+
+ if(View) {
+ initializedView=true;
+ glutReshapeFunc(reshape);
+ glutDisplayFunc(display);
+ glutKeyboardFunc(keyboard);
+ glutMouseFunc(mouse);
+
+ glutCreateMenu(menu);
+ glutAddMenuEntry("(h) Home",HOME);
+ glutAddMenuEntry("(f) Fitscreen",FITSCREEN);
+ glutAddMenuEntry("(x) X spin",XSPIN);
+ glutAddMenuEntry("(y) Y spin",YSPIN);
+ glutAddMenuEntry("(z) Z spin",ZSPIN);
+ glutAddMenuEntry("(s) Stop",STOP);
+ glutAddMenuEntry("(m) Mode",MODE);
+ glutAddMenuEntry("(e) Export",EXPORT);
+ glutAddMenuEntry("(c) Camera",CAMERA);
+ glutAddMenuEntry("(q) Quit" ,QUIT);
+
+ glutAttachMenu(GLUT_MIDDLE_BUTTON);
+
+ glutMainLoop();
+ } else {
+ if(glthread) {
+ exportHandler(0);
+ } else {
+ autoExport();
+ quit();
+ }
+ }
+}
+
+} // namespace gl
+
+#endif