summaryrefslogtreecommitdiff
path: root/graphics/asymptote/glrender.cc
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
committerNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
commite0c6872cf40896c7be36b11dcc744620f10adf1d (patch)
tree60335e10d2f4354b0674ec22d7b53f0f8abee672 /graphics/asymptote/glrender.cc
Initial commit
Diffstat (limited to 'graphics/asymptote/glrender.cc')
-rw-r--r--graphics/asymptote/glrender.cc1789
1 files changed, 1789 insertions, 0 deletions
diff --git a/graphics/asymptote/glrender.cc b/graphics/asymptote/glrender.cc
new file mode 100644
index 0000000000..d864ed34de
--- /dev/null
+++ b/graphics/asymptote/glrender.cc
@@ -0,0 +1,1789 @@
+/*****
+ * glrender.cc
+ * John Bowman and Orest Shardt
+ * Render 3D Bezier paths and surfaces.
+ *****/
+
+#ifdef __CYGWIN__
+#define _POSIX_C_SOURCE 200809L
+#endif
+
+#include <stdlib.h>
+#include <fstream>
+#include <cstring>
+#include <sys/time.h>
+
+
+#include "common.h"
+#include "locate.h"
+#include "seconds.h"
+#include "statistics.h"
+#include "bezierpatch.h"
+#include "beziercurve.h"
+
+#ifdef HAVE_GL
+
+#ifdef HAVE_LIBGLUT
+#ifdef __MSDOS__
+#ifndef FGAPI
+#define FGAPI GLUTAPI
+#endif
+#ifndef FGAPIENTRY
+#define FGAPIENTRY APIENTRY
+#endif
+#endif
+
+#define GLUT_BUILDING_LIB
+#endif // HAVE_LIBGLUT
+
+#include "picture.h"
+#include "arcball.h"
+#include "bbox3.h"
+#include "drawimage.h"
+#include "interact.h"
+#include "tr.h"
+
+#ifdef HAVE_LIBGLUT
+#ifdef FREEGLUT
+#include <GL/freeglut_ext.h>
+#endif
+#endif
+
+#include <glm/glm.hpp>
+#include <glm/gtc/matrix_transform.hpp>
+#include <glm/gtc/type_ptr.hpp>
+
+#include "shaders.h"
+#include "material.h"
+
+#ifdef HAVE_LIBOPENIMAGEIO
+#include <OpenImageIO/imageio.h>
+#endif
+
+using settings::locateFile;
+using camp::Material;
+using camp::Maxmaterials;
+using camp::Nmaterials;
+using camp::nmaterials;
+using utils::seconds;
+
+namespace camp {
+billboard BB;
+GLint materialShader;
+GLint colorShader;
+GLint noNormalShader;
+GLint pixelShader;
+
+size_t Maxmaterials;
+size_t Nmaterials=1;
+size_t nmaterials=48;
+}
+namespace gl {
+
+bool outlinemode=false;
+bool glthread=false;
+bool initialize=true;
+
+GLint Maxvertices;
+Int maxvertices;
+
+using camp::picture;
+using camp::drawRawImage;
+using camp::transform;
+using camp::pair;
+using camp::triple;
+using vm::array;
+using vm::read;
+using camp::bbox3;
+using settings::getSetting;
+using settings::Setting;
+
+bool Iconify=false;
+bool ignorezoom;
+int Fitscreen;
+
+bool queueExport=false;
+bool readyAfterExport=false;
+
+#ifdef HAVE_LIBGLUT
+timeval lasttime;
+timeval lastframetime;
+int oldWidth,oldHeight;
+
+bool Xspin,Yspin,Zspin;
+bool Animate;
+bool Step;
+bool remesh;
+bool forceRemesh=false;
+
+bool queueScreen=false;
+
+int x0,y0;
+string Action;
+
+double lastangle;
+Arcball arcball;
+int window;
+#endif
+
+double Aspect;
+bool View;
+int Oldpid;
+string Prefix;
+const picture* Picture;
+string Format;
+int Width,Height;
+int fullWidth,fullHeight;
+double oWidth,oHeight;
+int screenWidth,screenHeight;
+int maxWidth;
+int maxHeight;
+int maxTileWidth;
+int maxTileHeight;
+
+double T[16];
+
+int Mode;
+
+double Angle;
+bool orthographic;
+double H;
+double xmin,xmax;
+double ymin,ymax;
+double zmin,zmax;
+
+double Xmin,Xmax;
+double Ymin,Ymax;
+
+pair Shift;
+double X,Y;
+double cx,cy;
+double Xfactor,Yfactor;
+
+static const double pi=acos(-1.0);
+static const double degrees=180.0/pi;
+static const double radians=1.0/degrees;
+
+double Background[4];
+size_t Nlights=1; // Maximum number of lights compiled in shader
+size_t nlights; // Actual number of lights
+size_t nlights0;
+triple *Lights;
+double *Diffuse;
+double *Specular;
+bool antialias;
+
+double Zoom;
+double Zoom0;
+double lastzoom;
+
+using glm::dvec3;
+using glm::mat4;
+using glm::dmat4;
+using glm::value_ptr;
+using glm::translate;
+
+mat4 projViewMat;
+mat4 viewMat;
+mat4 normMat;
+
+dmat4 dprojMat;
+dmat4 dviewMat;
+dmat4 drotateMat;
+
+using utils::statistics;
+statistics S;
+
+ModelView modelView;
+GLuint ubo;
+
+#ifdef HAVE_LIBOPENIMAGEIO
+GLuint envMapBuf;
+
+GLuint initHDR() {
+ GLuint tex;
+ glGenTextures(1, &tex);
+
+ auto imagein = OIIO::ImageInput::open(locateFile("res/studio006.hdr").c_str());
+ OIIO::ImageSpec const& imspec = imagein->spec();
+
+ // uses GL_TEXTURE1 for now.
+ glActiveTexture(GL_TEXTURE1);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+ glBindTexture(GL_TEXTURE_2D, tex);
+ std::vector<float> pixels(imspec.width*imspec.height*3);
+ imagein->read_image(pixels.data());
+
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, imspec.width, imspec.height, 0,
+ GL_RGB, GL_FLOAT, pixels.data());
+
+ glGenerateMipmap(GL_TEXTURE_2D);
+ imagein->close();
+
+ glActiveTexture(GL_TEXTURE0);
+ return tex;
+}
+
+#endif
+void updateModelViewData()
+{
+ // Like Fortran, OpenGL uses transposed (column-major) format!
+ dmat4 MV=glm::transpose(dviewMat);
+ dmat4 MVinv=glm::inverse(MV);
+
+ normMat=mat4(MVinv);
+
+ double* T=value_ptr(MV);
+ double* Tinv=value_ptr(MVinv);
+
+ for(int j=0; j < 16; ++j) {
+ modelView.T[j]=T[j];
+ modelView.Tinv[j]=Tinv[j];
+ }
+}
+
+
+GLint shaderProg,shaderProgColor;
+
+double *Rotate;
+void *glrenderWrapper(void *a);
+
+#ifdef HAVE_LIBOSMESA
+OSMesaContext ctx;
+unsigned char *osmesa_buffer;
+#endif
+
+#ifdef HAVE_PTHREAD
+pthread_t mainthread;
+
+pthread_cond_t initSignal=PTHREAD_COND_INITIALIZER;
+pthread_mutex_t initLock=PTHREAD_MUTEX_INITIALIZER;
+
+pthread_cond_t readySignal=PTHREAD_COND_INITIALIZER;
+pthread_mutex_t readyLock=PTHREAD_MUTEX_INITIALIZER;
+
+void endwait(pthread_cond_t& signal, pthread_mutex_t& lock)
+{
+ pthread_mutex_lock(&lock);
+ pthread_cond_signal(&signal);
+ pthread_mutex_unlock(&lock);
+}
+void wait(pthread_cond_t& signal, pthread_mutex_t& lock)
+{
+ pthread_mutex_lock(&lock);
+ pthread_cond_signal(&signal);
+ pthread_cond_wait(&signal,&lock);
+ pthread_mutex_unlock(&lock);
+}
+#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;
+}
+
+glm::vec4 vec4(triple v)
+{
+ return glm::vec4(v.getx(),v.gety(),v.getz(),0);
+}
+
+glm::vec4 vec4(double *v)
+{
+ return glm::vec4(v[0],v[1],v[2],v[3]);
+}
+
+void setDimensions(int Width, int Height, double X, double Y)
+{
+ double Aspect=((double) Width)/Height;
+ double xshift=X/Width*lastzoom+Shift.getx()*Xfactor;
+ double yshift=Y/Height*lastzoom+Shift.gety()*Yfactor;
+ double Zoominv=1.0/lastzoom;
+ if(orthographic) {
+ double xsize=Xmax-Xmin;
+ double ysize=Ymax-Ymin;
+ if(xsize < ysize*Aspect) {
+ double r=0.5*ysize*Aspect*Zoominv;
+ double X0=2.0*r*xshift;
+ double Y0=(Ymax-Ymin)*Zoominv*yshift;
+ xmin=-r-X0;
+ xmax=r-X0;
+ ymin=Ymin*Zoominv-Y0;
+ ymax=Ymax*Zoominv-Y0;
+ } else {
+ double r=0.5*xsize/(Aspect*Zoom);
+ double X0=(Xmax-Xmin)*Zoominv*xshift;
+ double Y0=2.0*r*yshift;
+ xmin=Xmin*Zoominv-X0;
+ xmax=Xmax*Zoominv-X0;
+ ymin=-r-Y0;
+ ymax=r-Y0;
+ }
+ } else {
+ double r=H*Zoominv;
+ double rAspect=r*Aspect;
+ double X0=2.0*rAspect*xshift;
+ double Y0=2.0*r*yshift;
+ xmin=-rAspect-X0;
+ xmax=rAspect-X0;
+ ymin=-r-Y0;
+ ymax=r-Y0;
+ }
+}
+
+void frustum(GLdouble left, GLdouble right, GLdouble bottom,
+ GLdouble top, GLdouble nearVal, GLdouble farVal)
+{
+ dprojMat=glm::frustum(left,right,bottom,top,nearVal,farVal);
+ projViewMat=mat4(dprojMat*dviewMat);
+}
+
+void ortho(GLdouble left, GLdouble right, GLdouble bottom,
+ GLdouble top, GLdouble nearVal, GLdouble farVal)
+{
+ dprojMat=glm::ortho(left,right,bottom,top,nearVal,farVal);
+ projViewMat=mat4(dprojMat*dviewMat);
+}
+
+void setProjection()
+{
+ setDimensions(Width,Height,X,Y);
+ if(orthographic) ortho(xmin,xmax,ymin,ymax,-zmax,-zmin);
+ else frustum(xmin,xmax,ymin,ymax,-zmax,-zmin);
+
+#ifdef HAVE_LIBGLUT
+ double arcballRadius=getSetting<double>("arcballradius");
+ arcball.set_params(vec2(0.5*Width,0.5*Height),arcballRadius*Zoom);
+#endif
+}
+
+void drawscene(int Width, int Height)
+{
+#ifdef HAVE_PTHREAD
+ static bool first=true;
+ if(glthread && first && !getSetting<bool>("offscreen")) {
+ wait(initSignal,initLock);
+ endwait(initSignal,initLock);
+ first=false;
+ }
+#endif
+
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ triple m(xmin,ymin,zmin);
+ triple M(xmax,ymax,zmax);
+ double perspective=orthographic ? 0.0 : 1.0/zmax;
+
+ double size2=hypot(Width,Height);
+
+ if(remesh) {
+ camp::BezierPatch::clear();
+ camp::BezierPatch::Clear();
+ camp::BezierPatch::tClear();
+ camp::BezierCurve::clear();
+ camp::Pixel::clear();
+ }
+
+ // Render opaque objects
+ Picture->render(size2,m,M,perspective,false,remesh);
+
+ // Enable transparency
+ glDepthMask(GL_FALSE);
+
+ // Render transparent objects
+ Picture->render(size2,m,M,perspective,true,remesh);
+ glDepthMask(GL_TRUE);
+}
+
+// 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 border=1;
+ 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,border);
+ trImageSize(tr,fullWidth,fullHeight);
+ trImageBuffer(tr,GL_RGB,GL_UNSIGNED_BYTE,data);
+
+ setDimensions(fullWidth,fullHeight,X/Width*fullWidth,Y/Width*fullWidth);
+ (orthographic ? trOrtho : 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.
+ drawRawImage *Image=new drawRawImage(data,fullWidth,fullHeight,
+ transform(0.0,0.0,w,0.0,0.0,h),
+ antialias);
+ pic.append(Image);
+ pic.shipout(NULL,Prefix,Format,false,View);
+ delete Image;
+ delete[] data;
+ }
+ } catch(handled_error) {
+ } catch(std::bad_alloc&) {
+ outOfMemory();
+ }
+ setProjection();
+ bool offscreen=getSetting<bool>("offscreen");
+#ifdef HAVE_LIBGLUT
+ if(!offscreen)
+ glutPostRedisplay();
+#endif
+
+#ifdef HAVE_PTHREAD
+ if(glthread && readyAfterExport && !offscreen) {
+ readyAfterExport=false;
+ endwait(readySignal,readyLock);
+ }
+#endif
+}
+
+#ifdef HAVE_LIBGLUT
+void idle()
+{
+ glutIdleFunc(NULL);
+ Xspin=Yspin=Zspin=Animate=Step=false;
+}
+#endif
+
+void home()
+{
+ X=Y=cx=cy=0.0;
+#ifdef HAVE_LIBGLUT
+ if(!getSetting<bool>("offscreen")) {
+ idle();
+ arcball.init();
+ }
+#endif
+ viewMat=mat4(1.0f);
+ normMat=mat4(1.0f);
+
+ dviewMat=dmat4(1.0);
+ drotateMat=dmat4(1.0);
+
+ Rotate=value_ptr(drotateMat);
+ updateModelViewData();
+
+ remesh=true;
+ lastzoom=Zoom=Zoom0;
+ setDimensions(Width,Height,0,0);
+ glClearColor(Background[0],Background[1],Background[2],Background[3]);
+}
+
+void nodisplay()
+{
+}
+
+void destroywindow()
+{
+ glutDestroyWindow(glutGetWindow());
+}
+
+// Return the greatest power of 2 less than or equal to n.
+inline unsigned int floorpow2(unsigned int n)
+{
+ n |= n >> 1;
+ n |= n >> 2;
+ n |= n >> 4;
+ n |= n >> 8;
+ n |= n >> 16;
+ return n-(n >> 1);
+}
+
+void quit()
+{
+#ifdef HAVE_LIBOSMESA
+ if(getSetting<bool>("offscreen")) {
+ if(osmesa_buffer) delete[] osmesa_buffer;
+ if(ctx) OSMesaDestroyContext(ctx);
+ exit(0);
+ }
+#endif
+#ifdef HAVE_LIBGLUT
+ if(glthread) {
+ bool animating=getSetting<bool>("animating");
+ if(animating)
+ Setting("interrupt")=true;
+ home();
+ Animate=getSetting<bool>("autoplay");
+#ifdef HAVE_PTHREAD
+ if(!interact::interactive || animating) {
+ idle();
+ glutDisplayFunc(nodisplay);
+ endwait(readySignal,readyLock);
+ }
+#endif
+ if(interact::interactive)
+ glutHideWindow();
+ } else {
+ glutDestroyWindow(window);
+ exit(0);
+ }
+#endif
+}
+
+void mode()
+{
+ remesh=true;
+ switch(Mode) {
+ case 0: // wireframe -> regular
+ outlinemode=false;
+ nlights=nlights0;
+ glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
+ ++Mode;
+ break;
+ case 1: // regular -> outline
+ outlinemode=true;
+ nlights=0;
+ glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
+ ++Mode;
+ break;
+ case 2: // outline -> wireframe
+ outlinemode=false;
+ Mode=0;
+ break;
+ }
+#ifdef HAVE_LIBGLUT
+ if(!getSetting<bool>("offscreen"))
+ glutPostRedisplay();
+#endif
+}
+
+// GUI-related functions
+#ifdef HAVE_LIBGLUT
+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 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, bool reposition=true)
+{
+ int x,y;
+
+ capsize(w,h);
+ if(reposition) {
+ windowposition(x,y,w,h);
+ glutPositionWindow(x,y);
+ }
+ glutReshapeWindow(w,h);
+ reshape0(w,h);
+ glutPostRedisplay();
+}
+
+void fullscreen(bool reposition=true)
+{
+ Width=screenWidth;
+ Height=screenHeight;
+ reshape0(Width,Height);
+ if(reposition)
+ glutPositionWindow(0,0);
+ glutReshapeWindow(Width,Height);
+ glutPostRedisplay();
+}
+
+void fitscreen(bool reposition=true)
+{
+ if(Animate && Fitscreen == 2) Fitscreen=0;
+ switch(Fitscreen) {
+ case 0: // Original size
+ {
+ Xfactor=Yfactor=1.0;
+ setsize(oldWidth,oldHeight,reposition);
+ 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,reposition);
+ break;
+ }
+ case 2: // Full screen
+ {
+ Xfactor=((double) screenHeight)/Height;
+ Yfactor=((double) screenWidth)/Width;
+ fullscreen(reposition);
+ break;
+ }
+ }
+}
+
+void togglefitscreen()
+{
+ ++Fitscreen;
+ if(Fitscreen > 2) Fitscreen=0;
+ fitscreen();
+}
+
+void initTimer()
+{
+ gettimeofday(&lasttime,NULL);
+ gettimeofday(&lastframetime,NULL);
+}
+
+void idleFunc(void (*f)())
+{
+ initTimer();
+ glutIdleFunc(f);
+}
+
+void screen()
+{
+ if(glthread && !interact::interactive)
+ fitscreen(false);
+}
+
+void nextframe(int)
+{
+#ifdef HAVE_PTHREAD
+ endwait(readySignal,readyLock);
+#endif
+ double framedelay=getSetting<double>("framedelay");
+ if(framedelay > 0)
+ usleep((unsigned int) (1000.0*framedelay+0.5));
+ if(Step) Animate=false;
+}
+
+void display()
+{
+ if(queueScreen) {
+ if(!Animate) screen();
+ queueScreen=false;
+ }
+
+ maxvertices=getSetting<Int>("maxvertices");
+ if(maxvertices == 0) maxvertices=Maxvertices;
+
+ bool fps=settings::verbose > 2;
+ if(fps) seconds();
+ drawscene(Width,Height);
+ if(fps) {
+ glFinish();
+ double s=seconds();
+ if(s > 0.0) {
+ double rate=1.0/s;
+ S.add(rate);
+ cout << "FPS=" << rate << "\t" << S.mean() << " +/- " << S.stdev() << endl;
+ }
+ }
+ glutSwapBuffers();
+
+#ifdef HAVE_PTHREAD
+ if(glthread && Animate) {
+ queueExport=false;
+ double delay=1.0/getSetting<double>("framerate");
+ timeval tv;
+ gettimeofday(&tv,NULL);
+ double seconds=tv.tv_sec-lastframetime.tv_sec+
+ ((double) tv.tv_usec-lastframetime.tv_usec)/1000000.0;
+ lastframetime=tv;
+ double milliseconds=1000.0*(delay-seconds);
+ double framedelay=getSetting<double>("framedelay");
+ if(framedelay > 0) milliseconds -= framedelay;
+ if(milliseconds > 0)
+ glutTimerFunc((int) (milliseconds+0.5),nextframe,0);
+ else nextframe(0);
+ }
+#endif
+ if(queueExport) {
+ Export();
+ queueExport=false;
+ }
+ if(!glthread) {
+ if(Oldpid != 0 && waitpid(Oldpid,NULL,WNOHANG) != Oldpid) {
+ kill(Oldpid,SIGHUP);
+ Oldpid=0;
+ }
+ }
+}
+
+void update()
+{
+ glutDisplayFunc(display);
+ Animate=getSetting<bool>("autoplay");
+ glutShowWindow();
+ if(Zoom != lastzoom) remesh=true;
+ lastzoom=Zoom;
+ double cz=0.5*(zmin+zmax);
+
+ dviewMat=translate(translate(dmat4(1.0),dvec3(cx,cy,cz))*drotateMat,
+ dvec3(0,0,-cz));
+ viewMat=mat4(dviewMat);
+ setProjection();
+ updateModelViewData();
+
+ glutPostRedisplay();
+}
+
+void updateHandler(int)
+{
+ queueScreen=true;
+ update();
+ if(interact::interactive || !Animate) {
+ glutShowWindow();
+ }
+}
+
+void animate()
+{
+ Animate=!Animate;
+ if(Animate) {
+ if(Fitscreen == 2) {
+ togglefitscreen();
+ togglefitscreen();
+ }
+ update();
+ }
+}
+
+void reshape(int width, int height)
+{
+ if(glthread) {
+ static bool initialize=true;
+ if(initialize) {
+ initialize=false;
+ Signal(SIGUSR1,updateHandler);
+ }
+ }
+
+ if(capsize(width,height))
+ glutReshapeWindow(width,height);
+
+ reshape0(width,height);
+ remesh=true;
+}
+
+void shift(int x, int y)
+{
+ if(x > 0 && y > 0) {
+ double Zoominv=1.0/Zoom;
+ X += (x-x0)*Zoominv;
+ Y += (y0-y)*Zoominv;
+ x0=x; y0=y;
+ update();
+ }
+}
+
+void pan(int x, int y)
+{
+ if(x > 0 && y > 0) {
+ if(orthographic) {
+ double Zoominv=1.0/Zoom;
+ X += (x-x0)*Zoominv;
+ Y += (y0-y)*Zoominv;
+ } else {
+ cx += (x-x0)*(xmax-xmin)/Width;
+ cy += (y0-y)*(ymax-ymin)/Height;
+ }
+ 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;
+
+ if(Zoom != lastzoom) remesh=true;
+ lastzoom=Zoom;
+}
+
+void zoom(int x, int y)
+{
+ if(ignorezoom) {ignorezoom=false; y0=y; return;}
+ if(x > 0 && y > 0) {
+ double zoomFactor=getSetting<double>("zoomfactor");
+ if(zoomFactor > 0.0) {
+ double zoomStep=getSetting<double>("zoomstep");
+ const double limit=log(0.1*DBL_MAX)/log(zoomFactor);
+ double s=zoomStep*(y0-y);
+ if(fabs(s) < limit) {
+ Zoom *= pow(zoomFactor,s);
+ capzoom();
+ y0=y;
+ setProjection();
+ glutPostRedisplay();
+ }
+ }
+ }
+}
+
+void mousewheel(int wheel, int direction, int x, int y)
+{
+ double zoomFactor=getSetting<double>("zoomfactor");
+ if(zoomFactor > 0.0) {
+ if(direction > 0)
+ Zoom *= zoomFactor;
+ else
+ Zoom /= zoomFactor;
+ capzoom();
+ setProjection();
+ glutPostRedisplay();
+ }
+}
+
+void rotate(int x, int y)
+{
+ if(x > 0 && y > 0) {
+ arcball.mouse_motion(x,Height-y,0,
+ Action == "rotateX", // X rotation only
+ Action == "rotateY"); // 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)
+ value_ptr(drotateMat)[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()
+{
+ Rotate=value_ptr(drotateMat);
+ 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)
+{
+ dmat4 tmpRot(1.0);
+ tmpRot=glm::rotate(tmpRot,glm::radians(step),dvec3(1,0,0));
+ drotateMat=tmpRot*drotateMat;
+ updateArcball();
+}
+
+void rotateY(double step)
+{
+ dmat4 tmpRot(1.0);
+ tmpRot=glm::rotate(tmpRot,glm::radians(step),dvec3(0,1,0));
+ drotateMat=tmpRot*drotateMat;
+ updateArcball();
+}
+
+void rotateZ(double step)
+{
+ dmat4 tmpRot(1.0);
+ tmpRot=glm::rotate(tmpRot,glm::radians(step),dvec3(0,0,1));
+ drotateMat=tmpRot*drotateMat;
+ updateArcball();
+}
+
+void rotateZ(int x, int y)
+{
+ if(x > 0 && y > 0) {
+ 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
+
+string action(int button, int mod)
+{
+ size_t Button;
+ size_t nButtons=5;
+ switch(button) {
+ case GLUT_LEFT_BUTTON:
+ Button=0;
+ break;
+ case GLUT_MIDDLE_BUTTON:
+ Button=1;
+ break;
+ case GLUT_RIGHT_BUTTON:
+ Button=2;
+ break;
+ case GLUT_WHEEL_UP:
+ Button=3;
+ break;
+ case GLUT_WHEEL_DOWN:
+ Button=4;
+ break;
+ default:
+ Button=nButtons;
+ }
+
+ size_t Mod;
+ size_t nMods=4;
+ switch(mod) {
+ case 0:
+ Mod=0;
+ break;
+ case GLUT_ACTIVE_SHIFT:
+ Mod=1;
+ break;
+ case GLUT_ACTIVE_CTRL:
+ Mod=2;
+ break;
+ case GLUT_ACTIVE_ALT:
+ Mod=3;
+ break;
+ default:
+ Mod=nMods;
+ }
+
+ if(Button < nButtons) {
+ array *left=getSetting<array *>("leftbutton");
+ array *middle=getSetting<array *>("middlebutton");
+ array *right=getSetting<array *>("rightbutton");
+ array *wheelup=getSetting<array *>("wheelup");
+ array *wheeldown=getSetting<array *>("wheeldown");
+ array *Buttons[]={left,middle,right,wheelup,wheeldown};
+ array *a=Buttons[button];
+ size_t size=checkArray(a);
+ if(Mod < size)
+ return read<string>(a,Mod);
+ }
+ return "";
+}
+
+void timeout(int)
+{
+}
+
+void mouse(int button, int state, int x, int y)
+{
+ int mod=glutGetModifiers();
+ string Action=action(button,mod);
+
+ if(Action == "zoomin") {
+ glutMotionFunc(NULL);
+ mousewheel(0,1,x,y);
+ return;
+ }
+ if(Action == "zoomout") {
+ glutMotionFunc(NULL);
+ mousewheel(0,-1,x,y);
+ return;
+ }
+
+ if(state == GLUT_DOWN) {
+ if(Action == "rotate" || Action == "rotateX" || Action == "rotateY") {
+ arcball.mouse_down(x,Height-y);
+ glutMotionFunc(rotate);
+ } else if(Action == "shift") {
+ x0=x; y0=y;
+ glutMotionFunc(shift);
+ } else if(Action == "pan") {
+ x0=x; y0=y;
+ glutMotionFunc(pan);
+ } else if(Action == "zoom" || Action == "zoom/menu") {
+ y0=y;
+ glutMotionFunc(zoom);
+ } else if(Action == "rotateZ") {
+ lastangle=Degrees(x,y);
+ glutMotionFunc(rotateZ);
+ }
+ } else {
+ arcball.mouse_up();
+ glutMotionFunc(NULL);
+ }
+}
+
+double spinstep()
+{
+ timeval tv;
+ gettimeofday(&tv,NULL);
+ double step=getSetting<double>("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 expand()
+{
+ double resizeStep=getSetting<double>("resizestep");
+ if(resizeStep > 0.0)
+ setsize((int) (Width*resizeStep+0.5),(int) (Height*resizeStep+0.5));
+}
+
+void shrink()
+{
+ double resizeStep=getSetting<double>("resizestep");
+ if(resizeStep > 0.0)
+ setsize(max((int) (Width/resizeStep+0.5),1),
+ max((int) (Height/resizeStep+0.5),1));
+}
+
+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 write(const char *text, const double *v)
+{
+ cout << text << "=(" << v[0] << "," << v[1] << "," << v[2] << ")";
+}
+
+void showCamera()
+{
+ projection P=camera();
+ cout << endl
+ << "currentprojection="
+ << (P.orthographic ? "orthographic(" : "perspective(") << endl
+ << "camera=" << P.camera << "," << endl
+ << "up=" << P.up << "," << endl
+ << "target=" << P.target << "," << endl
+ << "zoom=" << P.zoom;
+ if(!orthographic)
+ cout << "," << endl << "angle=" << P.angle;
+ if(P.viewportshift != pair(0.0,0.0))
+ cout << "," << endl << "viewportshift=" << P.viewportshift;
+ if(!orthographic)
+ cout << "," << endl << "autoadjust=false";
+ cout << ");" << 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':
+ Export();
+ break;
+ case 'c':
+ showCamera();
+ break;
+ case '+':
+ case '=':
+ case '>':
+ expand();
+ break;
+ case '-':
+ case '_':
+ case '<':
+ shrink();
+ break;
+ case 'p':
+ if(getSetting<bool>("reverse")) Animate=false;
+ Setting("reverse")=Step=false;
+ animate();
+ break;
+ case 'r':
+ if(!getSetting<bool>("reverse")) Animate=false;
+ Setting("reverse")=true;
+ Step=false;
+ animate();
+ break;
+ case ' ':
+ Step=true;
+ animate();
+ break;
+ case 17: // Ctrl-q
+ case 'q':
+ if(!Format.empty()) Export();
+ quit();
+ break;
+ }
+}
+
+void setosize()
+{
+ oldWidth=(int) ceil(oWidth);
+ oldHeight=(int) ceil(oHeight);
+}
+#endif
+// end of GUI-related functions
+
+void exportHandler(int=0)
+{
+#ifdef HAVE_LIBGLUT
+ bool offscreen=getSetting<bool>("offscreen");
+ if(!Iconify && !offscreen)
+ glutShowWindow();
+#endif
+ readyAfterExport=true;
+ Export();
+#ifdef HAVE_LIBGLUT
+ if(!Iconify && !offscreen)
+ glutHideWindow();
+#endif
+ glutDisplayFunc(nodisplay);
+}
+
+static bool glinitialize=true;
+
+projection camera(bool user)
+{
+ if(glinitialize) return projection();
+
+ camp::Triple vCamera,vUp,vTarget;
+
+ double cz=0.5*(zmin+zmax);
+
+ if(user) {
+ for(int i=0; i < 3; ++i) {
+ double sumCamera=0.0, sumTarget=0.0, sumUp=0.0;
+ int i4=4*i;
+ for(int j=0; j < 4; ++j) {
+ int j4=4*j;
+ double R0=Rotate[j4];
+ double R1=Rotate[j4+1];
+ double R2=Rotate[j4+2];
+ double R3=Rotate[j4+3];
+ double T4ij=T[i4+j];
+ sumCamera += T4ij*(R3-cx*R0-cy*R1-cz*R2);
+ sumUp += T4ij*R1;
+ sumTarget += T4ij*(R3-cx*R0-cy*R1);
+ }
+ vCamera[i]=sumCamera;
+ vUp[i]=sumUp;
+ vTarget[i]=sumTarget;
+ }
+ } else {
+ for(int i=0; i < 3; ++i) {
+ int i4=4*i;
+ double R0=Rotate[i4];
+ double R1=Rotate[i4+1];
+ double R2=Rotate[i4+2];
+ double R3=Rotate[i4+3];
+ vCamera[i]=R3-cx*R0-cy*R1-cz*R2;
+ vUp[i]=R1;
+ vTarget[i]=R3-cx*R0-cy*R1;
+ }
+ }
+
+ return projection(orthographic,vCamera,vUp,vTarget,Zoom,
+ 2.0*atan(tan(0.5*Angle)/Zoom)/radians,
+ pair(X/Width*lastzoom+Shift.getx(),
+ Y/Height*lastzoom+Shift.gety()));
+}
+
+void init()
+{
+#ifdef HAVE_LIBGLUT
+ mem::vector<string> cmd;
+ cmd.push_back(settings::argv0);
+ if(!interact::interactive && Iconify)
+ cmd.push_back("-iconic");
+ push_split(cmd,getSetting<string>("glOptions"));
+ char **argv=args(cmd,true);
+ int argc=cmd.size();
+
+#ifndef __APPLE__
+ glutInitContextProfile(GLUT_CORE_PROFILE);
+#endif
+
+ glutInit(&argc,argv);
+ screenWidth=glutGet(GLUT_SCREEN_WIDTH);
+ screenHeight=glutGet(GLUT_SCREEN_HEIGHT);
+
+#endif
+}
+
+void init_osmesa()
+{
+#ifdef HAVE_LIBOSMESA
+ // create context and buffer
+ if(settings::verbose > 1)
+ cout << "Allocating osmesa_buffer of size " << screenWidth << "x"
+ << screenHeight << "x4x" << sizeof(GLubyte) << endl;
+ osmesa_buffer=new unsigned char[screenWidth*screenHeight*4*sizeof(GLubyte)];
+ if(!osmesa_buffer) {
+ cerr << "Cannot allocate image buffer." << endl;
+ exit(-1);
+ }
+
+ ctx = OSMesaCreateContextExt(OSMESA_RGBA,16,0,0,NULL);
+ if(!ctx) {
+ cerr << "OSMesaCreateContext failed." << endl;
+ exit(-1);
+ }
+
+ if(!OSMesaMakeCurrent(ctx,osmesa_buffer,GL_UNSIGNED_BYTE,
+ screenWidth,screenHeight )) {
+ cerr << "OSMesaMakeCurrent failed." << endl;
+ exit(-1);
+ }
+
+ int z=0, s=0, a=0;
+ glGetIntegerv(GL_DEPTH_BITS,&z);
+ glGetIntegerv(GL_STENCIL_BITS,&s);
+ glGetIntegerv(GL_ACCUM_RED_BITS,&a);
+ if(settings::verbose > 1)
+ cout << "Offscreen context settings: Depth=" << z << " Stencil=" << s
+ << " Accum=" << a << endl;
+
+ if(z <= 0) {
+ cerr << "Error initializing offscreen context: Depth=" << z << endl;
+ exit(-1);
+ }
+#endif // HAVE_LIBOSMESA
+}
+
+GLuint vertShader,fragShader;
+GLuint vertShaderCol,fragShaderCol;
+
+void initshader()
+{
+ Nlights=max(Nlights,nlights);
+ Nmaterials=max(Nmaterials,nmaterials);
+ shaderProg=glCreateProgram();
+ string vs=locateFile("shaders/vertex.glsl");
+ string fs=locateFile("shaders/fragment.glsl");
+ if(vs.empty() || fs.empty()) {
+ cerr << "GLSL shaders not found." << endl;
+ exit(-1);
+ }
+
+ std::vector<std::string> shaderParams;
+
+#if HAVE_LIBOPENIMAGEIO
+ if (getSetting<bool>("envmap")) {
+ shaderParams.push_back("ENABLE_TEXTURE");
+ envMapBuf=initHDR();
+ }
+#endif
+
+ std::vector<ShaderfileModePair> shaders;
+ shaders.push_back(ShaderfileModePair(vs.c_str(),GL_VERTEX_SHADER));
+ shaders.push_back(ShaderfileModePair(fs.c_str(),GL_FRAGMENT_SHADER));
+
+ camp::noNormalShader=compileAndLinkShader(shaders,Nlights,Nmaterials,
+ shaderParams);
+
+ shaderParams.push_back("WIDTH");
+ camp::pixelShader=compileAndLinkShader(shaders,Nlights,Nmaterials,
+ shaderParams);
+ shaderParams.pop_back();
+
+ shaderParams.push_back("NORMAL");
+ camp::materialShader=compileAndLinkShader(shaders,Nlights,Nmaterials,
+ shaderParams);
+
+ shaderParams.push_back("EXPLICIT_COLOR");
+ camp::colorShader=compileAndLinkShader(shaders,Nlights,Nmaterials,
+ shaderParams);
+}
+
+void deleteshader()
+{
+ glDeleteProgram(camp::materialShader);
+ glDeleteProgram(camp::colorShader);
+ glDeleteProgram(camp::noNormalShader);
+}
+
+// angle=0 means orthographic.
+void glrender(const string& prefix, const picture *pic, const string& format,
+ double width, double height, double angle, double zoom,
+ const triple& m, const triple& M, const pair& shift, double *t,
+ double *background, size_t nlightsin, triple *lights,
+ double *diffuse, double *specular, bool view, int oldpid)
+{
+ remesh=true;
+ bool offscreen=getSetting<bool>("offscreen");
+ Iconify=getSetting<bool>("iconify");
+
+#ifdef HAVE_PTHREAD
+ static bool initializedView=false;
+#endif
+ width=max(width,1.0);
+ height=max(height,1.0);
+
+ if(zoom == 0.0) zoom=1.0;
+
+ Prefix=prefix;
+ Picture=pic;
+ Format=format;
+ for(int i=0; i < 16; ++i)
+ T[i]=t[i];
+ for(int i=0; i < 4; ++i)
+ Background[i]=background[i];
+
+ nlights0=nlights=nlightsin;
+
+ Lights=lights;
+ Diffuse=diffuse;
+ Specular=specular;
+ View=view;
+ Angle=angle*radians;
+ Zoom0=zoom;
+ Oldpid=oldpid;
+ Shift=shift;
+
+ Xmin=m.getx();
+ Xmax=M.getx();
+ Ymin=m.gety();
+ Ymax=M.gety();
+ zmin=m.getz();
+ zmax=M.getz();
+
+ orthographic=Angle == 0.0;
+ H=orthographic ? 0.0 : -tan(0.5*Angle)*zmax;
+
+ ignorezoom=false;
+ Mode=0;
+ Xfactor=Yfactor=1.0;
+
+ pair maxtile=getSetting<pair>("maxtile");
+ maxTileWidth=(int) maxtile.getx();
+ maxTileHeight=(int) maxtile.gety();
+ if(maxTileWidth <= 0) maxTileWidth=1024;
+ if(maxTileHeight <= 0) maxTileHeight=768;
+
+ if(offscreen) {
+ screenWidth=maxTileWidth;
+ screenHeight=maxTileHeight;
+
+ static bool osmesa_initialized=false;
+ if(!osmesa_initialized) {
+ osmesa_initialized=true;
+ init_osmesa();
+ }
+ }
+
+ if(glinitialize) {
+ init();
+ Fitscreen=1;
+ }
+
+ static bool initialized=false;
+ 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;
+ else screenWidth=min(screenWidth,maxWidth);
+ if(screenHeight <= 0) screenHeight=maxHeight;
+ else screenHeight=min(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;
+
+ if(maxTileWidth <= 0) maxTileWidth=screenWidth;
+ if(maxTileHeight <= 0) maxTileHeight=screenHeight;
+#ifdef HAVE_LIBGLUT
+ setosize();
+#endif
+
+ if(View && settings::verbose > 1)
+ cout << "Rendering " << stripDir(prefix) << " as "
+ << Width << "x" << Height << " image" << endl;
+ }
+
+ bool havewindow=initialized && glthread && !offscreen;
+
+#ifdef HAVE_LIBGLUT
+ unsigned int displaymode=GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH;
+#endif
+
+#ifdef __APPLE__
+ displaymode |= GLUT_3_2_CORE_PROFILE;
+#endif
+
+ camp::clearMaterialBuffer();
+
+#ifdef HAVE_PTHREAD
+ if(glthread && initializedView && !offscreen) {
+ if(!View)
+ readyAfterExport=queueExport=true;
+ pthread_kill(mainthread,SIGUSR1);
+ return;
+ }
+#endif
+
+#ifdef HAVE_LIBGLUT
+ if(!offscreen) {
+ 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);
+
+ 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;
+ 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=floorpow2(multisample-1);
+ if(multisample > 1) {
+ glutReshapeWindow(1,1);
+ glutDisplayFunc(destroywindow);
+ glutShowWindow();
+ glutMainLoopEvent();
+ continue;
+ }
+ }
+ break;
+ }
+#endif
+#endif
+ if(settings::verbose > 1 && samples > 1)
+ cout << "Multisampling enabled with sample width " << samples
+ << endl;
+ glutDisplayFunc(display);
+ glutShowWindow();
+ } else if(!havewindow) {
+ glutInitWindowSize(maxTileWidth,maxTileHeight);
+ glutInitDisplayMode(displaymode);
+ window=glutCreateWindow("");
+ glutHideWindow();
+ }
+ }
+#endif // HAVE_LIBGLUT
+ initialized=true;
+
+ GLint val;
+ glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE,&val);
+ Maxmaterials=val/sizeof(Material);
+ if(nmaterials > Maxmaterials) nmaterials=Maxmaterials;
+
+ glGetIntegerv(GL_MAX_ELEMENTS_VERTICES,&Maxvertices);
+
+ if(glinitialize) {
+ glinitialize=false;
+ int result = glewInit();
+
+ if (result != GLEW_OK) {
+ cerr << "GLEW initialization error." << endl;
+ exit(-1);
+ }
+
+ initshader();
+ }
+
+ home();
+
+#ifdef HAVE_LIBGLUT
+ if(!offscreen) {
+ Animate=getSetting<bool>("autoplay") && glthread;
+
+ if(View) {
+ if(!getSetting<bool>("fitscreen"))
+ Fitscreen=0;
+ fitscreen();
+ setosize();
+ }
+ }
+#endif
+
+ glEnable(GL_BLEND);
+ glEnable(GL_DEPTH_TEST);
+ glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
+
+ glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
+ mode();
+
+ if(View && !offscreen) {
+#ifdef HAVE_LIBGLUT
+#ifdef HAVE_PTHREAD
+ initializedView=true;
+#endif
+ glutReshapeFunc(reshape);
+ glutKeyboardFunc(keyboard);
+ glutMouseFunc(mouse);
+ glutDisplayFunc(display);
+
+ glutMainLoop();
+#endif // HAVE_LIBGLUT
+ } else {
+ if(glthread && !offscreen) {
+ if(havewindow) {
+ readyAfterExport=true;
+#ifdef HAVE_PTHREAD
+ pthread_kill(mainthread,SIGUSR1);
+#endif
+ } else {
+ initialized=true;
+ readyAfterExport=true;
+ Signal(SIGUSR1,exportHandler);
+ exportHandler();
+ }
+ } else {
+ exportHandler();
+ quit();
+ }
+ }
+}
+
+} // namespace gl
+
+namespace camp {
+
+string getLightIndex(size_t const& index, string const& fieldName) {
+ ostringstream buf;
+ buf << "lights[" << index << "]." << fieldName;
+ return Strdup(buf.str());
+}
+
+void setUniforms(GLint shader)
+{
+ if(gl::nlights > gl::Nlights || nmaterials > Nmaterials) {
+ gl::deleteshader();
+ gl::initshader();
+ }
+
+ glUseProgram(shader);
+
+ glUniformMatrix4fv(glGetUniformLocation(shader,"projViewMat"),1,GL_FALSE, value_ptr(gl::projViewMat));
+ glUniformMatrix4fv(glGetUniformLocation(shader,"viewMat"),1,GL_FALSE, value_ptr(gl::viewMat));
+ glUniformMatrix4fv(glGetUniformLocation(shader,"normMat"),1,GL_FALSE, value_ptr(gl::normMat));
+
+ GLuint binding=0;
+ GLint blockindex=glGetUniformBlockIndex(shader,"MaterialBuffer");
+ glUniformBlockBinding(shader,blockindex,binding);
+
+ glGenBuffers(1,&gl::ubo);
+ glBindBuffer(GL_UNIFORM_BUFFER,gl::ubo);
+
+ glBufferData(GL_UNIFORM_BUFFER,drawElement::material.size()*sizeof(Material),
+ drawElement::material.data(),GL_STATIC_DRAW);
+ glBindBufferBase(GL_UNIFORM_BUFFER,binding,gl::ubo);
+
+ glUniform1i(glGetUniformLocation(shader,"nlights"),gl::nlights);
+
+ for(size_t i=0; i < gl::nlights; ++i) {
+ triple Lighti=gl::Lights[i];
+ size_t i4=4*i;
+ glUniform4f(glGetUniformLocation(shader,
+ getLightIndex(i,"direction").c_str()),
+ (GLfloat) Lighti.getx(),(GLfloat) Lighti.gety(),
+ (GLfloat) Lighti.getz(),0.0);
+
+ glUniform4f(glGetUniformLocation(shader,
+ getLightIndex(i,"diffuse").c_str()),
+ (GLfloat) gl::Diffuse[i4],(GLfloat) gl::Diffuse[i4+1],
+ (GLfloat) gl::Diffuse[i4+2],(GLfloat) gl::Diffuse[i4+3]);
+
+ glUniform4f(glGetUniformLocation(shader,
+ getLightIndex(i,"specular").c_str()),
+ (GLfloat) gl::Specular[i4],(GLfloat) gl::Specular[i4+1],
+ (GLfloat) gl::Specular[i4+2],(GLfloat) gl::Specular[i4+3]);
+ }
+
+#if HAVE_LIBOPENIMAGEIO
+ // textures
+ if (settings::getSetting<bool>("envmap")) {
+ glActiveTexture(GL_TEXTURE1);
+ glBindBuffer(GL_TEXTURE_2D, gl::envMapBuf);
+ glUniform1i(glGetUniformLocation(shader, "environmentMap"), 1);
+ glActiveTexture(GL_TEXTURE0);
+ }
+#endif
+}
+
+void deleteUniforms()
+{
+ glBindBuffer(GL_UNIFORM_BUFFER,0);
+ glDeleteBuffers(1,&gl::ubo);
+ glUseProgram(0);
+}
+
+}
+
+#endif