/* gl.c: rendering interface for Iris GL and clones */ #include #include #include /* Iris GL defines a constant named MATERIAL while we have a type called MATERIAL */ #define GL_MATERIAL MATERIAL #undef MATERIAL /* idem for our function called Scale() in transform.h */ #define GL_Scale Scale #undef Scale #include "render.h" #include "canvas.h" #include "camera.h" #include "error.h" #include "pools.h" #include "scene.h" #include "radiance.h" #include "renderhook.h" #include "image.h" static XVisualInfo *gl_vinfo; /* describes visual to be used for rendering */ #ifdef TIMINGS #include static clock_t render_time=0; static int render_frames=0; #endif static int dlistid = -1; /* display list ID */ static int glinited = False; /* set to True when GL is initialized on the window. * No drawing into the window should be allowed before * this variable is set to True in GLinitCallback(). */ static long zmax; /* Z value to clear Z buffer with */ /* identity matrix */ static const Matrix idmat = {{1.0, 0.0, 0.0, 0.0}, {0.0, 1.0, 0.0, 0.0}, {0.0, 0.0, 1.0, 0.0}, {0.0, 0.0, 0.0, 1.0}}; /* GLX configuration info. See GLXgetconfig() manual page. */ static GLXconfig sdescin[] = { /* GLXconfig given to XGLgetconfig as input parm */ #ifndef NO_DOUBLE_BUFFERING {GLX_NORMAL, GLX_DOUBLE , True}, /* window with double buffering */ #else {GLX_NORMAL, GLX_DOUBLE , False}, /* window without double buffering */ #endif {GLX_NORMAL, GLX_RGB , True}, /* window in RGB colormode */ {GLX_NORMAL, GLX_ZSIZE , GLX_NOCONFIG}, /* max. nr of Z planes */ {0 , 0 , 0} }, *sdescout=NULL; /* GLXconfig returned by GLXgetconfig */ /* finds the visual and colormap to be used for Iris GL rendering on the specified * screen */ Boolean RenderGetVisualInfoAndColormap(Screen *screen, XVisualInfo *visual_info, Colormap *colormap) { int scrno, count; XVisualInfo vinfo_template; GLXconfig *psdesc; Display *dpy; dpy = DisplayOfScreen(screen); /* find screen number of screen on display */ for (scrno=0; scrno < ScreenCount(dpy); scrno++) { if (ScreenOfDisplay(dpy, scrno) == screen) break; } if (scrno >= ScreenCount(dpy)) { Fatal(1, "RenderGetVisualInfoAndColormap", "screen not found on its display - what a nonsense!"); } /* returns information about the visual and colormaps to be used for rendering */ sdescout = GLXgetconfig(dpy, scrno, sdescin); if (!sdescout) { Fatal(1, "RenderGetVisualInfoAndColormap", "Could not obtain GLX config info\n"); } psdesc = sdescout; while (psdesc->buffer != 0) { switch (psdesc->buffer) { case GLX_NORMAL: switch (psdesc->mode) { case GLX_COLORMAP: *colormap = (Colormap)psdesc->arg; break; case GLX_VISUAL: vinfo_template.visualid = (VisualID)psdesc->arg; vinfo_template.screen = scrno; vinfo_template.class = TrueColor; /* find the XVisualInfo for this visual ID */ gl_vinfo = XGetVisualInfo(dpy, VisualIDMask|VisualScreenMask|VisualClassMask, &vinfo_template, &count); if (!gl_vinfo) Fatal(1, "RenderGetVisualInfoAndColormap", "couldn't find visual info for visual id %d\n", vinfo_template.visualid); *visual_info = *gl_vinfo; break; default: break; } break; default: break; } psdesc++; } return True; /* non-default visual required */ } /* Prepare for rendering in the given window. */ void RenderInitWindow(Display *display, Window window, XVisualInfo *visinfo) { float lmprops[4]; GLXconfig *psdesc; if (!sdescout) { /* external canvas window, check window visual and set colormap. */ XWindowAttributes wattr; XVisualInfo gl_vinfo; Colormap cmap; XGetWindowAttributes(display, window, &wattr); RenderGetVisualInfoAndColormap(wattr.screen, &gl_vinfo, &cmap); if (gl_vinfo.visualid != visinfo->visualid) Fatal(1, NULL, "The external canvas has wrong visualID (0x%02x, should be 0x%02)", visinfo->visualid, gl_vinfo.visualid); XSetWindowColormap(display, window, cmap); } /* fill in the window IDs in the arg field of description items with * mode GLX_WINDOW */ for (psdesc = sdescout; psdesc->buffer != 0; psdesc++) { if (psdesc->mode == GLX_WINDOW) psdesc->arg = window; } /* prepare for drawing onto the display */ if (GLXlink(display, sdescout) < 0) Fatal(1, "GLinitCallback", "Graphics initialisation failed: nonzero return code from GLXlink()"); /* all subsequent GL drawing will happen in the canvas window */ if (GLXwinset(display, window) < 0) Fatal(1, "RenderCreateWindow", "Graphics initialisation failed: nonzero return code from GLXwinset()"); zfunction(ZF_LEQUAL); zmax = getgdesc(GD_ZMAX); /* value to clear the Z buffer with */ zbuffer(1); /* switch Z buffering on */ frontbuffer(1); /* everything that is drawn outside RenderScene() * should be visible immediately, so enable rendering * in the frontbuffer. */ /* shading model is very simple: there is only an ambient light source with * intensity (1,1,1) */ lmprops[0] = AMBIENT; lmprops[1] = 1.0; lmprops[2] = 1.0; lmprops[3] = 1.0; lmdef(DEFLMODEL, 1, 4, lmprops); lmbind(LMODEL, 1); /* the glinited variable indicates whether or not GL has been initialized */ glinited = True; } /* this expose callback function is invoked the first time the canvas widget is * exposed. It initializes Iris Gl for rendering into the canvas window. */ static void GLinitCallback(Widget canvas, XtPointer client_data, XtPointer call_data) { RenderInitWindow(XtDisplay(canvas), XtWindow(canvas), gl_vinfo); /* remove the GLinitCallback: it needs to be executed just once: when the widget * is exposed for the first time. */ XtRemoveCallback(canvas, XmNexposeCallback, GLinitCallback, (XtPointer)NULL); } /* create window for rendering */ Widget RenderCreateWindow(Widget parent) { Widget canvas; canvas = XtVaCreateManagedWidget("canvas", xmDrawingAreaWidgetClass, parent, NULL); /* initialize GL on first expose event */ glinited = False; XtAddCallback(canvas, XmNexposeCallback, GLinitCallback, (XtPointer)NULL); return canvas; } void RenderCreateOffscreenWindow(int hres, int vres) { Fatal(1, NULL, "Offscreen rendering is only supported through the Mesa library.\nRe-compile the program using the Mesa library if you want it"); } int RenderInitialized(void) { return glinited; } /* clear the canvas window and Z buffer */ void RenderClearWindow(void) { long bkgcolor; bkgcolor = (unsigned char)(Camera.background.r * 255.) | ((unsigned char)(Camera.background.g * 255.) << 8 ) | ((unsigned char)(Camera.background.b * 255.) << 16); czclear(bkgcolor, zmax); } /* up-direction can be anything, not just (0,1,0) */ static void RenderFixUpTransform(void) { float d, t; VECTOR up=Camera.updir; VectorNormalize(&up); /* push the transforms in reverse order */ t = acos(up.y) * 180. / M_PI; rot(t, 'z'); d = 1. - up.y*up.y; if (d > EPSILON) { t = acos(up.x/sqrt(d)) * 180. / M_PI; if (up.z < 0.) t = -t; rot(t, 'y'); } } static void RenderFixUpPoint(VECTOR *src, VECTOR *dest) { VECTOR tmp; float d, t; VECTOR up=Camera.updir; VectorNormalize(&up); d = 1. - up.y*up.y; if (d > EPSILON) { t = acos(up.x/sqrt(d)); if (up.z < 0.) t = -t; /* rot(t, 'y'); */ tmp.z = cos(t) * src->z - sin(t) * src->x; tmp.x = sin(t) * src->z + cos(t) * src->x; tmp.y = src->y; } else tmp = *src; t = acos(up.y); /* rot(t, 'z'); */ dest->x = cos(t) * tmp.x - sin(t) * tmp.y; dest->y = sin(t) * tmp.x + cos(t) * tmp.y; dest->z = tmp.z; } /* passes the current virtual camera position, focus point, fov etc... * to the graphics hardware */ void RenderSetCamera(void) { VECTOR eyep, lookp; RenderClearWindow(); loadmatrix(idmat); /* something valid on the matrix stack at least */ /* use full viewport */ viewport(0, Camera.hres-1, 0, Camera.vres-1); /* determine distance to front- and backclipping plane */ RenderGetNearFar(&Camera.near, &Camera.far); /* x2 because Iris GL has another opinion about what fov is and x 10 because * perspective wants angles in tents of degrees: fov=450 -> 45 degrees */ perspective(Camera.vfov*20., (float)Camera.hres/(float)Camera.vres, Camera.near, Camera.far); /* GL assumes that up is along the positive Y axis, convert our idea of up */ RenderFixUpPoint(&Camera.eyep, &eyep); RenderFixUpPoint(&Camera.lookp, &lookp); lookat(eyep.x, eyep.y ,eyep.z, lookp.x, lookp.y, lookp.z, 0.0); RenderFixUpTransform(); } /* sets the current color */ void RenderSetColor(RGB *rgb) { RGB corrected_rgb; corrected_rgb = *rgb; RGBGAMMACORRECT(corrected_rgb, renderopts.gamma); c3f((float *)&corrected_rgb); } /* renders a convex polygon flat shaded in the current color */ void RenderPolygonFlat(int nrverts, POINT *verts) { int i; bgnpolygon(); for (i=0; i 256) { Error("RenderTriangleStrip", "Too many vertices in strip (<256)"); return; } for(i = 0; i < N; i++) { RenderSetColor(&col[i]); v3f((float *)(&point[i])); } } void RenderEndTriangleStrip(void) { endtmesh(); } /* renders the patch as a solid polygon, flat shaded with its computed color */ void RenderPatchFlat(PATCH *patch) { int i; RenderSetColor(&patch->color); bgnpolygon(); for (i=0; inrvertices; i++) v3f((float *)patch->vertex[i]->point); endpolygon(); } /* renders the patch as a solid polygon, smooth shaded using the colors computed * for its vertices */ void RenderPatchSmooth(PATCH *patch) { int i; bgnpolygon(); for (i=0; inrvertices; i++) { RenderSetColor(&patch->vertex[i]->color); v3f((float *)patch->vertex[i]->point); } endpolygon(); } /* renders the patch outline in the current color */ void RenderPatchOutline(PATCH *patch) { int i; POINT tmp; VECTOR dir; if (renderopts.backface_culling) { /* don't draw outlines of backfacing polygons. */ VECTORSUBTRACT(Camera.eyep, patch->midpoint, dir); if (VECTORDOTPRODUCT(dir, patch->normal) < 0.) return; } bgnclosedline(); for (i=0; inrvertices; i++) { /* move the outlines a bit closer to the eyepoint to avoid Z buffer * artefacts */ VECTORSUBTRACT(Camera.eyep, *patch->vertex[i]->point, dir); VECTORSUMSCALED(*patch->vertex[i]->point, 0.01, dir, tmp); v3f((float *)&tmp); } endclosedline(); } /* renders a patch */ void RenderPatch(PATCH *patch) { if (renderopts.smooth_shading) RenderPatchSmooth(patch); else RenderPatchFlat(patch); if (renderopts.draw_outlines) { RenderSetColor(&renderopts.outline_color); RenderPatchOutline(patch); } } void RenderNewDisplayList(void) { if (dlistid >= 0) delobj(dlistid); dlistid = -1; renderopts.render_raytracing_image = FALSE; } static void RenderRadiance(void) { RenderSetCamera(); backface(renderopts.backface_culling); if (renderopts.smooth_shading) shademodel(GOURAUD); else shademodel(FLAT); if (renderopts.use_display_lists) { if (dlistid < 0) { dlistid = 1; makeobj(dlistid); if (!Radiance || !Radiance->RenderScene) { PatchListIterate(Patches, RenderPatch); } else Radiance->RenderScene(); closeobj(); } callobj(dlistid); } else { if (!Radiance || !Radiance->RenderScene) { PatchListIterate(Patches, RenderPatch); } else Radiance->RenderScene(); } if (renderopts.draw_bounding_boxes) RenderBoundingBoxHierarchy(); if (renderopts.draw_clusters) RenderClusterHierarchy(); /* Call installed render hooks, that want to render something in the scene */ RenderHooks(); } /* renders the whole scene */ void RenderScene(void) { #ifdef NEVER clock_t t = clock(); #endif /* don't draw if GL has not yet been initialized for drawing into the canvas window */ if (!glinited) return; CanvasPushMode(CANVASMODE_RENDER); backbuffer(1); frontbuffer(0); /* render in the backbuffer only */ if (Camera.changed) renderopts.render_raytraced_image = FALSE; if (!renderopts.render_raytraced_image || !RenderRayTraced()) RenderRadiance(); swapbuffers(); backbuffer(0); frontbuffer(1); /* everything that is rendered elsewhere is expected to * be visible immediately */ CanvasPullMode(); #ifdef NEVER render_time += clock() - t; render_frames++; fprintf(stderr, "%d frames, %g sec: %g msec/frame\n", render_frames, (float)render_time/(float)CLOCKS_PER_SEC, (float)render_time/(float)CLOCKS_PER_SEC/(float)render_frames*1000.); #endif } void RenderFinish(void) { finish(); } /* renders an image of m lines of n pixels at column x on row y (= lower * left corner of image, relative to the lower left corner of the window) */ void RenderPixels(int x, int y, int n, int m, RGB *rgb) { int i; unsigned long *c; RGB corrected_rgb; c = (unsigned long *)Alloc(n * m * sizeof(unsigned long)); for (i=0; i= Camera.near) && (z <= Camera.far)) { zbuffer(0); mmode(MPROJECTION); pushmatrix(); ortho2(-0.5, (float)Camera.hres+0.5, -0.5, (float)Camera.vres+0.5); mmode(MVIEWING); pushmatrix(); loadmatrix(idmat); circ(x, y, 3); mmode(MVIEWING); popmatrix(); mmode(MPROJECTION); popmatrix(); zbuffer(1); } } /* reads a GL drawing buffer */ unsigned long *GetScreenBuffer(long *x, long *y) { unsigned long *screen; getsize(x, y); screen = (unsigned long *)Alloc((*x) * (*y) * sizeof(unsigned long)); lrectread(0, 0, *x-1, *y-1, screen); return screen; } /* saves the image in the front buffer to a ppm file, viewable with e.g. xv */ void SaveScreen(char *fname, FILE *fp, int ispipe) { ImageOutputHandle *img; unsigned long *screen; long i, j, x, y; unsigned char *buf; readsource(SRC_FRONT); screen = GetScreenBuffer(&x, &y); if (!fp || !(img = CreateImageOutputHandle(fname, fp, ispipe, x, y))) return; buf = (unsigned char *)Alloc(3 * x); for (j=y-1; j>=0; j--) { long *pixel = screen+j*x; unsigned char *fbuf = buf; for (i=0; i>8); *fbuf++ = (unsigned char)(((*pixel)&0xff0000)>>16); } WriteDisplayRGB(img, buf); } Free((char *)buf, 3 * x); Free((char *)screen, x * y * sizeof(unsigned long)); DeleteImageOutputHandle(img); } /* for determining directly received importance etc... */ static void RenderPatchID(PATCH *patch) { int i; cpack(((unsigned long)patch->id)&0xffffff); bgnpolygon(); for (i=0; inrvertices; i++) v3f((float *)patch->vertex[i]->point); endpolygon(); } /* Patch ID rendering. Returns an array of size (*x)*(*y) containing the IDs of * the patches visible through each pixel or 0 if the background is visible through * the pixel. x and y, determining the size of the array are returned. */ unsigned long *RenderIds(long *x, long *y) { unsigned long *ids; *x = *y = 0; backbuffer(1); frontbuffer(0); /* the user shouldn't notice it */ RenderSetCamera(); backface(renderopts.backface_culling); shademodel(FLAT); PatchListIterate(Patches, RenderPatchID); /* indicate that we want to read from the back buffer */ readsource(SRC_BACK); ids = GetScreenBuffer(x, y); backbuffer(0); frontbuffer(1); /* render in the frontbuffer only again */ return ids; }