/* opengl.c: OpenGL graphics driver. */ #include #include #include #include #include #include #include #include #include "render.h" #include "canvas.h" #include "camera.h" #include "error.h" #include "pools.h" #include "scene.h" #include "radiance.h" #include "raytracing.h" #include "raycasting.h" #include "patch.h" #include "renderhook.h" #include "image.h" #include "tonemapping.h" #include #ifdef TIMINGS static clock_t render_time=0; static int render_frames=0; #endif #ifdef ALL_DOUBLE /* Use double for everything : DIRTY !!!*/ #define glVertex3fv glVertex3dv #define GLfloat GLdouble #endif static int dlistid = -1; /* display list ID */ static XVisualInfo *glx_visual; static GLXContext glx_context; static Display *glx_display; static Window glx_window; static int attribute_list_db[] = {GLX_RGBA, GLX_RED_SIZE, 2, GLX_GREEN_SIZE, 2, GLX_BLUE_SIZE, 2, GLX_DEPTH_SIZE, 16, GLX_DOUBLEBUFFER, None}; static int attribute_list_nodb[] = {GLX_RGBA, GLX_RED_SIZE, 2, GLX_GREEN_SIZE, 2, GLX_BLUE_SIZE, 2, GLX_DEPTH_SIZE, 16, None}; static int ogl_inited = FALSE, doublebuf = FALSE; int do_render_normals; static GLubyte *background_ptr = NULL; static GLuint backgroundTex=0; static XVisualInfo *OpenGLGetVisual(Display *dpy) { if (!glXQueryExtension(dpy, NULL, NULL)) Fatal(1, NULL, "No GLX extension available on the display"); /* get an appropriate visual: first look for a visual with double buffering */ glx_visual = glXChooseVisual(dpy, DefaultScreen(dpy), attribute_list_db); if (glx_visual) { doublebuf = TRUE; return glx_visual; } else { /* look for a visual without double buffering */ doublebuf = FALSE; return glx_visual = glXChooseVisual(dpy, DefaultScreen(dpy), attribute_list_nodb); } } /* finds the visual and colormap to be used for OpenGL rendering on the specified * screen */ Boolean RenderGetVisualInfoAndColormap(Screen *screen, XVisualInfo *visual_info, Colormap *colormap) { XVisualInfo *vi; Display *dpy = DisplayOfScreen(screen); vi = OpenGLGetVisual(dpy); if (!vi) Fatal(1, NULL, "Failed to get a suitable X visual"); *visual_info = *vi; *colormap = XCreateColormap(dpy, RootWindow(dpy, visual_info->screen), visual_info->visual, AllocNone); return TRUE; } void RenderClearWindow(void) { glClearColor(Camera.background.r, Camera.background.g, Camera.background.b, 0.); glClearDepth(1.); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } void RenderSetCamera(void) { RenderClearWindow(); /* use full viewport */ glViewport(0, 0, Camera.hres, Camera.vres); /* draw backgroudn when needed */ if(renderopts.use_background && background_ptr) RenderBackground(&Camera); /* determine distance to front- and backclipping plane */ RenderGetNearFar(&Camera.near, &Camera.far); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(Camera.vfov*2., (float)Camera.hres/(float)Camera.vres, Camera.near, Camera.far); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gluLookAt(Camera.eyep.x, Camera.eyep.y ,Camera.eyep.z, Camera.lookp.x, Camera.lookp.y, Camera.lookp.z, Camera.updir.x, Camera.updir.y, Camera.updir.z); } static void OpenGLInitState(void) { glDepthFunc(GL_LEQUAL); glEnable(GL_DEPTH_TEST); glDrawBuffer(GL_FRONT_AND_BACK); RenderClearWindow(); glFinish(); ogl_inited = TRUE; dlistid = -1; do_render_normals = FALSE; } void RenderInitWindow(Display *display, Window window, XVisualInfo *visinfo) { glx_display = display; glx_window = window; glx_visual = visinfo; /* create a GLX context */ glx_context = glXCreateContext(glx_display, glx_visual, 0, GL_TRUE); if (!glx_context) Fatal(1, NULL, "Couldn't create GLX context. Is the window OpenGL capable??"); /* connect the context to the window */ if (!glXMakeCurrent(glx_display, glx_window, glx_context)) Fatal(1, NULL, "Couldn't make the GLX context current."); doublebuf = TRUE; /* assume it is true */ OpenGLInitState(); } static void OpenGLInitCallback(Widget canvas, XtPointer client_data, XtPointer call_data) { RenderInitWindow(XtDisplay(canvas), XtWindow(canvas), glx_visual); XtRemoveCallback(canvas, XmNexposeCallback, OpenGLInitCallback, (XtPointer)NULL); } /* open window for rendering */ Widget RenderCreateWindow(Widget parent) { Widget canvas; canvas = XtVaCreateManagedWidget("canvas", xmDrawingAreaWidgetClass, parent, NULL); /* initialize OpenGL on first expose */ ogl_inited = FALSE; XtAddCallback(canvas, XmNexposeCallback, OpenGLInitCallback, (XtPointer)NULL); return canvas; } #ifdef OSMESA /* Offscreen rendering using Mesa's offscreen rendering contexts */ #include "GL/osmesa.h" /* creates an offscreen window for rendering */ void RenderCreateOffscreenWindow(int hres, int vres) { GLubyte *image_buffer = (GLubyte *)Alloc(hres * vres * sizeof(GLubyte) * 4); OSMesaContext osctx = OSMesaCreateContext(OSMESA_RGBA, NULL); if (!osctx) Fatal(1, NULL, "Couldn't create Mesa offscreen rendering context"); if (!OSMesaMakeCurrent(osctx, image_buffer, GL_UNSIGNED_BYTE, hres, vres)) Fatal(1, NULL, "Couldn't bind Mesa offscreen rendering context to image buffer of size %d x %d", hres, vres); OpenGLInitState(); } #else /*OSMESA*/ /* not Mesa, so no Mesa offscreen rendering context */ void RenderCreateOffscreenWindow(int hres, int vres) { Fatal(1, NULL, "Offscreen rendering is only supported through the Mesa library.\nRe-link the program with the Mesa library if you want it"); } #endif /*OSMESA*/ int RenderInitialized(void) { return ogl_inited; } void RenderLine(VECTOR *x, VECTOR *y) { POINT X, Y; VECTOR dir; glBegin(GL_LINES); /* move the line a bit closer to the eyepoint to avoid Z buffer * artefacts */ VECTORSUBTRACT(Camera.eyep, *x, dir); VECTORSUMSCALED(*x, 0.01, dir, X); glVertex3fv((GLfloat *)&X); VECTORSUBTRACT(Camera.eyep, *y, dir); VECTORSUMSCALED(*y, 0.01, dir, Y); glVertex3fv((GLfloat *)&Y); glEnd(); } void RenderAALine(VECTOR *x, VECTOR *y) { glEnable(GL_LINE_SMOOTH); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glHint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE); RenderLine(x,y); glDisable(GL_LINE_SMOOTH); glDisable(GL_BLEND); } /* sets the current color */ void RenderSetColor(RGB *rgb) { RGB corrected_rgb; corrected_rgb = *rgb; RGBGAMMACORRECT(corrected_rgb); glColor3fv((GLfloat *)&corrected_rgb); } /* sets line width */ void RenderSetLineWidth(float width) { glLineWidth(width); } /* renders a convex polygon flat shaded in the current color */ void RenderPolygonFlat(int nrverts, POINT *verts) { int i; glBegin(GL_POLYGON); for (i=0; i 256) { Error("RenderTriangleStrip", "Too many vertices in strip (<256)"); return; } for(i = 0; i < N; i++) { RenderSetColor(&col[i]); glVertex3fv((float *)(&point[i])); } } void RenderEndTriangleStrip(void) { glEnd(); } void RenderPatchFlat(PATCH *patch) { int i; RenderSetColor(&patch->color); switch (patch->nrvertices) { case 3: glBegin(GL_TRIANGLES); glVertex3fv((GLfloat *)patch->vertex[0]->point); glVertex3fv((GLfloat *)patch->vertex[1]->point); glVertex3fv((GLfloat *)patch->vertex[2]->point); glEnd(); break; case 4: glBegin(GL_QUADS); glVertex3fv((GLfloat *)patch->vertex[0]->point); glVertex3fv((GLfloat *)patch->vertex[1]->point); glVertex3fv((GLfloat *)patch->vertex[2]->point); glVertex3fv((GLfloat *)patch->vertex[3]->point); glEnd(); break; default: glBegin(GL_POLYGON); for (i=0; inrvertices; i++) glVertex3fv((GLfloat *)patch->vertex[i]->point); glEnd(); } } void RenderPatchSmooth(PATCH *patch) { int i; switch (patch->nrvertices) { case 3: glBegin(GL_TRIANGLES); RenderSetColor(&patch->vertex[0]->color); glVertex3fv((GLfloat *)patch->vertex[0]->point); RenderSetColor(&patch->vertex[1]->color); glVertex3fv((GLfloat *)patch->vertex[1]->point); RenderSetColor(&patch->vertex[2]->color); glVertex3fv((GLfloat *)patch->vertex[2]->point); glEnd(); break; case 4: glBegin(GL_QUADS); RenderSetColor(&patch->vertex[0]->color); glVertex3fv((GLfloat *)patch->vertex[0]->point); RenderSetColor(&patch->vertex[1]->color); glVertex3fv((GLfloat *)patch->vertex[1]->point); RenderSetColor(&patch->vertex[2]->color); glVertex3fv((GLfloat *)patch->vertex[2]->point); RenderSetColor(&patch->vertex[3]->color); glVertex3fv((GLfloat *)patch->vertex[3]->point); glEnd(); break; default: glBegin(GL_POLYGON); for (i=0; inrvertices; i++) { RenderSetColor(&patch->vertex[i]->color); glVertex3fv((GLfloat *)patch->vertex[i]->point); } glEnd(); } } /* renders the patch outline in the current color */ void RenderPatchOutline(PATCH *patch) { int i; POINT tmp; VECTOR dir; glBegin(GL_LINE_LOOP); 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); glVertex3fv((GLfloat *)&tmp); } glEnd(); } void RenderPatch(PATCH *patch) { if(!renderopts.no_shading) { if (renderopts.smooth_shading) RenderPatchSmooth(patch); else RenderPatchFlat(patch); } if (renderopts.draw_outlines && (VECTORDOTPRODUCT(patch->normal, Camera.eyep) + patch->plane_constant > EPSILON || renderopts.use_display_lists)) { RenderSetColor(&renderopts.outline_color); RenderPatchOutline(patch); } } static void ReallyRenderOctreeLeaf(GEOM *geom, void (*render_patch)(PATCH *)) { PATCHLIST *patchlist = GeomPatchList(geom); ForAllPatches(P, patchlist) { render_patch(P); } EndForAll; } static void RenderOctreeLeaf(GEOM *geom, void (*render_patch)(PATCH *)) { if (renderopts.use_display_lists) { if (geom->dlistid <= 0) { geom->dlistid = geom->id; glNewList(geom->dlistid, GL_COMPILE_AND_EXECUTE); ReallyRenderOctreeLeaf(geom, render_patch); glEndList(); } else glCallList(geom->dlistid); } else ReallyRenderOctreeLeaf(geom, render_patch); } static int ViewCullBounds(float *bounds) { int i; for (i=0; i= 8) { Error("RenderOctreeNonLeaf", "Invalid octree geom node (more than 8 compound children)"); return; } octree_children[i++].geom = child; } else { /* render the patches associated with the octree node right away */ RenderOctreeLeaf(child, render_patch); } } EndForAll; n = i; /* nr of compound children */ /* cull the non-leaf octree children geoms */ for (i=0; ibounds)) { octree_children[i].geom = NULL; /* culled */ octree_children[i].dist = HUGE; } else { /* not culled, compute distance from eye to midpoint of child */ octree_children[i].dist = BoundsDistance2(Camera.eyep, octree_children[i].geom->bounds); } } #ifdef RENDER_UNSORTED /* render children geoms */ for (i=0; i 0) { /* find closest remaining child */ int closest = 0; for (i=1; idlistid >= 0) glDeleteLists(geom->dlistid, 1); geom->dlistid = -1; if (GeomIsAggregate(ClusteredWorldGeom)) { GEOMLIST *children = GeomPrimList(geom); ForAllGeoms(child, children) { GeomDeleteDLists(child); } EndForAll; } } static void RenderNewOctreeDisplayLists(void) { if (ClusteredWorldGeom) GeomDeleteDLists(ClusteredWorldGeom); } void RenderNewDisplayList(void) { if (dlistid >= 0) glDeleteLists(dlistid, 1); dlistid = -1; if (renderopts.frustum_culling) RenderNewOctreeDisplayLists(); /* renderopts.render_raytraced_image = FALSE; */ } void ReallyRender(void) { if (Radiance && Radiance->RenderScene) { Radiance->RenderScene(); } else if (renderopts.frustum_culling) { RenderWorldOctree(RenderPatch); } else { PatchListIterate(Patches, RenderPatch); } } void RenderRadiance(void) { if (renderopts.smooth_shading) glShadeModel(GL_SMOOTH); else glShadeModel(GL_FLAT); RenderSetCamera(); if (renderopts.backface_culling) glEnable(GL_CULL_FACE); else glDisable(GL_CULL_FACE); if (renderopts.use_display_lists && !renderopts.frustum_culling) { if (dlistid <= 0) { dlistid = 1; glNewList(dlistid, GL_COMPILE_AND_EXECUTE); /* render the scene */ ReallyRender(); glEndList(); } else glCallList(1); } else { /* just render the scene */ ReallyRender(); } if (renderopts.draw_bounding_boxes) RenderBoundingBoxHierarchy(); if (renderopts.draw_clusters) RenderClusterHierarchy(); if (renderopts.draw_cameras) RenderCameras(); /* RenderNormals(); */ } void RenderScene(void) { #ifdef TIMINGS clock_t t = clock(); #endif if (!ogl_inited) return; RenderSetLineWidth(renderopts.linewidth); CanvasPushMode(CANVASMODE_RENDER); if (Camera.changed) renderopts.render_raytraced_image = FALSE; if (doublebuf) glDrawBuffer(GL_BACK); if (tmopts.display_test_image) RenderGammaTestImage(tmopts.testimg); else if (!renderopts.render_raytraced_image || !RenderRayTraced()) RenderRadiance(); /* Call installed render hooks, that want to render something in the scene */ RenderHooks(); glFinish(); if (doublebuf) glXSwapBuffers(glx_display,glx_window); glDrawBuffer(GL_FRONT_AND_BACK); CanvasPullMode(); if (do_render_normals) RenderNormals(); #ifdef TIMINGS 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 } /* returns only after all issued graphics commands have been executed */ void RenderFinish(void) { glFinish(); } /* 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 width, int height, RGB *rgb) { int j, rowlen; GLubyte *c, *c1; /* length of one row of RGBA image data rounded up to a multiple of 8 */ rowlen = (4 * width * sizeof(GLubyte) + 7) & ~7; c1 = (GLubyte *)Alloc(height * rowlen + 8); c = c1 + ((((unsigned)c1 + 7) & (~7)) - (unsigned)c1); /* align to 8-byte boundary */ for (j=0; j=0; j--) { long i; unsigned char *pbuf = buf; GLubyte *pixel = &screen[j*x*4]; for (i=0; i=0; j--) { long i; unsigned char *pbuf = buf; GLfloat *pixel = &screen[j*x]; for (i=0; i= Camera.near) && (z <= Camera.far)) { glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); glOrtho(0.0, Camera.hres, 0.0, Camera.vres, -1.0, 1.0); glDisable(GL_DEPTH_TEST); circ(x, y, 3.); glEnable(GL_DEPTH_TEST); glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glPopMatrix(); } } #ifdef SOFT_ID_RENDERING #include "softids.h" unsigned long *RenderIds(long *x, long *y) { return SoftRenderIds(x, y); } #else /*SOFT_ID_RENDERING*/ /* Here's an attempt to make ID rendering work with <24 bit frame buffers, * but I believe it failed. To be investigated some time. */ static unsigned firstbit, nr, ng, nb, rmask, gmask, bmask, cr, cg, cb; static int nrpasses, pass; static void RenderPatchID(PATCH *patch) { unsigned red, green, blue; unsigned long id = (unsigned long)(patch->id) >> firstbit; int i; /* SwitchBackfaceCulling(patch); */ red = id & rmask; id >>= nr; green = id & gmask; id >>= ng; blue = id & bmask; glColor3ub(red< (%u,%u,%u) -> %lu\n", firstbit, (unsigned long)(patch->id), red<nrvertices) { case 3: glBegin(GL_TRIANGLES); glVertex3fv((GLfloat *)patch->vertex[0]->point); glVertex3fv((GLfloat *)patch->vertex[1]->point); glVertex3fv((GLfloat *)patch->vertex[2]->point); glEnd(); break; case 4: glBegin(GL_QUADS); glVertex3fv((GLfloat *)patch->vertex[0]->point); glVertex3fv((GLfloat *)patch->vertex[1]->point); glVertex3fv((GLfloat *)patch->vertex[2]->point); glVertex3fv((GLfloat *)patch->vertex[3]->point); glEnd(); break; default: glBegin(GL_POLYGON); for (i=0; inrvertices; i++) glVertex3fv((GLfloat *)patch->vertex[i]->point); glEnd(); } } static void RenderIdPass(GLubyte *screen, unsigned long *ids, int x, int y) { GLubyte *pixel; unsigned long *id; long i, nrpixels = x*y; RenderSetCamera(); glShadeModel(GL_FLAT); if (renderopts.backface_culling) glEnable(GL_CULL_FACE); else glDisable(GL_CULL_FACE); if (renderopts.frustum_culling) { int use_display_lists = renderopts.use_display_lists; renderopts.use_display_lists = FALSE; /* temporarily switch it off */ RenderWorldOctree(RenderPatchID); renderopts.use_display_lists = use_display_lists; } else PatchListIterate(Patches, RenderPatchID); glFinish(); if (doublebuf) glReadBuffer(GL_BACK); glReadPixels(0, 0, x, y, GL_RGBA, GL_UNSIGNED_BYTE, screen); for (i=0, pixel=screen, id=ids; i> cr, green=pixel[1] >> cg, blue=pixel[2] >> cb; (*id) |= ((((blue << ng) | green) << nr) | red) << firstbit; } } unsigned long *RenderIds(long *x, long *y) { unsigned long *ids = (unsigned long *)NULL; GLubyte *screen; GLint r, g, b; long maxpatchid = PatchGetNextID()-1; GLint current_draw_buffer = GL_FRONT; if (doublebuf) { glGetIntegerv(GL_DRAW_BUFFER, ¤t_draw_buffer); glDrawBuffer(GL_BACK); } glDisable(GL_DITHER); *x = Camera.hres; *y = Camera.vres; ids = (unsigned long *)Alloc((int)(*x) * (int)(*y) * sizeof(unsigned long)); memset(ids, 0, (int)(*x) * (int)(*y) * sizeof(unsigned long)); screen = (GLubyte *)Alloc((int)(*x) * (int)(*y) * sizeof(GLubyte) * 4); glGetIntegerv(GL_RED_BITS, &r); glGetIntegerv(GL_GREEN_BITS, &g); glGetIntegerv(GL_BLUE_BITS, &b); nr = r>8?8:r; ng = g>8?8:g; nb = b>8?8:b; cr = 8-nr, cg = 8-ng, cb = 8-nb; rmask = (1<viewdist * cam->tanhfov, vsiz = camlen * cam->viewdist * cam->tanvfov; int i, j, maxi = 12, maxj = (int)((float)maxi * vsiz/hsiz); VECTORCOMB2(1., cam->eyep, camlen * cam->viewdist, cam->Z, c); glDisable(GL_DEPTH_TEST); RenderSetColor(&renderopts.camera_color); for (i=0; i<=maxi; i++) { VECTORCOMB3(c, (-1. + 2. * ((float)i/(float)maxi)) * hsiz, cam->X, -vsiz, cam->Y, P); VECTORCOMB3(c, (-1. + 2. * ((float)i/(float)maxi)) * hsiz, cam->X, +vsiz, cam->Y, Q); RenderLine(&P, &Q); } for (j=0; j<=maxj; j++) { VECTORCOMB3(c, -hsiz, cam->X, (-1. + 2. * ((float)j/(float)maxj)) * vsiz, cam->Y, P); VECTORCOMB3(c, +hsiz, cam->X, (-1. + 2. * ((float)j/(float)maxj)) * vsiz, cam->Y, Q); RenderLine(&P, &Q); } VECTORCOMB3(c, hsiz, cam->X, -vsiz, cam->Y, Q); RenderLine(&cam->eyep, &Q); VECTORCOMB3(c, -hsiz, cam->X, -vsiz, cam->Y, Q); RenderLine(&cam->eyep, &Q); VECTORCOMB3(c, -hsiz, cam->X, vsiz, cam->Y, Q); RenderLine(&cam->eyep, &Q); VECTORCOMB3(c, hsiz, cam->X, vsiz, cam->Y, Q); RenderLine(&cam->eyep, &Q); glLineWidth(1); glEnable(GL_DEPTH_TEST); } extern CAMERA AlternateCamera; void RenderCameras(void) { RenderFrustum(&AlternateCamera); } void RenderBackground(CAMERA *cam) { /* turn of Z-buffer */ glDisable(GL_DEPTH_TEST); /* push matrix and create bogus cam */ glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); gluOrtho2D(0, cam->hres, 0, cam->vres); /* draw background (as texture) */ glEnable(GL_TEXTURE_2D); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL); glBindTexture(GL_TEXTURE_2D, backgroundTex); glBegin(GL_QUADS); glTexCoord2f(0.0, 1.0); glVertex3f(0 , 0 , 0.0); glTexCoord2f(1.0, 1.0); glVertex3f(cam->hres, 0 , 0.0); glTexCoord2f(1.0, 0.0); glVertex3f(cam->hres, cam->vres, 0.0); glTexCoord2f(0.0, 0.0); glVertex3f(0 , cam->vres, 0.0); glEnd(); glDisable(GL_TEXTURE_2D); /* pop matrix */ glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glPopMatrix(); /* enable Z-buffer back */ glEnable(GL_DEPTH_TEST); } void skipComment(FILE *fp) { char b = fgetc(fp); while(b == '#') { while (b != '\n') b = fgetc(fp); b = fgetc(fp); } ungetc(b, fp); } int InitBackground(FILE *fp) { int width, height; int width2, height2; int colorsize, r; float pow1, pow2; long mem_needed; void *map; /* load image (ppm) */ if (fp) { /* read header (P6) */ fscanf(fp, "P6\n"); /* skip comment (#) */ skipComment(fp); /* read dimensions */ r = fscanf(fp, "%d %d\n%d\n", &width, &height, &colorsize); if (r != 3) { Error(NULL, "Not a vallid PPM file"); return FALSE; } /* alloc memory */ mem_needed = width * height * 3; map = malloc(mem_needed); /* load map */ r = fread(map, 1, mem_needed, fp); if (r != mem_needed) { free(map); Error(NULL, "Not a vallid PPM file"); return FALSE; } /* convert to openGL convenient format */ pow1 = ceil(log((float)(width)) / log(2.)); pow2 = ceil(log((float)(height)) / log(2.)); if(pow1 > 10.) pow1 = 10.; if(pow2 > 10.) pow1 = 10.; width2 = (int)(pow(2, pow1)); height2 = (int)(pow(2,pow2)); mem_needed = width2 * height2 * 3; if(background_ptr) free(background_ptr); background_ptr = malloc(mem_needed); glPixelStorei(GL_PACK_ROW_LENGTH, 0); glPixelStorei(GL_PACK_SKIP_ROWS, 0); glPixelStorei(GL_PACK_SKIP_PIXELS, 0); gluScaleImage(GL_RGB, width , height , GL_UNSIGNED_BYTE, map, width2, height2, GL_UNSIGNED_BYTE, (void *)(background_ptr)); /* free load map */ free(map); /* register as texture */ glGenTextures(1, &backgroundTex); glBindTexture(GL_TEXTURE_2D, backgroundTex); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width2, height2, 0, GL_RGB, GL_UNSIGNED_BYTE, background_ptr); return TRUE; } else return FALSE; }