/* oglrenderer.C: OpenGL VRML renderer */ #include #include "oglrenderer.H" #include "Grouping.H" #include "Transforming.H" #include "Shape.H" #include "Appearance.H" #include "Material.H" #include "TextureTransform.H" #include "Texture.H" #include "ImageTexture.H" #include "PixelTexture.H" #include "Geometry.H" #include "ElevationGrid.H" #include "IndexedFaceSet.H" #include "DirectionalLight.H" #include "PointLight.H" #include "SpotLight.H" #include "nodeCatalog.H" #include #include #include #include #include "render.h" #include "file.H" // use the integer user data field for associating a display list ID with the node #define DLISTID(node) (node->data.i) #define LIGHTID(node) (GLenum)(GL_LIGHT0 + node->data.i) // for lights static int max_lights; void opengl_renderer::begin_frame(class world* world) { // determine maximum number of lights glGetIntegerv(GL_MAX_LIGHTS, &max_lights); // set default material begin_Material(dynamic_cast(builtin_nodes->lookup("Material"))); views.size = 0; // will be read again nrlights = 0; texture_active = lighting_active = false; current_texture_components = 0; current_alpha = 1.0; // define and enable headlight is required. ninfo = world->stacks->navigationInfo.top(); if (enable_headlight) { GLfloat pos[4] = {0., 0., 1., 0.}; // directional light shines along -Z GLfloat col[4] = {1., 1., 1., 1.}; glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); glLightfv(GL_LIGHT0, GL_POSITION, pos); glLightfv(GL_LIGHT0, GL_AMBIENT, col); glLightfv(GL_LIGHT0, GL_DIFFUSE, col); glLightfv(GL_LIGHT0, GL_SPECULAR, col); glPopMatrix(); glEnable(GL_LIGHT0); nrlights = 1; } glMatrixMode(GL_TEXTURE); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE); #ifdef GL_VERSION_1_2 glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR); #endif glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, 1); glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 1); GLfloat Black[] = { 0., 0., 0., 1. }; glLightModelfv(GL_LIGHT_MODEL_AMBIENT, Black); glEnable(GL_BLEND); glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); } void opengl_renderer::end_frame(class world*) { use_dlists = renderopts.use_display_lists; } void opengl_renderer::worldInfo(WorldInfo *info) { if (!winfo) winfo = info; } void opengl_renderer::viewpoint(Viewpoint *vp) { views.append(vp); } void opengl_renderer::navigationInfo(class NavigationInfo *info) { // nothing to do } bool opengl_renderer::node_has_changed(SFNode* node) { return (node->is_updated() || first_frame); } void opengl_renderer::begin_Grouping(Grouping *group) { if (node_has_changed(group)) { if (group->bboxSize.x >= 0. || group->bboxSize.y >= 0. || group->bboxSize.z >= 0.) { gmin = (Vec3)(group->bboxCenter) - (Vec3)(group->bboxSize)/(float)2.; gmax = (Vec3)(group->bboxCenter) + (Vec3)(group->bboxSize)/(float)2.; } else { gmin = Vec3(HUGE, HUGE, HUGE); gmax = Vec3(-HUGE, -HUGE, -HUGE); } } } void opengl_renderer::end_Grouping(Grouping *group) { if (node_has_changed(group)) { group->bboxCenter = ((gmin + gmax)/(float)2.); group->bboxSize = (gmax - gmin); group->clear_update(); } } void opengl_renderer::begin_Transforming(Transforming *xf) { renderer::begin_Transforming(xf); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glMultMatrixf((const GLfloat *)(xf->xf).m); } void opengl_renderer::end_Transforming(Transforming *xf) { glMatrixMode(GL_MODELVIEW); glPopMatrix(); renderer::end_Transforming(xf); } void opengl_renderer::begin_Shape(Shape *shape) { glDisable(GL_LIGHTING); lighting_active = false; GLfloat white[4] = {1., 1., 1., 1.}; glColor4fv(white); current_alpha = 1.; } // called after begin_Texture (if there is a texture) void opengl_renderer::begin_Material(Material *m) { if (current_texture_components != 2 && current_texture_components != 4) { // texture without alpha channel: set material alpha. For textures with // alpha channel, the texture alpha shall be used instead. With modulated // textures, this means that we should keep the previously initialised value // 1 for the alpha current_alpha = 1. - m->transparency; } GLfloat diffuse[4]; diffuse[3] = current_alpha; //#ifdef NEVER if (current_texture_components == 3 || current_texture_components == 4) { // diffuse material color of RGB and RGBA textures shall be texture color. // With modulated textures, this means that we should set the diffuse // material color to 1. diffuse[0] = diffuse[1] = diffuse[2] = 1.; } else { //#endif diffuse[0] = m->diffuseColor.r; diffuse[1] = m->diffuseColor.g; diffuse[2] = m->diffuseColor.b; //#ifdef NEVER } //#endif GLfloat ambient[] = { diffuse[0] * m->ambientIntensity, diffuse[1] * m->ambientIntensity, diffuse[2] * m->ambientIntensity, current_alpha }; GLfloat specular[] = { m->specularColor.r, m->specularColor.g, m->specularColor.b, current_alpha }; GLfloat emission[] = { m->emissiveColor.r, m->emissiveColor.g, m->emissiveColor.b, current_alpha }; glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, ambient); glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse); glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specular); glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, emission); glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, m->shininess*128. > 127. ? 127. : m->shininess*128.); if (!renderopts.disable_lighting) { glEnable(GL_LIGHTING); lighting_active = true; } glColor4fv(diffuse); glDisable(GL_COLOR_MATERIAL); } void opengl_renderer::end_Material(Material *) { // nothing to do. } void opengl_renderer::begin_TextureTransform(TextureTransform* txf) { glMatrixMode(GL_TEXTURE); glLoadMatrixf((const GLfloat *)(txf->xf).m); glMatrixMode(GL_MODELVIEW); } void opengl_renderer::end_TextureTransform(TextureTransform*) { glMatrixMode(GL_TEXTURE); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); } int opengl_renderer::load_Texture(int width, int height, int numcomponents, unsigned char* image, bool repeatS, bool repeatT) { // create a texture object for the image texture glPixelStorei(GL_UNPACK_ALIGNMENT, 1); GLuint texid; glGenTextures(1, &texid); glBindTexture(GL_TEXTURE_2D, texid); if (repeatS) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); if (repeatT) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); GLint formats[5] = { 0, GL_LUMINANCE, GL_LUMINANCE_ALPHA, GL_RGB, GL_RGBA }; current_texture_components = numcomponents; #ifdef NO_MIPMAPPING // TODO: use gluScaleImage if image width or height is not a power of 2. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexImage2D(GL_TEXTURE_2D, 0, numcomponents, width, height, 0, formats[numcomponents], GL_UNSIGNED_BYTE, image); #endif /*NO_MIPMAPPING*/ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); gluBuild2DMipmaps(GL_TEXTURE_2D, numcomponents, width, height, formats[numcomponents], GL_UNSIGNED_BYTE, image); return texid; } void opengl_renderer::begin_Texture(Texture* t) { if (disable_textures) return; if (t->data.i == 0) { if (!t->Map) // read the texture image if not done so before t->load(); if (t->Map) // succesfully read the image, create OpenGL texture object t->data.i = load_Texture(t->Width, t->Height, t->Channels, t->Map, t->repeatS, t->repeatT); else t->data.i = -1; // indicates a problem loading the texture } if (t->data.i > 0) { current_texture_components = t->Channels; // make texture object current and enable texturing glEnable(GL_TEXTURE_2D); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, current_texture_components < 3 ? GL_MODULATE : GL_MODULATE); glBindTexture(GL_TEXTURE_2D, t->data.i); texture_active = true; } } void opengl_renderer::end_Texture(Texture* t) { if (t->data.i <= 0 || disable_textures) return; // disable texturing glDisable(GL_TEXTURE_2D); texture_active = false; current_texture_components = 0; } void opengl_renderer::begin_LightSource(LightSource *l) { if (!l->on || nrlights>=max_lights) return; /* LIGHTID(l) */ l->data.i = nrlights; nrlights++; GLfloat amb[] = { l->ambientIntensity * l->color.r, l->ambientIntensity * l->color.g, l->ambientIntensity * l->color.b, 1. }; GLfloat diff[] = { l->intensity * l->color.r, l->intensity * l->color.g, l->intensity * l->color.b, 1. }; GLfloat spec[] = { l->intensity * l->color.r, l->intensity * l->color.g, l->intensity * l->color.b, 1. }; glLightfv(LIGHTID(l), GL_AMBIENT, amb); glLightfv(LIGHTID(l), GL_DIFFUSE, diff); glLightfv(LIGHTID(l), GL_SPECULAR, spec); glEnable(LIGHTID(l)); } void opengl_renderer::end_LightSource(LightSource *l) { if (!l->on || nrlights>=max_lights) return; nrlights--; glDisable(LIGHTID(l)); } void opengl_renderer::begin_DirectionalLight(DirectionalLight *l) { if (!l->on || nrlights>=max_lights) return; begin_LightSource(l); GLfloat dir[4] = { -l->direction.x, -l->direction.y, -l->direction.z, 0.0 }; glLightfv(LIGHTID(l), GL_POSITION, dir); } void opengl_renderer::begin_PointLight(PointLight *l) { if (!l->on || nrlights>=max_lights) return; begin_LightSource(l); GLfloat pos[4] = { l->location.x, l->location.y, l->location.z, 1.0 }; glLightfv(LIGHTID(l), GL_POSITION, pos); glLightf(LIGHTID(l), GL_CONSTANT_ATTENUATION, l->attenuation.x); glLightf(LIGHTID(l), GL_LINEAR_ATTENUATION, l->attenuation.y); glLightf(LIGHTID(l), GL_QUADRATIC_ATTENUATION, l->attenuation.z); } void opengl_renderer::begin_SpotLight(SpotLight *l) { if (!l->on || nrlights>=max_lights) return; begin_LightSource(l); GLfloat pos[4] = { l->location.x, l->location.y, l->location.z, 1.0 }; glLightfv(LIGHTID(l), GL_POSITION, pos); glLightf(LIGHTID(l), GL_CONSTANT_ATTENUATION, l->attenuation.x); glLightf(LIGHTID(l), GL_LINEAR_ATTENUATION, l->attenuation.y); glLightf(LIGHTID(l), GL_QUADRATIC_ATTENUATION, l->attenuation.z); GLfloat dir[3] = { l->direction.x, l->direction.y, l->direction.z }; glLightfv(LIGHTID(l), GL_SPOT_DIRECTION, dir); GLfloat cutoff = (l->cutOffAngle > M_PI/2.) ? (M_PI/2.) : (double)l->cutOffAngle; glLightf(LIGHTID(l), GL_SPOT_CUTOFF, cutoff * 180. / M_PI); GLfloat expon = 0.0; if (l->beamWidth < cutoff - 1e-3 && l->beamWidth > 1e-3) // exponent is such that we have half intensity at beamWidth angle expon = log(0.5) / log(cos(l->beamWidth)); glLightf(LIGHTID(l), GL_SPOT_EXPONENT, (expon > 127) ? 127 : expon); } void opengl_renderer::geometry(Geometry *geom) { renderer::normals_required = lighting_active; renderer::texcoords_required = texture_active; // per-vertex or per-face colors are only relevant if there is // no texture or the texture is a luminance texture or luminance+alpha // texture. For RGB and RGBA textures, the texture color replaces // the fragment color. renderer::colors_required = current_texture_components < 3; // if (texture_active && lighting_active) { // glEnable(GL_COLOR_MATERIAL); // } #ifdef NEVER cerr << __FILE__ << ":" << __LINE__ << ": " << "lighting = " << (glIsEnabled(GL_LIGHTING) ? "ON" : "OFF") << ", " << "light 0 = " << (glIsEnabled(GL_LIGHT0) ? "ON" : "OFF") << ", " << "light 1 = " << (glIsEnabled(GL_LIGHT1) ? "ON" : "OFF") << ", " << "light 2 = " << (glIsEnabled(GL_LIGHT2) ? "ON" : "OFF") << ", " << "light 3 = " << (glIsEnabled(GL_LIGHT3) ? "ON" : "OFF") << "\n"; #endif if (node_has_changed(geom) || use_dlists != renderopts.use_display_lists) { geom->clear_update(); if (renderopts.use_display_lists) { if (DLISTID(geom) > 0) glDeleteLists(DLISTID(geom), 1); DLISTID(geom) = (long)glGenLists(1); glNewList(DLISTID(geom), GL_COMPILE); renderer::geometry(geom); glEndList(); } } if (geom->solid) { if (geom->ccw) glCullFace(GL_BACK); // by default CCW faces are front facing else glCullFace(GL_FRONT); if (dynamicBackfaceCulling) glEnable(GL_CULL_FACE); } else if (dynamicBackfaceCulling) glDisable(GL_CULL_FACE); if (renderopts.use_display_lists) glCallList(DLISTID(geom)); else renderer::geometry(geom); } void opengl_renderer::elevationGrid(ElevationGrid* geom) { if (geom->color && current_texture_components < 3) glEnable(GL_COLOR_MATERIAL); geometry(geom); } void opengl_renderer::indexedFaceSet(IndexedFaceSet* geom) { if (geom->color && current_texture_components < 3) glEnable(GL_COLOR_MATERIAL); geometry(geom); } void opengl_renderer::begin_faces(Geometry * geom) { if (geom->colorPerVertex) glShadeModel(GL_SMOOTH); else glShadeModel(GL_FLAT); stripsize = -1; lastnrverts = -1; } void opengl_renderer::begin_face(int, int nverts) { #ifdef NEVER cerr << "begin face:\n"; #endif if (draw_outlines) { if (stripsize>=0) glEnd(); glColor3f(1.,1.,1.); glBegin(GL_LINE_LOOP); return; } else if (lastnrverts!=3 || lastnrverts!=4 || nverts != lastnrverts || stripsize > 200) { if (stripsize >= 0) glEnd(); switch (nverts) { case 3: glBegin(GL_TRIANGLES); break; case 4: glBegin(GL_QUADS); break; default: glBegin(GL_POLYGON); break; } stripsize = 0; } lastnrverts = nverts; } void opengl_renderer::face_normal(int, const SFVec3f& norm) { glNormal3f(norm.x, norm.y, norm.z); } void opengl_renderer::face_color(int, const SFColor& col) { glColor4f(col.r, col.g, col.b, current_alpha); } void opengl_renderer::vertex_normal(int, const SFVec3f& norm) { glNormal3f(norm.x, norm.y, norm.z); } void opengl_renderer::vertex_color(int, const SFColor& col) { glColor4f(col.r, col.g, col.b, current_alpha); } void opengl_renderer::vertex_texCoord(int id, const SFVec2f& texco) { glTexCoord2f(texco.s, texco.t); } void opengl_renderer::vertex_coord(int id, const SFVec3f& vert) { Vec3 v = Vec3(vert) * xf; // compute bounding box min <<= v; max >>= v; gmin <<= v; gmax >>= v; glVertex3f(vert.x, vert.y, vert.z); stripsize++; } void opengl_renderer::end_face(int, int) { // nothing to do } void opengl_renderer::end_faces(Geometry *) { if (stripsize >= 0) glEnd(); }