// // Vrml 97 library // Copyright (C) 1998 Chris Morley // // %W% %G% // ViewerOpenGL.cpp // Display of VRML models using OpenGL. This is an abstract class // with no window system dependencies. // #include "config.h" #if HAVE_GL #include "ViewerOpenGL.h" #include #include #include // sprintf #include "MathUtils.h" #include "OpenGLEvent.h" #include "System.h" #include "VrmlScene.h" #include "VrmlNodeNavigationInfo.h" // Put geometry into display lists. // If geometry is not in display lists, performance will suffer, // especially for non-convex geometries. You probably shouldn't // change these unless you know what you are doing. #define USE_GEOMETRY_DISPLAY_LISTS 1 // Textures are now done using OGL1.1 bindTexture API rather than // display lists when this flag is set. Don't define this if you // are still at OpenGL 1.0 (or get a newer OpenGL). #define USE_TEXTURE_DISPLAY_LISTS 1 // Construct a viewer for the specified scene. I'm not happy with the // mutual dependencies between VrmlScene/VrmlNodes and Viewers... // Maybe a static callback function pointer should be passed in rather // than a class object pointer. Currently, the scene is used to access // the VrmlScene::render() method. Also, the static VrmlScene::update // is called from the idle function. A way to pass mouse/keyboard sensor // events back to the scene is also needed. ViewerOpenGL::ViewerOpenGL(VrmlScene *scene) : Viewer(scene) { d_GLinitialized = false; d_blend = true; d_lit = true; d_texture = true; d_wireframe = false; // Don't make any GL calls here since the window probably doesn't exist. d_nObjects = 0; d_nestedObjects = 0; d_nSensitive = 0; d_overSensitive = 0; d_activeSensitive = 0; d_selectMode = false; d_selectZ = 0.0; d_background[0] = d_background[1] = d_background[2] = 0.0; d_winWidth = 1; d_winHeight = 1; for (int i=0; ierror("GL ERROR: %s %s\n", s, gluErrorString(glerr)); } // // beginObject/endObject should correspond to grouping nodes. // Group-level scoping for directional lights, anchors, sensors // are handled here. Display lists can optionally be created // (but the retain flag is just a hint, not guaranteed). Retained // objects can be referred to later to avoid duplicating geometry. // OpenGL doesn't allow nested objects. The top-down approach of // putting entire groups in display lists is faster for static // scenes but uses more memory and means that if anything is changed, // the whole object must be tossed. // The bottom-up model wraps each piece of geometry in a dlist but // requires traversal of the entire scene graph to reference each dlist. // The decision about what groups to stuff in an object is punted to // the object itself, as it can decide whether it is mutable. // // The OpenGL viewer never puts objects in display lists, so the // retain hint is ignored. Viewer::Object ViewerOpenGL::beginObject(const char *, bool /* retain */ ) { // Finish setup stuff before first object if (1 == ++d_nObjects) { // Finish specifying the view (postponed to make Background easier) glMultMatrixf(&d_rotationMatrix[0][0]); glTranslatef(d_translatex, d_translatey, d_translatez); glPushMatrix(); } ++d_nestedObjects; // Increment nesting level for group-scoped lights for (int i=0; i 0 || nGroundAngles > 0 || pixels) ) { glid = glGenLists(1); glNewList( glid, GL_COMPILE_AND_EXECUTE ); } #endif glClearColor( r, g, b, a ); GLuint mask = GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT; #if USE_STENCIL_SHAPE mask |= GL_STENCIL_BUFFER_BIT; glClear( mask ); #else glClear( mask ); // Draw the background as big spheres centered at the view position if ( ! d_selectMode && (nSkyAngles > 0 || nGroundAngles > 0 || pixels) ) { glDisable( GL_DEPTH_TEST ); glDisable( GL_LIGHTING ); glPushMatrix(); glLoadIdentity(); // Undo translation glTranslatef( d_position[0], d_position[1], d_position[2] ); // Apply current view rotation glMultMatrixf( &d_rotationMatrix[0][0] ); glScalef(1000.,1000.,1000.); // Sphere constants const int nCirc = 8; // number of circumferential slices const double cd = 2.0 * M_PI / nCirc; double heightAngle0, heightAngle1 = 0.0; float *c0, *c1 = skyColor; for (int nSky=0; nSky= 0; --i) { glVertex3fv( &c[i][0] ); } glVertex3fv( &c[nfacets-1][0] ); glEnd(); } } endGeometry(); if (glid) glEndList(); return (Object) glid; } // Compute a normal at vert i,j of an ElevationGrid. static void elevationVertexNormal(int i, int j, int nx, int nz, float dx, float dz, float *height, float N[]) { float Vx[3], Vz[3]; if (i > 0 && i < nx-1) { Vx[0] = 2.0f * dx; Vx[1] = *(height+1) - *(height-1); } else if (i == 0) { Vx[0] = dx; Vx[1] = *(height+1) - *(height); } else { Vx[0] = dx; Vx[1] = *(height) - *(height-1); } Vx[2] = 0.0; if (j > 0 && j < nz-1) { Vz[0] = 2.0f * dz; Vz[1] = *(height+nx) - *(height-nx); } else if (j == 0) { Vz[0] = dz; Vz[1] = *(height+nx) - *(height); } else { Vz[0] = dz; Vz[1] = *(height) - *(height-nx); } Vz[2] = 0.0; Vcross( N, Vx, Vz ); } Viewer::Object ViewerOpenGL::insertElevationGrid(unsigned int mask, int nx, int nz, float *height, float dx, float dz, float *texture_coords, float *normals, float *colors) { int i, j; float x, z; GLuint glid = 0; #if USE_GEOMETRY_DISPLAY_LISTS if (! d_selectMode) { glid = glGenLists(1); glNewList( glid, GL_COMPILE_AND_EXECUTE ); } #endif beginGeometry(); // Face orientation & culling glFrontFace( (mask & MASK_CCW) ? GL_CCW : GL_CW ); if (! (mask & MASK_SOLID) ) glDisable( GL_CULL_FACE ); // x varies fastest for (j=0; jN[0] ); } static void WINAPI tessExtrusionVertex( void *vdata, void *pdata ) { size_t j = (size_t)vdata; TessExtrusion *p = (TessExtrusion *)pdata; glTexCoord2f( (p->crossSection[2*j] - p->tcDeltaU) * p->tcScaleU, (p->crossSection[2*j+1] - p->tcDeltaV) * p->tcScaleV ); glVertex3fv( &(p->c[3 * (j + p->vOffset)]) ); } #endif void ViewerOpenGL::insertExtrusionCaps( unsigned int mask, int nSpine, float *c, int nCrossSection, float *cs ) { // Determine x,z ranges for top & bottom tex coords float xz[4] = { cs[0], cs[0], cs[1], cs[1] }; float *csp = cs; for (int nn=1; nn xz[1]) xz[1] = csp[0]; if (csp[1] < xz[2]) xz[2] = csp[1]; else if (csp[1] > xz[3]) xz[3] = csp[1]; } float dx = xz[1] - xz[0]; float dz = xz[3] - xz[2]; if (! FPZERO(dx)) dx = 1.0f / dx; if (! FPZERO(dz)) dz = 1.0f / dz; // If geometry is in dlists, should just always use the tesselator... #if GLU_VERSION_1_2 int last = 2*(nCrossSection-1); bool equalEndpts = FPEQUAL(cs[0], cs[last]) && FPEQUAL(cs[1], cs[last+1]); if (! (mask & MASK_CONVEX)) { if (! d_tess) d_tess = gluNewTess(); gluTessCallback( d_tess, (GLenum) GLU_TESS_BEGIN_DATA, (TessCB) tessExtrusionBegin ); gluTessCallback( d_tess, (GLenum) GLU_TESS_VERTEX_DATA, (TessCB) tessExtrusionVertex ); gluTessCallback( d_tess, (GLenum) GLU_TESS_END, (TessCB) glEnd ); if (mask & MASK_BOTTOM) { TessExtrusion bottom = { c, cs, xz[0], xz[2], dx, dz, 0 }; indexFaceNormal( 0, 1, 2, c, bottom.N ); gluTessBeginPolygon( d_tess, &bottom ); gluTessBeginContour( d_tess ); GLdouble v[3]; // Mesa tesselator doesn;t like closed polys size_t j = equalEndpts ? nCrossSection-2 : nCrossSection-1; for ( ; j>=0; --j) { v[0] = c[3*j]; v[1] = c[3*j+1]; v[2] = c[3*j+2]; gluTessVertex( d_tess, v, (void*)j ); } gluTessEndContour( d_tess ); gluTessEndPolygon( d_tess ); } if (mask & MASK_TOP) { int n = (nSpine - 1) * nCrossSection; TessExtrusion top = { c, cs, xz[0], xz[2], dx, dz, n }; indexFaceNormal( 3*n+2, 3*n+1, 3*n, c, top.N ); gluTessBeginPolygon( d_tess, &top ); gluTessBeginContour( d_tess ); GLdouble v[3]; // Mesa tesselator doesn;t like closed polys size_t j = equalEndpts ? 1 : 0; for ( ; j < nCrossSection; ++j) { v[0] = c[3*(j+n)]; v[1] = c[3*(j+n)+1]; v[2] = c[3*(j+n)+2]; gluTessVertex( d_tess, v, (void*)j ); } gluTessEndContour( d_tess ); gluTessEndPolygon( d_tess ); } } else #endif // Convex (or not GLU1.2 ...) { float N[3]; // Normal if (mask & MASK_BOTTOM) { glBegin( GL_POLYGON ); indexFaceNormal( 0, 1, 2, c, N ); glNormal3fv( N ); for (int j = nCrossSection-1; j>=0; --j) { glTexCoord2f( (cs[2*j]-xz[0])*dx, (cs[2*j+1]-xz[2])*dz ); glVertex3fv( &c[3*j] ); } glEnd(); } if (mask & MASK_TOP) { int n = (nSpine - 1) * nCrossSection; glBegin( GL_POLYGON ); indexFaceNormal( 3*n+2, 3*n+1, 3*n, c, N ); glNormal3fv( N ); for (int j = 0; j < nCrossSection; ++j) { glTexCoord2f( (cs[2*j]-xz[0])*dx, (cs[2*j+1]-xz[2])*dz ); glVertex3fv( &c [3*(j+n)] ); } glEnd(); } } } Viewer::Object ViewerOpenGL::insertExtrusion(unsigned int mask, int nOrientation, float *orientation, int nScale, float *scale, int nCrossSection, float *crossSection, int nSpine, float *spine ) { float *c = new float[nCrossSection * nSpine * 3]; float *tc = new float[nCrossSection * nSpine * 3]; computeExtrusion( nOrientation, orientation, nScale, scale, nCrossSection, crossSection, nSpine, spine, c, tc, 0 ); GLuint glid = 0; #if USE_GEOMETRY_DISPLAY_LISTS if (! d_selectMode) { glid = glGenLists(1); glNewList( glid, GL_COMPILE_AND_EXECUTE ); } #endif beginGeometry(); // Face orientation & culling glFrontFace( (mask & MASK_CCW) ? GL_CCW : GL_CW ); if (! (mask & MASK_SOLID) ) glDisable( GL_CULL_FACE ); // Handle creaseAngle, correct normals, ... int n = 0; for (int i = 0; i < nSpine-1; ++i, n+=nCrossSection) { glBegin( GL_QUAD_STRIP ); for (int j = 0; j < nCrossSection; ++j) { // Compute normals float v1[3], v2[3]; if (j < nCrossSection-1) Vdiff( v1, &c[3*(n+j+1)], &c[3*(n+j)] ); else Vdiff( v1, &c[3*(n+j)], &c[3*(n+j-1)] ); Vdiff( v2, &c[3*(n+j+nCrossSection)], &c[3*(n+j)] ); Vcross( v1, v1, v2 ); glNormal3fv( v1 ); glTexCoord2fv( &tc[3*(n+j+nCrossSection)] ); glVertex3fv( &c [3*(n+j+nCrossSection)] ); glTexCoord2fv( &tc[3*(n+j)] ); glVertex3fv( &c [3*(n+j)] ); } glEnd(); } // Draw caps. Convex can only impact the caps of an extrusion. if (mask & (MASK_BOTTOM | MASK_TOP)) insertExtrusionCaps( mask, nSpine, c, nCrossSection, crossSection ); delete [] c; delete [] tc; endGeometry(); if (glid) glEndList(); return (Object) glid; } Viewer::Object ViewerOpenGL::insertLineSet(int npoints, float *points, int nlines, int *lines, bool colorPerVertex, float *color, int nci, int *ci) { GLuint glid = 0; if (npoints < 2) return 0; #if USE_GEOMETRY_DISPLAY_LISTS if (! d_selectMode) { glid = glGenLists(1); glNewList( glid, GL_COMPILE_AND_EXECUTE ); } #endif beginGeometry(); // Lighting, texturing don't apply to line sets glDisable( GL_LIGHTING ); glDisable( GL_TEXTURE_2D ); if (color && ! colorPerVertex ) glShadeModel( GL_FLAT ); glBegin( GL_LINE_STRIP ); if (color && ! colorPerVertex) glColor3fv( &color[ (nci > 0) ? 3*ci[0] : 0 ] ); int nl = 0; for (int i = 0; i 0) ? 3*ci[nl] : 3*nl ] ); } else { if (color && colorPerVertex) glColor3fv( &color[ (nci > 0) ? 3*ci[i] : 3*lines[i] ] ); glVertex3fv( &points[3*lines[i]] ); } } glEnd(); endGeometry(); if (glid) glEndList(); return (Object) glid; } Viewer::Object ViewerOpenGL::insertPointSet(int npoints, float *points, float *colors) { GLuint glid = 0; #if USE_GEOMETRY_DISPLAY_LISTS if (! d_selectMode) { glid = glGenLists(1); glNewList( glid, GL_COMPILE_AND_EXECUTE ); } #endif beginGeometry(); // Lighting, texturing don't apply to points glDisable( GL_LIGHTING ); glDisable( GL_TEXTURE_2D ); glBegin( GL_POINTS ); for (int i = 0; i bounds[1]) bounds[1] = points[0]; if (points[1] < bounds[2]) bounds[2] = points[1]; else if (points[1] > bounds[3]) bounds[3] = points[1]; if (points[2] < bounds[4]) bounds[4] = points[2]; else if (points[2] > bounds[5]) bounds[5] = points[2]; } } void texGenParams( float bounds[], // xmin,xmax, ymin,ymax, zmin,zmax int axes[2], // s, t float params[4] ) // s0, 1/sSize, t0, 1/tSize { axes[0] = 0; axes[1] = 1; params[0] = params[1] = params[2] = params[3] = 0.0; for (int nb=0; nb<3; ++nb) { float db = bounds[2*nb+1]-bounds[2*nb]; if ( db > params[1] ) { axes[1] = axes[0]; axes[0] = nb; params[2] = params[0]; params[3] = params[1]; params[0] = bounds[2*nb]; params[1] = db; } else if ( db > params[3] ) { axes[1] = nb; params[2] = bounds[2*nb]; params[3] = db; } } // If two of the dimensions are zero, give up. if ( FPZERO( params[1] ) || FPZERO( params[3] )) return; params[1] = 1.0f / params[1]; params[3] = 1.0f / params[3]; } // Address of _ith entry of indexed triplet array _v. yummy #define INDEX_VAL(_v,_i) \ &((_v).v[ 3*(((_v).ni > 0) ? (_v).i[_i] : (_i)) ]) #define INDEX_VTX_VAL(_v,_f,_i) \ &((_v).v[ 3*(((_v).ni > 0) ? (_v).i[_i] : (_f)[_i]) ]) void ViewerOpenGL::insertShellConvex( ShellData *s ) { float N[3]; int i, nf = 0; // Number of faces for (i = 0; infaces-1; ++i) { if (i == 0 || s->faces[i] == -1) { if (i > 0) glEnd(); glBegin(GL_POLYGON); // Per-face attributes if (s->color.v && ! (s->mask & MASK_COLOR_PER_VERTEX)) glColor3fv( INDEX_VAL(s->color, nf) ); if (! (s->mask & MASK_NORMAL_PER_VERTEX)) { int i1 = (i == 0) ? 0 : i+1; if (s->normal.v) glNormal3fv( INDEX_VAL(s->normal, nf) ); else if (i < s->nfaces - 4 && s->faces[i1] >= 0 && s->faces[i1+1] >= 0 && s->faces[i1+2] >= 0) { indexFaceNormal( 3*s->faces[i1], 3*s->faces[i1+1], 3*s->faces[i1+2], s->points, N ); // Lukas: flip normal if primitiv-orientation is clockwise if (!(s->mask & MASK_CCW)) for (int k=0;k<3;k++) // flip Normal N[k] = -N[k]; glNormal3fv( N ); } } ++nf; // } if (s->faces[i] >= 0) { // Per-vertex attributes if (s->color.v && (s->mask & MASK_COLOR_PER_VERTEX) ) glColor3fv( INDEX_VTX_VAL(s->color, s->faces, i) ); if (s->mask & MASK_NORMAL_PER_VERTEX) { if (s->normal.v) glNormal3fv( INDEX_VTX_VAL(s->normal, s->faces, i) ); else ; // Generate per-vertex normal here... } float *v = &s->points[3*s->faces[i]]; if (s->texCoord.v) { int tcindex; if (s->texCoord.ni > 0) tcindex = 2 * s->texCoord.i[i]; else tcindex = 2 * s->faces[i]; glTexCoord2f( s->texCoord.v[ tcindex ], 1.0f - s->texCoord.v[ tcindex+1 ] ); } else { float c0, c1; c0 = (v[s->texAxes[0]] - s->texParams[0]) * s->texParams[1]; c1 = (v[s->texAxes[1]] - s->texParams[2]) * s->texParams[3]; glTexCoord2f( c0, c1 ); } glVertex3fv( v ); } } glEnd(); } #if GLU_VERSION_1_2 static void WINAPI tessShellBegin( GLenum type, void *pdata ) { ViewerOpenGL::ShellData *s = (ViewerOpenGL::ShellData *)pdata; float N[3]; glBegin( type ); // Per-face attributes if (s->color.v && ! (s->mask & Viewer::MASK_COLOR_PER_VERTEX)) glColor3fv( INDEX_VAL(s->color, s->nf) ); if (! (s->mask & Viewer::MASK_NORMAL_PER_VERTEX)) { int i1 = s->i == 0 ? 0 : s->i-1; if (s->normal.v) glNormal3fv( INDEX_VAL(s->normal, s->nf) ); else if (s->i < s->nfaces - 4 && s->faces[i1] >= 0 && s->faces[i1+1] >= 0 && s->faces[i1+2] >= 0) { indexFaceNormal( 3*s->faces[i1], 3*s->faces[i1+1], 3*s->faces[i1+2], s->points, N ); // Lukas: flip normal if primitiv-orientation is clockwise if (!(s->mask & Viewer::MASK_CCW)) for (int k=0;k<3;k++) // flip Normal N[k] = -N[k]; glNormal3fv( N ); } } } static void WINAPI tessShellVertex( void *vdata, void *pdata ) { size_t i = (size_t)vdata; ViewerOpenGL::ShellData *s = (ViewerOpenGL::ShellData *)pdata; // Per-vertex attributes if (s->color.v && (s->mask & Viewer::MASK_COLOR_PER_VERTEX) ) glColor3fv( INDEX_VTX_VAL(s->color, s->faces, i) ); if (s->mask & Viewer::MASK_NORMAL_PER_VERTEX) { if (s->normal.v) glNormal3fv( INDEX_VTX_VAL(s->normal, s->faces, i) ); else ; // Generate per-vertex normal here... } float *v = &s->points[3*s->faces[i]]; if (s->texCoord.v) { int tcindex; if (s->texCoord.ni > 0) tcindex = 2 * s->texCoord.i[i]; else tcindex = 2 * s->faces[i]; glTexCoord2f( s->texCoord.v[ tcindex ], 1.0f - s->texCoord.v[ tcindex+1 ] ); } else { float c0, c1; c0 = (v[s->texAxes[0]] - s->texParams[0]) * s->texParams[1]; c1 = 1.0f - (v[s->texAxes[1]] - s->texParams[2]) * s->texParams[3]; glTexCoord2f( c0, c1 ); } glVertex3fv( v ); } void ViewerOpenGL::insertShellTess(ShellData *s) { if (! d_tess) d_tess = gluNewTess(); gluTessCallback( d_tess, (GLenum) GLU_TESS_BEGIN_DATA, (TessCB) tessShellBegin ); gluTessCallback( d_tess, (GLenum) GLU_TESS_VERTEX_DATA, (TessCB) tessShellVertex ); gluTessCallback( d_tess, (GLenum) GLU_TESS_END, (TessCB) glEnd ); size_t i; for (i = 0; infaces-1; ++i) { if (i == 0 || s->faces[i] == -1) { if (i > 0) { gluTessEndContour( d_tess ); gluTessEndPolygon( d_tess ); ++ s->nf; } gluTessBeginPolygon( d_tess, s ); gluTessBeginContour( d_tess ); s->i = (int)i; } if (s->faces[i] >= 0) { GLdouble v[3]; v[0] = s->points[3*s->faces[i]+0]; v[1] = s->points[3*s->faces[i]+1]; v[2] = s->points[3*s->faces[i]+2]; gluTessVertex( d_tess, v, (void*)i ); } } gluTessEndContour( d_tess ); gluTessEndPolygon( d_tess ); } #endif // GLU_VERSION_1_2 // There are too many arguments to this... Viewer::Object ViewerOpenGL::insertShell(unsigned int mask, int npoints, float *points, int nfaces, int *faces, // face list (-1 ends each face) float *tc, // texture coordinates int ntci, // # of texture coordinate indices int *tci, // texture coordinate indices float *normal, // normals int nni, // # of normal indices int *ni, // normal indices float *color, // colors int nci, int *ci) { if (nfaces < 4) return 0; // 3 pts and a trailing -1 // Texture coordinate generation parameters. int texAxes[2]; // Map s,t to x,y,z float texParams[4]; // s0, 1/sSize, t0, 1/tSize // Compute bounding box for texture coord generation and lighting. if ( ! tc ) // || any positional lights are active... { float bounds[6]; // xmin,xmax, ymin,ymax, zmin,zmax computeBounds( npoints, points, bounds ); // do the bounds intersect the radius of any active positional lights... texGenParams( bounds, texAxes, texParams ); if ( FPZERO( texParams[1] ) || FPZERO( texParams[3] )) return 0; } GLuint glid = 0; #if USE_GEOMETRY_DISPLAY_LISTS if (! d_selectMode) { glid = glGenLists(1); glNewList( glid, GL_COMPILE_AND_EXECUTE ); } #endif beginGeometry(); // Face orientation & culling glFrontFace( (mask & MASK_CCW) ? GL_CCW : GL_CW ); if (! (mask & MASK_SOLID) ) glDisable( GL_CULL_FACE ); // Color per face if (color && ! (mask & MASK_COLOR_PER_VERTEX)) glShadeModel(GL_FLAT); // ------------------------------------------------------- // Generation of per vertex normals isn't implemented yet... if (! normal && (mask & MASK_NORMAL_PER_VERTEX)) mask &= ~MASK_NORMAL_PER_VERTEX; // ------------------------------------------------------- // Should build tri strips (probably at the VrmlNode level)... ShellData s = { mask, points, nfaces, faces, { tc, ntci, tci }, { normal, nni, ni }, { color, nci, ci }, texAxes, texParams, 0 }; #if GLU_VERSION_1_2 // Handle non-convex polys if (! (mask & MASK_CONVEX)) insertShellTess( &s ); else #endif insertShellConvex( &s ); endGeometry(); if (glid) glEndList(); return (Object) glid; } Viewer::Object ViewerOpenGL::insertSphere(float radius) { GLuint glid = 0; #if USE_GEOMETRY_DISPLAY_LISTS if (! d_selectMode) { glid = glGenLists(1); glNewList( glid, GL_COMPILE_AND_EXECUTE ); } #endif const int numLatLong = 10; const int npts = numLatLong * numLatLong; float c[ npts ][ 3 ]; float tc[ npts ][ 3 ]; // should only compute tc if a texture is present... computeSphere(radius, numLatLong, c, tc, 0); beginGeometry(); for ( int i = 0; i < numLatLong-1; ++i) { int n = i * numLatLong; glBegin( GL_QUAD_STRIP ); for ( int j = 0; j < numLatLong; ++j ) { glTexCoord2f( tc[n+j+numLatLong][0], 1.0f-tc[n+j+numLatLong][1] ); glNormal3fv( &c[n+j+numLatLong][0] ); glVertex3fv( &c[n+j+numLatLong][0] ); glTexCoord2f( tc[n+j][0], 1.0f-tc[n+j][1] ); glNormal3fv( &c[n+j][0] ); glVertex3fv( &c[n+j][0] ); } glTexCoord2f( tc[n+numLatLong][0], 1.0f-tc[n+numLatLong][1] ); glNormal3fv( &c[n+numLatLong][0] ); glVertex3fv( &c[n+numLatLong][0] ); glTexCoord2f( tc[n][0], 1.0f-tc[n][1] ); glNormal3fv( &c[n][0] ); glVertex3fv( &c[n][0] ); glEnd(); } endGeometry(); if (glid) glEndList(); return (Object) glid; } // Not fully implemented... need font, extents Viewer::Object ViewerOpenGL::insertText(int *justify, float size, int n, char **s) { GLuint glid = 0; #if USE_GEOMETRY_DISPLAY_LISTS if (! d_selectMode) { glid = glGenLists(1); glNewList( glid, GL_COMPILE_AND_EXECUTE ); } #endif beginGeometry(); text3(justify, size, n, (const char **)s); endGeometry(); if (glid) glEndList(); return (Object) glid; } // Lights Viewer::Object ViewerOpenGL::insertDirLight(float ambient, float intensity, float rgb[], float direction[]) { float amb[4] = { ambient * rgb[0], ambient * rgb[1], ambient * rgb[2], 1.0 }; float dif[4] = { intensity * rgb[0], intensity * rgb[1], intensity * rgb[2], 1.0 }; float pos[4] = { direction[0], direction[1], -direction[2], 0.0 }; // Find an unused light, give up if none left. int i; for (i=0; i causes problems when the texture or geometry node is // USE'd with a different context. void ViewerOpenGL::setMaterialMode (int textureComponents, bool colors) { if (textureComponents && d_texture && ! d_wireframe) { glEnable( GL_TEXTURE_2D ); // This is a hack: if per-{face,vertex} colors are specified, // they take precedence over textures with GL_MODULATE. The // textures won't be lit this way but at least they show up... if (textureComponents > 2 && colors) glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE ); else glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); } else glDisable( GL_TEXTURE_2D ); if (colors && textureComponents < 3 /* && lighting enabled... */ ) glEnable( GL_COLOR_MATERIAL ); else glDisable( GL_COLOR_MATERIAL ); } void ViewerOpenGL::setSensitive(void *object) { if (object) { // should make this dynamic... if (d_nSensitive == MAXSENSITIVE) { theSystem->error("Internal Error: too many sensitive objects.\n"); return; } // push name, increment object count d_sensitiveObject[ d_nSensitive ] = object; glPushName( ++d_nSensitive ); // array index+1 } else { glPopName( ); } } // Scale an image to make sizes powers of two. This puts the data back // into the memory pointed to by pixels, so there better be enough. void ViewerOpenGL::scaleTexture(int w, int h, int newW, int newH, int nc, unsigned char* pixels) { GLenum fmt[] = { GL_LUMINANCE, // single component GL_LUMINANCE_ALPHA, // 2 components GL_RGB, // 3 components GL_RGBA // 4 components }; unsigned char *newpix = new unsigned char[nc*newW*newH]; glPixelStorei( GL_PACK_ALIGNMENT, 1 ); glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); if (0 == gluScaleImage( fmt[nc-1], w, h, GL_UNSIGNED_BYTE, pixels, newW, newH, GL_UNSIGNED_BYTE, newpix)) memcpy(pixels, newpix, nc*newW*newH); delete [] newpix; } // // Pixels are lower left to upper right by row. // Viewer::TextureObject ViewerOpenGL::insertTexture(int w, int h, int nc, bool repeat_s, bool repeat_t, unsigned char *pixels, bool retainHint) { GLenum fmt[] = { GL_LUMINANCE, // single component GL_LUMINANCE_ALPHA, // 2 components GL_RGB, // 3 components GL_RGBA // 4 components }; GLuint glid = 0; if (d_selectMode) return 0; // Enable blending if needed if (d_blend && (nc == 2 || nc == 4)) glEnable(GL_BLEND); #if USE_TEXTURE_DISPLAY_LISTS if (retainHint) glGenTextures(1, &glid); glBindTexture( GL_TEXTURE_2D, glid ); #endif // Texturing is enabled in setMaterialMode glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); glTexImage2D( GL_TEXTURE_2D, 0, nc, w, h, 0, fmt[nc-1], GL_UNSIGNED_BYTE, pixels); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, repeat_s ? (GLfloat)GL_REPEAT : (GLfloat)GL_CLAMP ); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, repeat_t ? (GLfloat)GL_REPEAT : (GLfloat)GL_CLAMP ); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); return (TextureObject) glid; } void ViewerOpenGL::insertTextureReference(TextureObject t, int nc) { #if USE_TEXTURE_DISPLAY_LISTS // Enable blending if needed if (d_blend && (nc == 2 || nc == 4)) glEnable(GL_BLEND); glBindTexture( GL_TEXTURE_2D, (GLuint) t ); #endif } void ViewerOpenGL::removeTextureObject(TextureObject t) { #if USE_TEXTURE_DISPLAY_LISTS GLuint glid = (GLuint) t; glDeleteTextures( 1, &glid ); #endif } // Texture coordinate transform // Tc' = -C x S x R x C x T x Tc void ViewerOpenGL::setTextureTransform( float *center, float rotation, float *scale, float *translation ) { glMatrixMode(GL_TEXTURE); glLoadIdentity(); if (center) glTranslatef(-center[0], -center[1], 0.0); if (scale) glScalef(scale[0], scale[1], 1.0); if (rotation != 0.0) glRotatef(rotation * 180.0f / (float)M_PI, 0.0f, 0.0f, 1.0f); if (center) glTranslatef(center[0], center[1], 0.0); if (translation) glTranslatef(translation[0], translation[1], 0.0); glMatrixMode(GL_MODELVIEW); } // Transforms // P' = T x C x R x SR x S x -SR x -C x P void ViewerOpenGL::setTransform(float * center, float * rotation, float * scale, float * scaleOrientation, float * translation) { glTranslatef(translation[0], translation[1], translation[2]); glTranslatef(center[0], center[1], center[2]); if (! FPZERO(rotation[3]) ) glRotatef(rotation[3] * 180.0f / (float)M_PI, rotation[0], rotation[1], rotation[2]); if (! FPEQUAL(scale[0], 1.0) || ! FPEQUAL(scale[1], 1.0) || ! FPEQUAL(scale[2], 1.0) ) { if (! FPZERO(scaleOrientation[3]) ) glRotatef(scaleOrientation[3] * 180.0f / (float)M_PI, scaleOrientation[0], scaleOrientation[1], scaleOrientation[2]); glScalef(scale[0], scale[1], scale[2]); if (! FPZERO(scaleOrientation[3]) ) glRotatef(-scaleOrientation[3] * 180.0f / (float)M_PI, scaleOrientation[0], scaleOrientation[1], scaleOrientation[2]); } glTranslatef(-center[0], -center[1], -center[2]); } // I used to just do a glPushMatrix()/glPopMatrix() in beginObject()/endObject(). // This is a hack to work around the glPushMatrix() limit (32 deep on Mesa). // It has some ugly disadvantages: it is slower and the resulting transform // after a setTransform/unsetTransform may not be identical to the original. // It might be better to just build our own matrix stack... void ViewerOpenGL::unsetTransform(float *center, float *rotation, float *scale, float *scaleOrientation, float *translation) { glTranslatef(center[0], center[1], center[2]); if (! FPEQUAL(scale[0], 1.0) || ! FPEQUAL(scale[1], 1.0) || ! FPEQUAL(scale[2], 1.0) ) { if (! FPZERO(scaleOrientation[3]) ) glRotatef(scaleOrientation[3] * 180.0f / (float)M_PI, scaleOrientation[0], scaleOrientation[1], scaleOrientation[2]); glScalef(1.0f/scale[0], 1.0f/scale[1], 1.0f/scale[2]); if (! FPZERO(scaleOrientation[3]) ) glRotatef(-scaleOrientation[3] * 180.0f / (float)M_PI, scaleOrientation[0], scaleOrientation[1], scaleOrientation[2]); } if (! FPZERO(rotation[3]) ) glRotatef(- rotation[3] * 180.0f / (float)M_PI, rotation[0], rotation[1], rotation[2]); glTranslatef(-center[0], -center[1], -center[2]); glTranslatef(-translation[0], -translation[1], -translation[2]); } // The matrix gets popped at endObject() - Not anymore. I added // an explicit unsetBillboardTransform to work around the matrix // depth limit of 32 in mesa. Now the limit only applies to // nested billboards. void ViewerOpenGL::setBillboardTransform(float *axisOfRotation) { GLint viewport[4]; GLdouble modelview[16], projection[16]; glGetIntegerv (GL_VIEWPORT, viewport); glGetDoublev (GL_MODELVIEW_MATRIX, modelview); for (int i=0; i<4; ++i) for (int j=0; j<4; ++j) projection[4*i+j] = (i == j) ? 1.0 : 0.0; double dx, dy, dz, o[3]; float y[3], z[3]; gluUnProject( 0.0, 0.0, 0.0, modelview, projection, viewport, o, o+1, o+2); // Rotate around specified axis if (! FPZERO(axisOfRotation[0]) || ! FPZERO(axisOfRotation[1]) || ! FPZERO(axisOfRotation[2]) ) { y[0] = axisOfRotation[0]; y[1] = axisOfRotation[1]; y[2] = axisOfRotation[2]; } // Face the viewer else { gluUnProject( 0.0, 1.0, 0.0, modelview, projection, viewport, &dx, &dy, &dz); y[0] = (float)(dx - o[0]); y[1] = (float)(dy - o[1]); y[2] = (float)(dz - o[2]); } gluUnProject( 0.0, 0.0, 1.0, modelview, projection, viewport, &dx, &dy, &dz); z[0] = (float)(dx - o[0]); z[1] = (float)(dy - o[1]); z[2] = (float)(dz - o[2]); Vnorm( y ); Vnorm( z ); float m[16]; Vcross( m, y, z ); m[3] = 0.0; m[4] = y[0]; m[5] = y[1]; m[6] = y[2]; m[7] = 0.0; m[8] = z[0]; m[9] = z[1]; m[10] = z[2]; m[11] = 0.0; m[12] = 0.0; m[13] = 0.0; m[14] = 0.0; m[15] = 1.0; glPushMatrix(); glMultMatrixf(m); } void ViewerOpenGL::unsetBillboardTransform(float * /*axisOfRotation*/) { glPopMatrix(); } void ViewerOpenGL::setViewpoint(float *position, float *orientation, float fieldOfView, float avatarSize, float visibilityLimit) { glMatrixMode( GL_PROJECTION ); if (! d_selectMode) glLoadIdentity(); float field_of_view = fieldOfView * 180.0f / (float)M_PI; float aspect = ((float) d_winWidth) / d_winHeight; float znear = (avatarSize > 0.0f) ? (0.5f * avatarSize) : 0.01f; float zfar = (visibilityLimit > 0.0f) ? visibilityLimit : 1000.0f; gluPerspective(field_of_view, aspect, znear, zfar); glMatrixMode(GL_MODELVIEW); // Guess some distance along the sight line to use as a target... float d = 10.0f * avatarSize; if (d < znear || d > zfar) d = 0.2f * (avatarSize + zfar); float target[3], up[3]; computeView(position, orientation, d, target, up); // Save position for use when drawing background d_position[0] = position[0]; d_position[1] = position[1]; d_position[2] = position[2]; gluLookAt(position[0], position[1], position[2], target[0]+d_target[0], target[1]+d_target[1], target[2]+d_target[2], up[0], up[1], up[2]); // View modifiers are applied in first beginObject if (d_rotationChanged) { build_rotmatrix(d_rotationMatrix, d_curquat); d_rotationChanged = false; } } // The viewer knows the current viewpoint void ViewerOpenGL::transformPoints(int np, float *p) { float m[16]; glGetFloatv (GL_MODELVIEW_MATRIX, m); float x, y, z; for (int i=0; iupdate( timeNow )) { checkErrors("update"); wsPostRedraw(); } // Set an alarm clock for the next update time. wsSetTimer( d_scene->getDelta() ); } void ViewerOpenGL::redraw() { if (! d_GLinitialized) initialize(); double start = theSystem->time(); glDisable( GL_FOG ); // this is a global attribute glDisable( GL_TEXTURE_2D ); glEnable( GL_CULL_FACE ); glFrontFace( GL_CCW ); glCullFace( GL_BACK ); if (d_lit) glEnable( GL_LIGHTING ); glDisable( GL_COLOR_MATERIAL ); glDisable(GL_BLEND); glShadeModel( GL_SMOOTH ); d_nObjects = 0; d_nestedObjects = 0; d_nSensitive = 0; // Clean out any defined lights for (int i=0; irender(this); if (d_reportFPS) { if (d_background[0]+d_background[1]+d_background[2] > 2.0) glColor3f(0.0, 0.0, 0.0); else glColor3f(1.0, 1.0, 1.0); // Report average of last 2 frame times (can't report this // frame time because we want to count swapBuffers time). int fper = (int) (1.0 / (0.5 * (d_renderTime + d_renderTime1))); char buf[30]; sprintf(buf, "%d f/s", fper); text2( 5, 5, 20.0, buf ); } wsSwapBuffers(); d_renderTime1 = d_renderTime; d_renderTime = theSystem->time() - start; } void ViewerOpenGL::resize(int width, int height) { if (width < 2) width = 2; if (height < 2) height = 2; glViewport(0, 0, width, height); d_winWidth = width; d_winHeight = height; } void ViewerOpenGL::input( EventInfo *e ) { switch (e->event) { case EVENT_KEY_DOWN: handleKey( e->what ); break; case EVENT_MOUSE_MOVE: (void) checkSensitive( e->x, e->y, EVENT_MOUSE_MOVE ); break; case EVENT_MOUSE_CLICK: case EVENT_MOUSE_RELEASE: handleButton( e ); break; case EVENT_MOUSE_DRAG: handleMouseDrag( e->x, e->y ); break; } } void ViewerOpenGL::step( float x, float y, float z ) { GLint viewport[4]; GLdouble modelview[16], projection[16]; glGetIntegerv (GL_VIEWPORT, viewport); glGetDoublev (GL_MODELVIEW_MATRIX, modelview); glGetDoublev (GL_PROJECTION_MATRIX, projection); GLdouble ox, oy, oz; gluUnProject( 0.0, 0.0, 0.0, modelview, projection, viewport, &ox, &oy, &oz); GLdouble dx, dy, dz; gluUnProject( 100.*x, 100.*y, z, modelview, projection, viewport, &dx, &dy, &dz); dx -= ox; dy -= oy; dz -= oz; double d = dx * dx + dy * dy + dz * dz; if (FPZERO(d)) return; d = sqrt(d); VrmlNodeNavigationInfo *nav = d_scene->bindableNavigationInfoTop(); float speed = 1.0; if (nav) speed = nav->speed(); d = speed / d; dx *= d; dy *= d; dz *= d; if (FPZERO(d)) return; d_translatex += (float)dx; d_translatey += (float)dy; d_translatez += (float)dz; wsPostRedraw(); } void ViewerOpenGL::handleKey(int key) { switch (key) { case KEY_LEFT: step( 1.0, 0.0, 0.0 ); break; case KEY_UP: step( 0.0, 0.0, 1.0 ); break; case KEY_RIGHT: step( -1.0, 0.0, 0.0 ); break; case KEY_DOWN: step( 0.0, 0.0, -1.0 ); break; case 'a': // Look up trackball(d_lastquat, 0.0, 0.45, 0.0, 0.65); add_quats(d_lastquat, d_curquat, d_curquat); d_rotationChanged = true; wsPostRedraw(); break; case 'z': // Look down trackball(d_lastquat, 0.0, 0.65, 0.0, 0.45); add_quats(d_lastquat, d_curquat, d_curquat); d_rotationChanged = true; wsPostRedraw(); break; case ',': // Look left //trackball(d_lastquat, 0.65, 0.0, 0.45, 0.0); //add_quats(d_lastquat, d_curquat, d_curquat); //d_rotationChanged = true; d_target[0] -= 1.0; wsPostRedraw(); break; case '.': // Look right //trackball(d_lastquat, 0.45, 0.0, 0.65, 0.0); //add_quats(d_lastquat, d_curquat, d_curquat); //d_rotationChanged = true; d_target[0] += 1.0; wsPostRedraw(); break; case KEY_PAGE_DOWN: if (d_scene) d_scene->nextViewpoint(); wsPostRedraw(); break; case KEY_PAGE_UP: if (d_scene) d_scene->prevViewpoint(); wsPostRedraw(); break; case '/': // Frames/second d_reportFPS = ! d_reportFPS; wsPostRedraw(); break; case 'b': d_blend = ! d_blend; wsPostRedraw(); theSystem->inform(" Alpha blending %sabled.", d_blend ? "en" : "dis"); break; case 'l': d_lit = ! d_lit; wsPostRedraw(); theSystem->inform(" Lighting %sabled.", d_lit ? "en" : "dis"); break; case KEY_HOME: case 'r': // Reset view resetUserNavigation(); break; case 't': d_texture = ! d_texture; wsPostRedraw(); theSystem->inform(" Texture mapping %sabled.", d_texture ? "en" : "dis"); break; case 'w': // Wireframe (should disable texturing) d_wireframe = ! d_wireframe; glPolygonMode(GL_FRONT_AND_BACK, d_wireframe ? GL_LINE : GL_FILL); wsPostRedraw(); theSystem->inform(" Drawing polygons in %s mode.", d_wireframe ? "wireframe" : "filled"); break; case 'q': d_scene->destroyWorld(); // may not return break; default: break; } } // Mouse button up/down void ViewerOpenGL::handleButton( EventInfo *e) { d_rotating = d_scaling = d_translating = false; // Check for a sensitive object first if (e->what == 0 && checkSensitive( e->x, e->y, e->event ) ) return; d_activeSensitive = 0; // Nothing under the mouse if (e->event == EVENT_MOUSE_RELEASE) wsSetCursor( CURSOR_INHERIT ); else switch (e->what) // button { case 0: wsSetCursor( CURSOR_CYCLE ); d_rotating = true; d_beginx = e->x; d_beginy = e->y; break; case 1: wsSetCursor( CURSOR_UP_DOWN ); d_scaling = true; d_beginx = e->x; d_beginy = e->y; break; case 2: wsSetCursor( CURSOR_CROSSHAIR ); d_translating = true; d_beginx = e->x; d_beginy = e->y; break; } } // Mouse movement with button down void ViewerOpenGL::handleMouseDrag(int x, int y) { if (d_activeSensitive) { (void) checkSensitive( x, y, EVENT_MOUSE_DRAG ); } else if (d_rotating) { trackball(d_lastquat, (2.0f * d_beginx - d_winWidth) / d_winWidth, (d_winHeight - 2.0f * d_beginy) / d_winHeight, (2.0f * x - d_winWidth) / d_winWidth, (d_winHeight - 2.0f * y) / d_winHeight); d_beginx = x; d_beginy = y; add_quats(d_lastquat, d_curquat, d_curquat); d_rotationChanged = true; wsPostRedraw(); } // This is not scaling, it is now moving in screen Z coords else if (d_scaling) { //d_scale = d_scale * (1.0 - (((float) (d_beginy - y)) / d_winHeight)); //d_translatez += 2.0 * ((float) (d_beginy - y)) / d_winHeight; step( 0.0f, 0.0f, 0.2f * ((float) (d_beginy - y)) / d_winHeight ); d_beginx = x; d_beginy = y; wsPostRedraw(); } else if (d_translating) { //d_translatex += 2.0 * ((float) (x - d_beginx)) / d_winWidth; //d_translatey += 2.0 * ((float) (d_beginy - y)) / d_winHeight; step( 0.2f * ((float) (x - d_beginx)) / d_winWidth, 0.2f * ((float) (d_beginy - y)) / d_winHeight, 0.0f ); d_beginx = x; d_beginy = y; wsPostRedraw(); } } // Check for pickable objects. Should check whether d_nSensitive > 0... bool ViewerOpenGL::checkSensitive(int x, int y, EventType mouseEvent ) { double timeNow = theSystem->time(); GLint viewport[4]; glGetIntegerv( GL_VIEWPORT, viewport ); GLuint selectBuffer[ 4*MAXSENSITIVE ]; glSelectBuffer( 4*MAXSENSITIVE, selectBuffer ); (void) glRenderMode( GL_SELECT ); d_selectMode = true; glInitNames(); glPushName(0); glMatrixMode( GL_PROJECTION ); glLoadIdentity(); gluPickMatrix( (GLdouble) x, (GLdouble)(viewport[3] - y), 2.0, 2.0, viewport ); // Set up the global attributes glDisable( GL_FOG ); glDisable( GL_TEXTURE_2D ); glFrontFace( GL_CCW ); glEnable( GL_CULL_FACE ); glCullFace( GL_BACK ); glDisable( GL_LIGHTING ); glDisable( GL_COLOR_MATERIAL ); glShadeModel( GL_FLAT ); d_nObjects = 0; d_nestedObjects = 0; d_nSensitive = 0; // Clean out any defined lights for (int i=0; irender( this ); d_selectMode = false; // select closest hit GLuint closest = 0, minz = 0xffffffff; int selected = 0; // nothing selected int hits = glRenderMode( GL_RENDER ); if (hits > 0) { // selectBuffer[] = { N1, z1, z2, name1, ..., nameN1, N2, ... } GLuint *sel = selectBuffer; for (int nh=0; nh 0 && closest <= (GLuint)d_nSensitive) { selected = closest; } } wsSetCursor( selected ? CURSOR_INFO : CURSOR_INHERIT ); // Compute & store the world coords of the pick if something // was already active or is about to become active. The window // Z coord is retained when a drag is started on a sensitive // so the drag stays in the same plane even if the mouse moves // off the original sensitive object. double selectCoord[3] = { 0.0, 0.0, 0.0 }; if (d_activeSensitive || selected) { if (! d_activeSensitive) d_selectZ = minz / (double)0xffffffff; GLint viewport[4]; GLdouble modelview[16], projection[16]; glGetIntegerv (GL_VIEWPORT, viewport); glGetDoublev (GL_PROJECTION_MATRIX, projection); glGetDoublev (GL_MODELVIEW_MATRIX, modelview); GLdouble dx, dy, dz; gluUnProject( (GLdouble) x, (GLdouble) (viewport[3] - y), d_selectZ, modelview, projection, viewport, &dx, &dy, &dz); selectCoord[0] = dx; selectCoord[1] = dy; selectCoord[2] = dz; } bool wasActive = false; // An active sensitive object "grabs" the mouse until button released if (d_activeSensitive) { if (mouseEvent == EVENT_MOUSE_RELEASE || mouseEvent == EVENT_MOUSE_MOVE) { d_scene->sensitiveEvent( d_sensitiveObject[ d_activeSensitive-1 ], timeNow, selected == d_activeSensitive, false, selectCoord ); d_activeSensitive = 0; } else // _DRAG { d_scene->sensitiveEvent( d_sensitiveObject[ d_activeSensitive-1 ], timeNow, selected == d_activeSensitive, true, selectCoord ); } wasActive = true; } // A click down over a sensitive object initiates an active grab and // mouse over events are no longer relevant. else if (mouseEvent == EVENT_MOUSE_CLICK && selected != 0) { if (d_overSensitive && d_overSensitive != selected) { d_scene->sensitiveEvent( d_sensitiveObject[ d_overSensitive-1 ], timeNow, false, false, // isOver, isActive selectCoord ); d_overSensitive = 0; } d_activeSensitive = selected; d_scene->sensitiveEvent( d_sensitiveObject[ d_activeSensitive-1 ], timeNow, true, true, // isOver, isActive selectCoord ); } // Handle isOver events (coords are bogus) else if (mouseEvent == EVENT_MOUSE_MOVE) { if (d_overSensitive && d_overSensitive != selected) { d_scene->sensitiveEvent( d_sensitiveObject[ d_overSensitive-1 ], timeNow, false, false, // isOver, isActive selectCoord ); } d_overSensitive = selected; if (d_overSensitive) d_scene->sensitiveEvent( d_sensitiveObject[ d_overSensitive-1 ], timeNow, true, false, // isOver, isActive selectCoord ); } // Was event handled here? if (d_activeSensitive || wasActive) update( timeNow ); // Everything is handled except down clicks where nothing was selected // and up clicks where nothing was active. return d_activeSensitive || wasActive; } // Text rendering /* From smooth.c by Nate Robins, 1997 in the glut dist */ #if HAVE_GLUT # include #endif /* text: general purpose text routine. draws a string according to * format in a stroke font at x, y after scaling it by the scale * specified (scale is in window-space (lower-left origin) pixels). * * x - position in x (in window-space) * y - position in y (in window-space) * scale - scale in pixels * text - text string to render */ void ViewerOpenGL::text2( int x, int y, float scale, char* text ) { #if HAVE_GLUT GLfloat font_scale = scale / (119.05 + 33.33); // ??? glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); gluOrtho2D(0, d_winWidth, 0, d_winHeight); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); glPushAttrib(GL_ENABLE_BIT); glDisable(GL_LIGHTING); glDisable(GL_TEXTURE_2D); glDisable(GL_DEPTH_TEST); glTranslatef(x, y, 0.0); glScalef(font_scale, font_scale, font_scale); for(char *p = text; *p; ++p) glutStrokeCharacter(GLUT_STROKE_ROMAN, *p); glPopAttrib(); glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); #endif } void ViewerOpenGL::text3(int *justify, float size, int n, const char **s) { #if HAVE_GLUT float font_scale = 0.005 * size; glPushAttrib(GL_ENABLE_BIT); glDisable(GL_LIGHTING); glDisable(GL_TEXTURE_2D); GLfloat x = 0.0, y = 0.0; if (justify[0] < 0) x -= strlen(s[0]); else if (justify[0] == 0) x -= 0.5 * strlen(s[0]); float font_height = 1.0; for (int i=0; i