/****************************************************************************** * $id: mapoglrenderer.cpp 7725 2011-04-09 15:56:58Z toby $ * * Project: MapServer * Purpose: Various template processing functions. * Author: Steve Lime and the MapServer team. * ****************************************************************************** * Copyright (c) 1996-2008 Regents of the University of Minnesota. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies of this Software or works derived from this Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. ****************************************************************************/ #ifdef USE_OGL #include #include #include "mapserver.h" #include "maperror.h" #include "mapoglrenderer.h" MS_CVSID("$Id$") using namespace std; double OglRenderer::SIZE_RES = 100.0 / 72.0; ms_uint32 OglRenderer::OUTLINE_WIDTH = 2; ms_uint32 OglRenderer::FONT_SIZE = 20; ms_uint32 OglRenderer::FONT_RES = 200; double OglRenderer::OGL_PI = 3.14159265; ms_uint32 OglRenderer::SHAPE_CIRCLE_RES = 10; double OglRenderer::SHAPE_CIRCLE_RADIUS = 10.0; OglRenderer::fontCache_t OglRenderer::fontCache = OglRenderer::fontCache_t(); OglRenderer::dashCache_t OglRenderer::dashCache = OglRenderer::dashCache_t(); std::vector OglRenderer::shapes = std::vector(); GLvoid CALLBACK beginCallback(GLenum which); GLvoid CALLBACK errorCallback(GLenum errorCode); GLvoid CALLBACK endCallback(void); GLvoid CALLBACK combineDataCallback(GLdouble coords[3], GLdouble* vertex_data[4], GLfloat weight[4], void** dataOut, void* polygon_data); GLvoid CALLBACK vertexCallback(GLdouble *vertex); OglRenderer::OglRenderer(ms_uint32 width, ms_uint32 height, colorObj* color) : width(width), height(height), texture(NULL), transparency(1.0), valid(false) { context = new OglContext(width, height); if (!context->isValid()) return; if (!context->makeCurrent()) return; int viewPort[4]; glGetIntegerv(GL_VIEWPORT ,viewPort); viewportX = viewPort[0]; viewportY = viewPort[1]; viewportWidth = viewPort[2]; viewportHeight = viewPort[3]; glPushMatrix(); // set read/write context to pbuffer glEnable(GL_MULTISAMPLE_ARB); glHint(GL_MULTISAMPLE_FILTER_HINT_NV, GL_NICEST); glEnable(GL_TEXTURE_2D); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE ); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); glDisable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); // draw & read the front buffer of pbuffer glDrawBuffer(GL_FRONT); glReadBuffer(GL_FRONT); if (!(tess = gluNewTess())) { msSetError(MS_OGLERR, "P-buffer Error: Unable to create tessalotor", "OglRenderer"); return; } gluTessCallback(tess, GLU_TESS_VERTEX, (void (CALLBACK*)(void)) vertexCallback); gluTessCallback(tess, GLU_TESS_BEGIN, (void(CALLBACK*)(void)) beginCallback); gluTessCallback(tess, GLU_TESS_END, (void(CALLBACK*)(void)) endCallback); gluTessCallback(tess, GLU_TESS_ERROR, (void(CALLBACK*)(void)) errorCallback); gluTessCallback(tess, GLU_TESS_COMBINE_DATA, (void(CALLBACK*)(void)) combineDataCallback); if (color) { glClearColor((double) color->red / 255, (double) color->green / 255, (double) color->blue / 255, (double)color->alpha / 100); } else { glClearColor(0.0, 0.0, 0.0, 0.0); } glClear(GL_COLOR_BUFFER_BIT); glViewport(0, 0, width, height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0.0, width, 0.0, height, -3.0, 3.0); createShapes(); valid = true; } OglRenderer::~OglRenderer() { makeCurrent(); glViewport(viewportX, viewportY, viewportWidth, viewportHeight); glPopMatrix(); delete context; } OglCachePtr OglRenderer::getTexture() { if (!texture) { makeCurrent(); texture = new OglCache(); texture->texture = createTexture(0,0);//TEXTURE_BORDER/2, TEXTURE_BORDER/2); texture->width = this->width; texture->height = this->height; } return texture; } GLuint OglRenderer::createTexture(ms_uint32 offsetX, ms_uint32 offsetY) { GLuint texture = 0; glGenTextures(1, &texture); glBindTexture(GL_TEXTURE_2D, texture); 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_GENERATE_MIPMAP, GL_TRUE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_NEAREST); if (OglContext::MAX_ANISOTROPY != 0) { glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, OglContext::MAX_ANISOTROPY); } glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, offsetX, offsetY, context->getWidth(), context->getHeight(), 0); glBindTexture(GL_TEXTURE_2D,0); return texture; } bool OglRenderer::getStringBBox(char *font, double size, char *string, rectObj *rect, double** advances) { FTFont* face = getFTFont(font, size); if (!face) { msSetError(MS_OGLERR, "Failed to load font (%s).", "OglRenderer::getStringBBox()", font); return false; } float llx =0.0f, lly=0.0f, llz=0.0f, urx=0.0f, ury=0.0f, urz=0.0f; glPushAttrib( GL_ALL_ATTRIB_BITS ); FTBBox boundingBox = face->BBox(string); glPopAttrib(); rect->minx = boundingBox.Lower().X(); rect->maxx = boundingBox.Upper().X(); rect->miny = -boundingBox.Upper().Y(); rect->maxy = -boundingBox.Lower().Y(); if (advances) { int length = strlen(string); *advances = new double[length]; for (int i = 0; i < length; ++i) { (*advances)[i] = face->Advance(&string[i], 1); } } return true; } void OglRenderer::initializeRasterBuffer(rasterBufferObj * rb, int width, int height, bool useAlpha) { unsigned char* buffer = new unsigned char[4 * width * height]; rb->data.rgba.pixels = buffer; rb->data.rgba.r = &buffer[2]; rb->data.rgba.g = &buffer[1]; rb->data.rgba.b = &buffer[0]; if (useAlpha) rb->data.rgba.a = &buffer[3]; rb->type =MS_BUFFER_BYTE_RGBA; rb->data.rgba.row_step = 4*width; rb->data.rgba.pixel_step = 4; rb->width = width; rb->height = height; } void OglRenderer::readRasterBuffer(rasterBufferObj * rb) { makeCurrent(); unsigned char* buffer = new unsigned char[4 * width * height]; glReadPixels(0, 0, width, height, GL_BGRA, GL_UNSIGNED_BYTE, buffer); rb->type =MS_BUFFER_BYTE_RGBA; rb->data.rgba.pixels = buffer; rb->data.rgba.row_step = 4*width; rb->data.rgba.pixel_step = 4; rb->width = width; rb->height = height; rb->data.rgba.r = &buffer[2]; rb->data.rgba.g = &buffer[1]; rb->data.rgba.b = &buffer[0]; rb->data.rgba.a = NULL; } void OglRenderer::drawRasterBuffer(rasterBufferObj *overlay, double opacity, int srcX, int srcY, int dstX, int dstY, int width, int height) { // todo to support all alpha and srcx/y this needs to be done by creating a texture makeCurrent(); glDrawPixels(width, height, GL_BGRA, GL_UNSIGNED_BYTE, overlay->data.rgba.pixels); } void OglRenderer::setTransparency(double transparency) { this->transparency = transparency; } void OglRenderer::setColor(colorObj *color) { if (color && MS_VALID_COLOR(*color)) { glColor4d((double) color->red / 255, (double) color->green / 255, (double) color->blue / 255, transparency); } else { glColor4d(0.0, 0.0, 0.0, 1.0); } } void OglRenderer::drawVectorLineStrip(symbolObj *symbol, double width) { glPushMatrix(); glLineWidth(width); glBegin(GL_LINE_STRIP); for (int i = 0; i < symbol->numpoints; i++) { if (symbol->points[i].x < 0 && symbol->points[i].y < 0) { glEnd(); glBegin(GL_LINE_STRIP); } else { glVertex2d(symbol->points[i].x, symbol->points[i].y); } } glEnd(); glPopMatrix(); } double OglRenderer::drawTriangles(pointObj* p1, pointObj* p2, double width, double tilelength, double textureStart) { double dx = p1->x - p2->x; double dy = p1->y - p2->y; double dist = sqrt(dx * dx + dy * dy); double theta; if (dx != 0) theta = atan(dy / dx); else theta = (dy > 0 ? 1 : -1) * OGL_PI / 2; double xw = sin(theta) * width / 2; double yw = cos(theta) * width / 2; double textureEnd = dist / tilelength; glTexCoord2d(textureStart, 1); glVertex2d(p1->x + xw, p1->y - yw); // Bottom Left Of The Texture and Quad glTexCoord2d(textureStart, 0); glVertex2d(p1->x - xw, p1->y + yw); // Bottom Right Of The Texture and Quad glTexCoord2d(textureStart + textureEnd, 1); glVertex2d(p2->x + xw, p2->y - yw); // Top Right Of The Texture and Quad glTexCoord2d(textureStart + textureEnd, 0); glVertex2d(p2->x - xw, p2->y + yw); // Top Left Of The Texture and Quad return dist; } double OglRenderer::drawQuad(pointObj* p1, pointObj* p2, double width, double tilelength, double textureStart) { double dx = p1->x - p2->x; double dy = p1->y - p2->y; double dist = sqrt(dx * dx + dy * dy); double theta; if (dx) theta = atan(dy / dx); else theta = (dy > 0 ? 1 : -1) * OGL_PI / 2; glPushMatrix(); glTranslated(p1->x, p1->y, 0); glRotated((p1->x < p2->x ? 0 : 180) + theta * (180 / OGL_PI), 0, 0, 1); glTranslated(-p1->x, -p1->y, 0); glBegin(GL_QUADS); glTexCoord2d(textureStart, 1); glVertex2d(p1->x, p1->y - width / 2); // Bottom Left Of The Texture and Quad glTexCoord2d(textureStart + (dist / tilelength), 1); glVertex2d(p1->x + dist, p1->y - width / 2); // Bottom Right Of The Texture and Quad glTexCoord2d(textureStart + (dist / tilelength), 0); glVertex2d(p1->x + dist, p1->y + width / 2); // Top Right Of The Texture and Quad glTexCoord2d(textureStart, 0); glVertex2d(p1->x, p1->y + width / 2); // Top Left Of The Texture and Quad glEnd(); glPopMatrix(); return dist; } void OglRenderer::renderTile(const OglCachePtr& tile, double x, double y, double angle) { makeCurrent(); glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE_ARB); glBindTexture(GL_TEXTURE_2D, tile->texture); glPushMatrix(); glTranslated(x, y, 0); if (angle) { glRotated(angle, 0, 0, 1); } glTranslated(-x, -y, 0); glBegin(GL_QUADS); glTexCoord2d(0, 1); glVertex2d( x-(tile->width/2), y+(tile->height/2)); // Bottom Left Of The Texture and Quad glTexCoord2d(1, 1); glVertex2d( x+(tile->width/2), y+(tile->height/2)); // Bottom Right Of The Texture and Quad glTexCoord2d(1, 0); glVertex2d( x+(tile->width/2), y-(tile->height/2)); // Top Right Of The Texture and Quad glTexCoord2d(0, 0); glVertex2d( x-(tile->width/2), y-(tile->height/2)); // Top Left Of The Texture and Quad glEnd(); glPopMatrix(); glBindTexture(GL_TEXTURE_2D, 0); glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE_ARB); } void OglRenderer::renderPolylineTile(shapeObj *shape, const OglCachePtr& tile){ makeCurrent(); glBindTexture(GL_TEXTURE_2D, tile->texture); // Select Our Texture glBegin(GL_TRIANGLE_STRIP); double place = 0.0; for (int i = 0; i < shape->numlines; i++) { for (int j = 0; j < shape->line[i].numpoints - 1; j++) { double dist = drawTriangles(&shape->line[i].point[j], &shape->line[i].point[j + 1], tile->height, tile->width, place / tile->width); place = (place + dist); while (place >= tile->width) place -= tile->width; } } glEnd(); glBindTexture(GL_TEXTURE_2D, 0); } void OglRenderer::renderVectorSymbol(double x, double y, symbolObj *symbol, double scale, double angle, colorObj *c, colorObj *oc, double ow) { makeCurrent(); glPushMatrix(); glTranslated(x, y, 0); glRotated(angle, 0, 0, 1); glScaled(scale, scale, 0); glTranslated(-symbol->sizex/2, -symbol->sizey/2, 0.0); if (oc != NULL && MS_VALID_COLOR(*oc) && ow > 0) { setColor(oc); drawVectorLineStrip(symbol, ow); } if (c != NULL && MS_VALID_COLOR(*c) && ow > 0) { setColor(c); drawVectorLineStrip(symbol, ow); } glPopMatrix(); } void OglRenderer::renderPolyline(shapeObj *p, colorObj *c, double width, int patternlength, double* pattern, int lineCap, int joinStyle, colorObj *outlinecolor, double outlinewidth) { makeCurrent(); if (outlinecolor) { glEnable(GL_DEPTH_TEST); glPushMatrix(); glTranslated(0, 0, 1); } setColor(c); if (patternlength > 0) { if (p->renderer_cache == NULL) { loadLine(p, width, patternlength, pattern); } /* FIXME */ if (p->renderer_cache == NULL) return; OglCache* cache = (OglCache*) p->renderer_cache; GLuint texture = cache->texture; if (cache->patternDistance > 0) { glBindTexture(GL_TEXTURE_2D, texture); double place = 0; glBegin(GL_TRIANGLE_STRIP); for (int j = 0; j < p->numlines; j++) { for (int i = 0; i < p->line[j].numpoints - 1; i++) { double dist = drawTriangles(&p->line[j].point[i], &p->line[j].point[i + 1], width, cache->patternDistance, place / cache->patternDistance); place = (place + dist); while (place >= cache->patternDistance) place -= cache->patternDistance; // better than mod, no rounding } } glEnd(); glBindTexture(GL_TEXTURE_2D, 0); } } else { float widthRange[2]; glGetFloatv(GL_LINE_WIDTH_RANGE, widthRange); bool quads = !(width >= widthRange[0] && width <= widthRange[1]); double circleScale = width / SHAPE_CIRCLE_RADIUS / 2; glLineWidth(width); if (quads || (width > 1.0 && (lineCap == MS_CJC_ROUND || joinStyle == MS_CJC_ROUND))) { for (int j = 0; j < p->numlines; j++) { for (int i = 0; i < p->line[j].numpoints; i++) { if (i != p->line[j].numpoints - 1) { if (quads) { drawQuad(&p->line[j].point[i], &p->line[j].point[i + 1], width); } else { glBegin(GL_LINES); glVertex2f(p->line[j].point[i].x, p->line[j].point[i].y); glVertex2f(p->line[j].point[i + 1].x,p->line[j].point[i + 1].y); glEnd(); } } if ((lineCap == MS_CJC_ROUND && (i == 0 || i == p->line[j].numpoints - 1)) || (joinStyle == MS_CJC_ROUND && (i > 0 && i < p->line[j].numpoints - 1))) { glPushMatrix(); glTranslated(p->line[j].point[i].x, p->line[j].point[i].y, 0); glScaled(circleScale, circleScale, 0); glCallList(shapes[circle]); glPopMatrix(); } } } } else { for (int j = 0; j < p->numlines; j++) { glBegin(GL_LINE_STRIP); for (int i = 0; i < p->line[j].numpoints; i++) { glVertex2f(p->line[j].point[i].x, p->line[j].point[i].y); } glEnd(); } } } if (outlinecolor != NULL && MS_VALID_COLOR(*outlinecolor)) { glPopMatrix(); double owidth = MS_MAX(width + OUTLINE_WIDTH, outlinewidth); glPushMatrix(); glTranslated(0, 0, -1); renderPolyline(p, outlinecolor, owidth, patternlength, pattern, lineCap, joinStyle); glPopMatrix(); glDisable(GL_DEPTH_TEST); } } void OglRenderer::renderPolygon(shapeObj *p, colorObj *color, colorObj *outlinecolor, double outlinewidth, const OglCachePtr& tile, int lineCap, int joinStyle) { /* OpenGL cannot draw complex polygons so we need to use a Tessallator to draw the polygon using a GL_TRIANGLE_FAN ISSUE: There's a problem here. It would seem that changing the dimensions or bounding box area breaks the code. Reports need for a combine callback. */ makeCurrent(); std::vector pointers; double texWidth = 0; double texHeight = 0; if (tile) { glBindTexture(GL_TEXTURE_2D, tile->texture); // Select Our Texture texWidth = tile->width; texHeight = tile->height; } setColor(color); gluTessBeginPolygon(tess, NULL ); for (int j = 0; j < p->numlines; j++) { gluTessBeginContour(tess); for (int i = 0; i < p->line[j].numpoints; i++) { // create temp array and place GLdouble* dbls = new GLdouble[5]; pointers.push_back(dbls); dbls[0] = p->line[j].point[i].x; dbls[1] = p->line[j].point[i].y; dbls[2] = 0.0; dbls[3] = texWidth; dbls[4] = texHeight; gluTessVertex(tess, dbls, dbls); } gluTessEndContour(tess); } gluTessEndPolygon(tess); // destroy temp arrays for (std::vector::iterator iter = pointers.begin(); iter != pointers.end(); iter++) { delete[] *iter; } glBindTexture(GL_TEXTURE_2D, 0); // Select Our Texture if (outlinecolor != NULL && MS_VALID_COLOR(*outlinecolor) && outlinewidth > 0) { renderPolyline(p, outlinecolor, outlinewidth, 0, NULL, lineCap, joinStyle); } } void OglRenderer::renderGlyphs(double x, double y, colorObj *color, colorObj *outlinecolor, double size, const char* font, char *thechars, double angle, colorObj *shadowcolor, double shdx, double shdy) { makeCurrent(); FTFont* face = getFTFont(font, size); if (!face) { msSetError(MS_OGLERR, "Failed to load font (%s).", "OglRenderer::renderGlyphs()", font); return; } glPushAttrib(GL_ALL_ATTRIB_BITS); glPushMatrix(); glTranslated(floor(x), floor(y), 0); glScaled(1.0, -1.0, 1); glRotated(angle * (180 / OGL_PI), 0, 0, 1); if (outlinecolor && MS_VALID_COLOR(*outlinecolor)) { setColor(outlinecolor); for (int i = -1; i <= 1; i++) { for (int j = -1; j <= 1; j++) { if (i || j) { glPushMatrix(); glTranslated(i, j, 0); face->Render(thechars); glPopMatrix(); } } } } if (color != NULL && MS_VALID_COLOR(*color)) { setColor(color); face->Render(thechars); } glPopMatrix(); glPopAttrib(); } void OglRenderer::renderPixmap(symbolObj *symbol, double x, double y, double angle, double scale) { makeCurrent(); float zoomX, zoomY; glGetFloatv(GL_ZOOM_X, &zoomX); glGetFloatv(GL_ZOOM_Y, &zoomY); GLubyte* imgdata = symbol->pixmap_buffer->data.rgba.pixels; glPixelZoom(zoomX*scale, zoomY*scale); glRasterPos2d(x-symbol->pixmap_buffer->width/2, y-symbol->pixmap_buffer->height/2); glDrawPixels(symbol->pixmap_buffer->width, symbol->pixmap_buffer->height, GL_BGRA, GL_UNSIGNED_BYTE, imgdata); glPixelZoom(zoomX, zoomY); } void OglRenderer::renderEllipse(double x, double y, double angle, double width, double height, colorObj *color, colorObj *outlinecolor, double outlinewidth = 1.0) { makeCurrent(); if (outlinecolor && MS_VALID_COLOR(*outlinecolor)) { glEnable(GL_DEPTH_TEST); glPushMatrix(); glTranslated(0, 0, 1); } setColor(color); glPushMatrix(); if (angle) { glRotated(angle, 0, 0, 1); } glTranslated(x, y, 0); glScaled(width / SHAPE_CIRCLE_RADIUS / 2, height / SHAPE_CIRCLE_RADIUS / 2, 1); glCallList(shapes[circle]); glPopMatrix(); if (outlinecolor != NULL && MS_VALID_COLOR(*outlinecolor)) { glPopMatrix(); glPushMatrix(); glTranslated(0, 0, -1); renderEllipse(x, y, angle, width + outlinewidth, height, outlinecolor, NULL, 0); glPopMatrix(); glDisable(GL_DEPTH_TEST); } } bool OglRenderer::loadLine(shapeObj* shape, double width, int patternlength, double* pattern) { /* OglCache* cache = (OglCache*) shape->renderer_cache; if (cache->patternDistance == 0) { for (int i = 0; i < patternlength; i++) { cache->patternDistance += pattern[i]; } } GLubyte imgdata[TEX_SIZE * TEX_SIZE * 4]; int pow2length = min(max(NextPowerOf2(cache->patternDistance), MIN_TEX_SIZE), TEX_SIZE); int pow2width = min(max(NextPowerOf2(width), MIN_TEX_SIZE), TEX_SIZE); double lengthScale = (double) pow2length / cache->patternDistance; glColor3d(0, 0, 0); double place = 0; double xRadii = width / 2 * lengthScale; double yRadii = pow2width / 2; for (int i = 0; i < patternlength; i++) { double next = pattern[i] * lengthScale; if (i % 2 == 0) { glPushMatrix(); glTranslated(place + xRadii, yRadii, 0); glScaled(xRadii / SHAPE_CIRCLE_RADIUS, yRadii / SHAPE_CIRCLE_RADIUS, 0); glCallList(shapes[circle]); glPopMatrix(); glBegin(GL_QUADS); glVertex2d(place + pow2width / 2, 0); glVertex2d(place + next - pow2width / 2, 0); glVertex2d(place + next - pow2width / 2, pow2width); glVertex2d(place + pow2width / 2, pow2width); glEnd(); glPushMatrix(); glTranslated(place + next - xRadii, yRadii, 0); glScaled(xRadii / SHAPE_CIRCLE_RADIUS, yRadii / SHAPE_CIRCLE_RADIUS, 0); glCallList(shapes[circle]); glPopMatrix(); } place += next; } glReadPixels(0, 0, pow2length, pow2width, GL_RGBA, GL_UNSIGNED_BYTE, imgdata); //createTexture(pow2length, pow2width, GL_ALPHA, imgdata);*/ return true; } FTFont* OglRenderer::getFTFont(const char* font, double size) { FTFont** face = &fontCache[font][size]; if (*face == NULL && ifstream(font)) { *face = new FTGLTextureFont( font ); if (*face) { (*face)->UseDisplayList(true); (*face)->FaceSize(size*SIZE_RES); } } return *face; } GLvoid CALLBACK beginCallback(GLenum which) { glBegin(which); } GLvoid CALLBACK errorCallback(GLenum errorCode) { const GLubyte *estring; estring = gluErrorString(errorCode); fprintf(stderr, "Tessellation Error: %d %s\n", errorCode, estring); exit(0); } GLvoid CALLBACK endCallback(void) { glEnd(); } GLvoid CALLBACK combineDataCallback(GLdouble coords[3], GLdouble* vertex_data[4], GLfloat weight[4], void** dataOut, void* polygon_data) { GLdouble *vertex; vertex = (GLdouble *) malloc(5 * sizeof(GLdouble)); vertex[0] = coords[0]; vertex[1] = coords[1]; vertex[2] = coords[2]; vertex[3] = vertex_data[0][3]; vertex[4] = vertex_data[0][4]; *dataOut = vertex; } GLvoid CALLBACK vertexCallback(GLdouble *vertex) { if (vertex[3] > 0) glTexCoord2d(vertex[0]/vertex[3], vertex[1]/vertex[4]); glVertex3dv(vertex); } void OglRenderer::createShapes() { double da = MS_MIN(OGL_PI / 2, OGL_PI / SHAPE_CIRCLE_RES); GLuint circle = glGenLists(1); glNewList(circle, GL_COMPILE); glBegin(GL_TRIANGLE_FAN); glVertex2d(0, 0); for (double a = 0.0; a <= 2 * OGL_PI; a += da) { glVertex2d(0 + cos(a) * SHAPE_CIRCLE_RADIUS, 0 + sin(a) * SHAPE_CIRCLE_RADIUS); } glVertex2d(0 + SHAPE_CIRCLE_RADIUS, 0); glEnd(); glEndList(); shapes.push_back(circle); } void OglRenderer::makeCurrent() { context->makeCurrent(); } #endif