/* This file is part of Mitsuba, a physically based rendering system. Copyright (c) 2007-2011 by Wenzel Jakob and others. Mitsuba is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License Version 3 as published by the Free Software Foundation. Mitsuba is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #if defined(__OSX__) #include #include #else #include #endif #include #include #include #include #include #include static mitsuba::PrimitiveThreadLocal glewContext; GLEWContextStruct *glewGetContext() { return &glewContext.get(); } MTS_NAMESPACE_BEGIN GLRenderer::GLRenderer(Session *session) : Renderer(session) { } GLRenderer::~GLRenderer() { } void GLRenderer::init(Device *device, Renderer *other) { Renderer::init(device, other); m_driverRenderer = (char *) glGetString(GL_RENDERER); m_driverVendor = (char *) glGetString(GL_VENDOR); m_driverVersion = (char *) glGetString(GL_VERSION); Log(m_logLevel, "OpenGL renderer : %s", m_driverRenderer.c_str()); Log(m_logLevel, "OpenGL vendor : %s", m_driverVendor.c_str()); Log(m_logLevel, "OpenGL version : %s", m_driverVersion.c_str()); /* OpenGL extensions */ GLenum err = glewInit(); if (err != GLEW_OK) Log(EError, "GLEW Error: %s\n", glewGetErrorString(err)); if (glewIsSupported("GL_EXT_framebuffer_object")) { m_capabilities->setSupported( RendererCapabilities::ERenderToTexture, true); Log(m_logLevel, "Capabilities: Framebuffers objects are supported."); } else { Log(m_warnLogLevel, "Capabilities: Framebuffers objects are NOT supported!"); } if (glewIsSupported("GL_ARB_shading_language_100")) { m_capabilities->setSupported( RendererCapabilities::EShadingLanguage, true); Log(m_logLevel, "Capabilities: GLSL is supported."); } else { Log(m_warnLogLevel, "Capabilities: GLSL is NOT supported!"); } if (glewIsSupported("GL_ARB_texture_float")) { m_capabilities->setSupported( RendererCapabilities::EFloatingPointTextures, true); Log(m_logLevel, "Capabilities: Floating point textures are supported."); } else { Log(m_warnLogLevel, "Capabilities: Floating point textures are NOT supported!"); } bool leopardWorkaround = false; #if defined(__OSX__) /* Floating point color render buffers sort-of work for Leopard/8600M or 9600M, but the extension is not reported */ SInt32 MacVersion; if (Gestalt(gestaltSystemVersion, &MacVersion) == noErr) { if (MacVersion >= 0x1050 && MacVersion < 0x1060) { Log(EInfo, "Enabling Leopard floating point color buffer workaround"); leopardWorkaround = true; } } #endif if (glewIsSupported("GL_ARB_color_buffer_float") || leopardWorkaround) { m_capabilities->setSupported( RendererCapabilities::EFloatingPointBuffer, true); Log(m_logLevel, "Capabilities: Floating point color buffers are supported."); } else { Log(m_warnLogLevel, "Capabilities: Floating point color buffers are NOT supported!"); } if (glewIsSupported("GL_EXT_framebuffer_blit")) { m_capabilities->setSupported( RendererCapabilities::EBufferBlit, true); Log(m_logLevel, "Capabilities: Fast buffer blitting is supported."); } else { Log(m_warnLogLevel, "Capabilities: Fast buffer blitting is NOT supported!"); } if (glewIsSupported("GL_EXT_framebuffer_multisample") && glewIsSupported("GL_EXT_framebuffer_blit") && glewIsSupported("GL_ARB_texture_multisample")) { m_capabilities->setSupported( RendererCapabilities::EMultisampleRenderToTexture, true); Log(m_logLevel, "Capabilities: Multisample framebuffer objects are supported."); } else { Log(m_warnLogLevel, "Capabilities: Multisample framebuffer objects are NOT supported!"); } if (glewIsSupported("GL_ARB_vertex_buffer_object")) { m_capabilities->setSupported( RendererCapabilities::EVertexBufferObjects, true); Log(m_logLevel, "Capabilities: Vertex buffer objects are supported."); } else { Log(m_warnLogLevel, "Capabilities: Vertex buffer objects are NOT supported!"); } if (glewIsSupported("GL_EXT_geometry_shader4")) { m_capabilities->setSupported( RendererCapabilities::EGeometryShaders, true); Log(m_logLevel, "Capabilities: Geometry shaders are supported."); } else { Log(m_warnLogLevel, "Capabilities: Geometry shaders are NOT supported!"); } if (glewIsSupported("GL_ARB_sync")) { m_capabilities->setSupported( RendererCapabilities::ESyncObjects, true); Log(m_logLevel, "Capabilities: Synchronization objects are supported."); } else { Log(m_warnLogLevel, "Capabilities: Synchronization objects are NOT supported!"); } if (glewIsSupported("GL_NV_vertex_buffer_unified_memory")) { m_capabilities->setSupported( RendererCapabilities::EBindless, true); Log(m_logLevel, "Capabilities: Bindless rendering is supported."); } else { Log(m_warnLogLevel, "Capabilities: Bindless rendering is NOT supported!"); } /* Hinting */ glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); glHint(GL_POINT_SMOOTH_HINT, GL_NICEST); glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST); glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); glHint(GL_POINT_SMOOTH_HINT, GL_NICEST); /* Disable color value clamping */ if (m_capabilities->isSupported( RendererCapabilities::EFloatingPointBuffer) && !leopardWorkaround) { glClampColorARB(GL_CLAMP_VERTEX_COLOR_ARB, GL_FALSE); glClampColorARB(GL_CLAMP_READ_COLOR_ARB, GL_FALSE); glClampColorARB(GL_CLAMP_FRAGMENT_COLOR_ARB, GL_FALSE); } /* Clip to viewport */ glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); setBlendMode(EBlendNone); glEnable(GL_POINT_SMOOTH); m_normalsEnabled = false; m_texcoordsEnabled = false; m_tangentsEnabled = false; m_colorsEnabled = false; m_stride = -1; m_queuedTriangles = 0; checkError(); } void GLRenderer::shutdown() { Renderer::shutdown(); } GPUTexture *GLRenderer::createGPUTexture(const std::string &name, Bitmap *bitmap) { return new GLTexture(name, bitmap); } GPUGeometry *GLRenderer::createGPUGeometry(const TriMesh *mesh) { return new GLGeometry(mesh); } GPUProgram *GLRenderer::createGPUProgram(const std::string &name) { return new GLProgram(name); } GPUSync *GLRenderer::createGPUSync() { return new GLSync(); } void GLRenderer::reconfigure(const Device *device) { glViewport(0, 0, device->getSize().x, device->getSize().y); } void GLRenderer::clear() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); checkError(); } void GLRenderer::checkError(bool onlyWarn) { int glError = glGetError(); if (glError) Log(onlyWarn ? m_warnLogLevel : EError, "OpenGL Error : %s", gluErrorString(glError)); } void GLRenderer::beginDrawingMeshes(bool transmitOnlyPositions) { m_transmitOnlyPositions = transmitOnlyPositions; glEnableClientState(GL_VERTEX_ARRAY); m_stride = -1; if (m_capabilities->isSupported(RendererCapabilities::EBindless)) { glEnableClientState(GL_VERTEX_ATTRIB_ARRAY_UNIFIED_NV); glEnableClientState(GL_ELEMENT_ARRAY_UNIFIED_NV); } } void GLRenderer::drawTriMesh(const TriMesh *mesh) { std::map::iterator it = m_geometry.find(mesh); if (it != m_geometry.end()) { /* Draw using vertex buffer objects (bindless if supported) */ GLGeometry *geometry = static_cast((*it).second); if (m_capabilities->isSupported(RendererCapabilities::EBindless)) { glBufferAddressRangeNV(GL_VERTEX_ARRAY_ADDRESS_NV, 0, geometry->m_vertexAddr, geometry->m_vertexSize); int stride = geometry->m_stride; if (stride != m_stride) { glVertexFormatNV(3, GL_FLOAT, stride); glNormalFormatNV(GL_FLOAT, stride); glClientActiveTexture(GL_TEXTURE0); glTexCoordFormatNV(2, GL_FLOAT, stride); glClientActiveTexture(GL_TEXTURE1); glTexCoordFormatNV(3, GL_FLOAT, stride); glColorFormatNV(3, GL_FLOAT, stride); m_stride = stride; } if (!m_transmitOnlyPositions) { int pos = 3 * sizeof(GLfloat); if (mesh->hasVertexNormals()) { if (!m_normalsEnabled) { glEnableClientState(GL_NORMAL_ARRAY); m_normalsEnabled = true; } glBufferAddressRangeNV(GL_NORMAL_ARRAY_ADDRESS_NV, 0, geometry->m_vertexAddr + pos, geometry->m_vertexSize - pos); pos += 3 * sizeof(GLfloat); } else if (!m_normalsEnabled) { glDisableClientState(GL_NORMAL_ARRAY); m_normalsEnabled = false; } if (mesh->hasVertexTexcoords()) { glClientActiveTexture(GL_TEXTURE0); if (!m_texcoordsEnabled) { glEnableClientState(GL_TEXTURE_COORD_ARRAY); m_texcoordsEnabled = true; } glBufferAddressRangeNV(GL_TEXTURE_COORD_ARRAY_ADDRESS_NV, 0, geometry->m_vertexAddr + pos, geometry->m_vertexSize - pos); pos += 2 * sizeof(GLfloat); } else if (m_texcoordsEnabled) { glClientActiveTexture(GL_TEXTURE0); glDisableClientState(GL_TEXTURE_COORD_ARRAY); m_texcoordsEnabled = false; } /* Pass 'dpdu' as second set of texture coordinates */ if (mesh->hasVertexTangents()) { glClientActiveTexture(GL_TEXTURE1); if (!m_tangentsEnabled) { glEnableClientState(GL_TEXTURE_COORD_ARRAY); m_tangentsEnabled = true; } glBufferAddressRangeNV(GL_TEXTURE_COORD_ARRAY_ADDRESS_NV, 1, geometry->m_vertexAddr + pos, geometry->m_vertexSize - pos); pos += 3 * sizeof(GLfloat); } else if (m_tangentsEnabled) { glClientActiveTexture(GL_TEXTURE1); glDisableClientState(GL_TEXTURE_COORD_ARRAY); m_tangentsEnabled = false; } if (mesh->hasVertexColors()) { if (!m_colorsEnabled) { glEnableClientState(GL_COLOR_ARRAY); m_colorsEnabled = true; } glBufferAddressRangeNV(GL_COLOR_ARRAY_ADDRESS_NV, 0, geometry->m_vertexAddr + pos, geometry->m_vertexSize - pos); } else if (m_colorsEnabled) { glDisableClientState(GL_COLOR_ARRAY); m_colorsEnabled = false; } } glBufferAddressRangeNV(GL_ELEMENT_ARRAY_ADDRESS_NV, 0, geometry->m_indexAddr, geometry->m_indexSize); } else { glBindBuffer(GL_ARRAY_BUFFER, geometry->m_vertexID); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, geometry->m_indexID); int stride = geometry->m_stride; /* Set up the vertex/normal arrays */ glVertexPointer(3, GL_FLOAT, stride, (GLfloat *) 0); if (!m_transmitOnlyPositions) { int pos = 3; if (mesh->hasVertexNormals()) { if (!m_normalsEnabled) { glEnableClientState(GL_NORMAL_ARRAY); m_normalsEnabled = true; } glNormalPointer(GL_FLOAT, stride, (GLfloat *) 0 + pos); pos += 3; } else if (m_normalsEnabled) { glDisableClientState(GL_NORMAL_ARRAY); m_normalsEnabled = false; } if (mesh->hasVertexTexcoords()) { glClientActiveTexture(GL_TEXTURE0); if (!m_texcoordsEnabled) { glEnableClientState(GL_TEXTURE_COORD_ARRAY); m_texcoordsEnabled = true; } glTexCoordPointer(2, GL_FLOAT, stride, (GLfloat *) 0 + pos); pos += 2; } else if (m_texcoordsEnabled) { glClientActiveTexture(GL_TEXTURE0); glDisableClientState(GL_TEXTURE_COORD_ARRAY); m_texcoordsEnabled = false; } /* Pass 'dpdu' as second set of texture coordinates */ if (mesh->hasVertexTangents()) { glClientActiveTexture(GL_TEXTURE1); if (!m_tangentsEnabled) { glEnableClientState(GL_TEXTURE_COORD_ARRAY); m_tangentsEnabled = true; } glTexCoordPointer(3, GL_FLOAT, stride, (GLfloat *) 0 + pos); pos += 3; } else if (m_tangentsEnabled) { glClientActiveTexture(GL_TEXTURE1); glDisableClientState(GL_TEXTURE_COORD_ARRAY); m_tangentsEnabled = false; } if (mesh->hasVertexColors()) { if (!m_colorsEnabled) { glEnableClientState(GL_COLOR_ARRAY); m_colorsEnabled = true; } glColorPointer(3, GL_FLOAT, stride, (GLfloat *) 0 + pos); } else if (m_colorsEnabled) { glDisableClientState(GL_COLOR_ARRAY); m_colorsEnabled = false; } } } size_t size = mesh->getTriangleCount(); if (EXPECT_TAKEN(m_queuedTriangles + size < MTS_GL_MAX_QUEUED_TRIS)) { /* Draw all triangles */ glDrawElements(GL_TRIANGLES, (GLsizei) (size * 3), GL_UNSIGNED_INT, (GLvoid *) 0); m_queuedTriangles += size; } else { /* Spoon-feed them (keeps the OS responsive) */ size_t size = mesh->getTriangleCount(), cur = 0; while (cur < size) { size_t drawAmt = std::min(size - cur, MTS_GL_MAX_QUEUED_TRIS - m_queuedTriangles); if (drawAmt > 0) glDrawElements(GL_TRIANGLES, (GLsizei) (drawAmt * 3), GL_UNSIGNED_INT, (GLuint *) 0 + cur * 3); m_queuedTriangles += drawAmt; cur += drawAmt; if (cur < size) { finish(); } } } if (!m_capabilities->isSupported(RendererCapabilities::EBindless)) { glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); } } else { /* Draw the old-fashioned way without VBOs */ const GLchar *positions = (const GLchar *) mesh->getVertexPositions(); const GLchar *normals = (const GLchar *) mesh->getVertexNormals(); const GLchar *texcoords = (const GLchar *) mesh->getVertexTexcoords(); const GLchar *tangents = (const GLchar *) mesh->getVertexTangents(); const GLchar *colors = (const GLchar *) mesh->getVertexColors(); const GLchar *indices = (const GLchar *) mesh->getTriangles(); GLenum dataType = sizeof(Float) == 4 ? GL_FLOAT : GL_DOUBLE; glVertexPointer(3, dataType, 0, positions); if (!m_transmitOnlyPositions) { if (mesh->hasVertexNormals()) { if (!m_normalsEnabled) { glEnableClientState(GL_NORMAL_ARRAY); m_normalsEnabled = true; } glNormalPointer(dataType, 0, normals); } else if (m_normalsEnabled) { glDisableClientState(GL_NORMAL_ARRAY); m_normalsEnabled = false; } glClientActiveTexture(GL_TEXTURE0); if (mesh->hasVertexTexcoords()) { if (!m_texcoordsEnabled) { glEnableClientState(GL_TEXTURE_COORD_ARRAY); m_texcoordsEnabled = true; } glTexCoordPointer(2, dataType, 0, texcoords); } else if (m_texcoordsEnabled) { glDisableClientState(GL_TEXTURE_COORD_ARRAY); m_texcoordsEnabled = false; } /* Pass 'dpdu' as second set of texture coordinates */ glClientActiveTexture(GL_TEXTURE1); if (mesh->hasVertexTangents()) { if (!m_tangentsEnabled) { glEnableClientState(GL_TEXTURE_COORD_ARRAY); m_tangentsEnabled = true; } glTexCoordPointer(3, dataType, sizeof(Vector), tangents); } else if (m_tangentsEnabled) { glDisableClientState(GL_TEXTURE_COORD_ARRAY); m_tangentsEnabled = false; } if (mesh->hasVertexColors()) { if (!m_colorsEnabled) { glEnableClientState(GL_COLOR_ARRAY); m_colorsEnabled = true; } // This won't work for spectral rendering glColorPointer(3, dataType, 0, colors); } else if (m_colorsEnabled) { glDisableClientState(GL_COLOR_ARRAY); m_colorsEnabled = false; } } size_t size = mesh->getTriangleCount(); if (EXPECT_TAKEN(m_queuedTriangles + size < MTS_GL_MAX_QUEUED_TRIS)) { /* Draw all triangles */ glDrawElements(GL_TRIANGLES, (GLsizei) (mesh->getTriangleCount()*3), GL_UNSIGNED_INT, indices); m_queuedTriangles += size; } else { /* Spoon-feed them (keeps the OS responsive) */ size_t size = mesh->getTriangleCount(), cur = 0; while (cur < size) { size_t drawAmt = std::min(size - cur, MTS_GL_MAX_QUEUED_TRIS - m_queuedTriangles); if (drawAmt > 0) glDrawElements(GL_TRIANGLES, (GLsizei) (drawAmt * 3), GL_UNSIGNED_INT, indices + cur * 3); m_queuedTriangles += drawAmt; cur += drawAmt; if (cur < size) { finish(); } } } } } void GLRenderer::endDrawingMeshes() { glDisableClientState(GL_VERTEX_ARRAY); if (m_normalsEnabled) { glDisableClientState(GL_NORMAL_ARRAY); m_normalsEnabled = false; } if (m_texcoordsEnabled) { glClientActiveTexture(GL_TEXTURE0); glDisableClientState(GL_TEXTURE_COORD_ARRAY); m_texcoordsEnabled = false; } if (m_tangentsEnabled) { glClientActiveTexture(GL_TEXTURE1); glDisableClientState(GL_TEXTURE_COORD_ARRAY); m_tangentsEnabled = false; } if (m_colorsEnabled) { glDisableClientState(GL_COLOR_ARRAY); m_colorsEnabled = false; } if (m_capabilities->isSupported(RendererCapabilities::EBindless)) { glDisableClientState(GL_VERTEX_ATTRIB_ARRAY_UNIFIED_NV); glDisableClientState(GL_ELEMENT_ARRAY_UNIFIED_NV); } else { glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); } } void GLRenderer::drawAll(const std::vector > &geo) { GLfloat temp[16]; GLRenderer::beginDrawingMeshes(true); glMatrixMode(GL_MODELVIEW); glPushMatrix(); std::vector >::const_iterator it; if (m_capabilities->isSupported(RendererCapabilities::EBindless)) { for (it = geo.begin(); it != geo.end(); ++it) { const GLGeometry *geometry = static_cast(it->first); const TriMesh *mesh = geometry->getTriMesh(); int pos=0; for (int j=0; j<4; j++) for (int i=0; i<4; i++) temp[pos++] = (GLfloat) it->second.getMatrix().m[i][j]; glLoadMatrixf(temp); int stride = geometry->m_stride; if (stride != m_stride) { glVertexFormatNV(3, GL_FLOAT, stride); m_stride = stride; } glBufferAddressRangeNV(GL_VERTEX_ARRAY_ADDRESS_NV, 0, geometry->m_vertexAddr, geometry->m_vertexSize); glBufferAddressRangeNV(GL_ELEMENT_ARRAY_ADDRESS_NV, 0, geometry->m_indexAddr, geometry->m_indexSize); size_t size = mesh->getTriangleCount(); if (EXPECT_TAKEN(m_queuedTriangles + size < MTS_GL_MAX_QUEUED_TRIS)) { /* Draw all triangles */ glDrawElements(GL_TRIANGLES, (GLsizei) (size * 3), GL_UNSIGNED_INT, (GLvoid *) 0); m_queuedTriangles += size; } else { /* Spoon-feed them (keeps the OS responsive) */ size_t size = mesh->getTriangleCount(), cur = 0; while (cur < size) { size_t drawAmt = std::min(size - cur, MTS_GL_MAX_QUEUED_TRIS - m_queuedTriangles); if (drawAmt > 0) glDrawElements(GL_TRIANGLES, (GLsizei) (drawAmt * 3), GL_UNSIGNED_INT, (GLuint *) 0 + cur * 3); m_queuedTriangles += drawAmt; cur += drawAmt; if (cur < size) finish(); } } } } else { for (it = geo.begin(); it != geo.end(); ++it) { const GLGeometry *geometry = static_cast(it->first); const TriMesh *mesh = geometry->getTriMesh(); int pos=0; for (int j=0; j<4; j++) for (int i=0; i<4; i++) temp[pos++] = (GLfloat) it->second.getMatrix().m[i][j]; glLoadMatrixf(temp); glBindBuffer(GL_ARRAY_BUFFER, geometry->m_vertexID); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, geometry->m_indexID); /* Set up the vertex/normal arrays */ glVertexPointer(3, GL_FLOAT, geometry->m_stride, (GLfloat *) 0); size_t size = mesh->getTriangleCount(); if (EXPECT_TAKEN(m_queuedTriangles + size < MTS_GL_MAX_QUEUED_TRIS)) { /* Draw all triangles */ glDrawElements(GL_TRIANGLES, (GLsizei) (size * 3), GL_UNSIGNED_INT, (GLvoid *) 0); m_queuedTriangles += size; } else { /* Spoon-feed them (keeps the OS responsive) */ size_t size = mesh->getTriangleCount(), cur = 0; while (cur < size) { size_t drawAmt = std::min(size - cur, MTS_GL_MAX_QUEUED_TRIS - m_queuedTriangles); if (drawAmt > 0) glDrawElements(GL_TRIANGLES, (GLsizei) (drawAmt * 3), GL_UNSIGNED_INT, (GLuint *) 0 + cur * 3); m_queuedTriangles += drawAmt; cur += drawAmt; if (cur < size) finish(); } } } } GLRenderer::endDrawingMeshes(); glPopMatrix(); } void GLRenderer::blitTexture(const GPUTexture *tex, bool flipVertically, bool centerHoriz, bool centerVert, const Vector2i &offset) { tex->bind(); glColor4f(1.0f, 1.0f, 1.0f, 1.0f); if (tex->getType() == GPUTexture::ETexture2D) { GLint viewport[4]; glGetIntegerv(GL_VIEWPORT, viewport); Vector2i scrSize = Vector2i(viewport[2], viewport[3]); Vector2i texSize = Vector2i(tex->getSize().x, tex->getSize().y); if (scrSize.x == 0 || scrSize.y == 0) { tex->unbind(); return; } glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0, scrSize.x, scrSize.y, 0, -1, 1); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glBegin(GL_QUADS); Vector2i upperLeft(0), lowerRight(0); if (centerHoriz) upperLeft.x = (scrSize.x - texSize.x)/2; if (centerVert) upperLeft.y = (scrSize.y - texSize.y)/2; upperLeft += offset; lowerRight = upperLeft + texSize; if (flipVertically) std::swap(upperLeft.y, lowerRight.y); const float zDepth = -1.0f; // just before the far plane glTexCoord2f(0.0f, 0.0f); glVertex3f((float) upperLeft.x, (float) upperLeft.y, zDepth); glTexCoord2f(1.0f, 0.0f); glVertex3f((float) lowerRight.x, (float) upperLeft.y, zDepth); glTexCoord2f(1.0f, 1.0f); glVertex3f((float) lowerRight.x, (float) lowerRight.y, zDepth); glTexCoord2f(0.0f, 1.0f); glVertex3f((float) upperLeft.x, (float) lowerRight.y, zDepth); glEnd(); } else if (tex->getType() == GPUTexture::ETextureCubeMap) { glMatrixMode(GL_PROJECTION); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); /* From OpenTK */ glBegin(GL_QUADS); // 0 -x glTexCoord3f(-1.0f, +1.0f, -1.0f); glVertex2f(-1.0f, +0.333f); glTexCoord3f(-1.0f, +1.0f, +1.0f); glVertex2f(-0.5f, +0.333f); glTexCoord3f(-1.0f, -1.0f, +1.0f); glVertex2f(-0.5f, -0.333f); glTexCoord3f(-1.0f, -1.0f, -1.0f); glVertex2f(-1.0f, -0.333f); // 1 +z glTexCoord3f(-1.0f, +1.0f, +1.0f); glVertex2f(-0.5f, +0.333f); glTexCoord3f(+1.0f, +1.0f, +1.0f); glVertex2f(+0.0f, +0.333f); glTexCoord3f(+1.0f, -1.0f, +1.0f); glVertex2f(+0.0f, -0.333f); glTexCoord3f(-1.0f, -1.0f, +1.0f); glVertex2f(-0.5f, -0.333f); // 2 +x glTexCoord3f(+1.0f, +1.0f, +1.0f); glVertex2f(+0.0f, +0.333f); glTexCoord3f(+1.0f, +1.0f, -1.0f); glVertex2f(+0.5f, +0.333f); glTexCoord3f(+1.0f, -1.0f, -1.0f); glVertex2f(+0.5f, -0.333f); glTexCoord3f(+1.0f, -1.0f, +1.0f); glVertex2f(+0.0f, -0.333f); // 3 -z glTexCoord3f(+1.0f, +1.0f, -1.0f); glVertex2f(+0.5f, +0.333f); glTexCoord3f(-1.0f, +1.0f, -1.0f); glVertex2f(+1.0f, +0.333f); glTexCoord3f(-1.0f, -1.0f, -1.0f); glVertex2f(+1.0f, -0.333f); glTexCoord3f(+1.0f, -1.0f, -1.0f); glVertex2f(+0.5f, -0.333f); // 4 +y glTexCoord3f(-1.0f, +1.0f, -1.0f); glVertex2f(-0.5f, +1.0f); glTexCoord3f(+1.0f, +1.0, -1.0f); glVertex2f(+0.0f, +1.0); glTexCoord3f(+1.0f, +1.0f, +1.0f); glVertex2f(+0.0f, +0.333f); glTexCoord3f(-1.0f, +1.0f, +1.0f); glVertex2f(-0.5f, +0.333f); // 5 -y glTexCoord3f(-1.0f, -1.0f, +1.0f); glVertex2f(-0.5f, -0.333f); glTexCoord3f(+1.0f, -1.0, +1.0f); glVertex2f(+0.0f, -0.333f); glTexCoord3f(+1.0f, -1.0f, -1.0f); glVertex2f(+0.0f, -1.0f); glTexCoord3f(-1.0f, -1.0f, -1.0f); glVertex2f(-0.5f, -1.0f); glEnd(); } tex->unbind(); } void GLRenderer::blitQuad(bool flipVertically) { GLint viewport[4]; glGetIntegerv(GL_VIEWPORT, viewport); Vector2 scrSize((float) viewport[2], (float) viewport[3]); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); glOrtho(0, scrSize.x, scrSize.y, 0, -1, 1); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); const Float zDepth = -1.0f; glBegin(GL_QUADS); glTexCoord2f(0.0f, flipVertically ? 1.0f : 0.0f); glVertex3f(0.0f, 0.0f, zDepth); glTexCoord2f(1.0f, flipVertically ? 1.0f : 0.0f); glVertex3f(scrSize.x, 0.0f, zDepth); glTexCoord2f(1.0f, flipVertically ? 0.0f : 1.0f); glVertex3f(scrSize.x, scrSize.y, zDepth); glTexCoord2f(0.0f, flipVertically ? 0.0f : 1.0f); glVertex3f(0.0f, scrSize.y, zDepth); glEnd(); glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); } void GLRenderer::drawText(const Point2i &_pos, const Font *font, const std::string &text) { int viewport[4]; glGetIntegerv(GL_VIEWPORT, viewport); Vector2i scrSize = Vector2i(viewport[2], viewport[3]); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0, scrSize.x, scrSize.y, 0, -1, 1); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); font->getTexture()->bind(); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); Point2i pos(_pos); int initial = pos.x; glBegin(GL_QUADS); for (size_t i=0; igetMaxVerticalBearing()*4.0/3.0); continue; } const Font::Glyph &glyph = font->getGlyph(character); Point2 start = Point2(pos + Vector2i( glyph.horizontalBearing, font->getMaxVerticalBearing() - glyph.verticalBearing )); Point2 end = start + Vector2(glyph.size); Point2 txStart = glyph.tx; Point2 txEnd = txStart + glyph.ts; glTexCoord2f(txStart.x, txStart.y); glVertex2f( start.x, start.y); glTexCoord2f(txEnd.x, txStart.y); glVertex2f( end.x, start.y); glTexCoord2f(txEnd.x, txEnd.y); glVertex2f( end.x, end.y); glTexCoord2f(txStart.x, txEnd.y); glVertex2f( start.x, end.y); pos.x += glyph.horizontalAdvance; if (i+1 < text.length()) pos.x += font->getKerning(character, text[i+1]); } glEnd(); font->getTexture()->unbind(); glDisable(GL_BLEND); } void GLRenderer::setPointSize(Float size) { glPointSize(size); } void GLRenderer::drawPoint(const Point &p) { glBegin(GL_POINTS); glVertex3f(p.x, p.y, p.z); glEnd(); } void GLRenderer::drawLine(const Point &a, const Point &b) { glBegin(GL_LINES); glVertex3f(a.x, a.y, a.z); glVertex3f(b.x, b.y, b.z); glEnd(); } void GLRenderer::drawEllipse(const Point ¢er, const Vector &axis1, const Vector &axis2) { const int nSteps = 100; const float stepSize = 2*M_PI/nSteps; glBegin(GL_LINE_LOOP); for (int i=0; i<100; ++i) { Point p = center + axis1 * std::cos(i*stepSize) + axis2 * std::sin(i*stepSize); glVertex3f(p.x, p.y, p.z); } glEnd(); } void GLRenderer::drawAABB(const AABB &aabb) { #define V(a,b,c) glVertex3f(aabb.a.x, aabb.b.y, aabb.c.z) glBegin(GL_LINE_LOOP); V(max,min,max); V(max,min,min); V(max,max,min); V(max,max,max); glEnd(); glBegin(GL_LINE_LOOP); V(max,max,max); V(max,max,min); V(min,max,min); V(min,max,max); glEnd(); glBegin(GL_LINE_LOOP); V(max,max,max); V(min,max,max); V(min,min,max); V(max,min,max); glEnd(); glBegin(GL_LINE_LOOP); V(min,min,max); V(min,max,max); V(min,max,min); V(min,min,min); glEnd(); glBegin(GL_LINE_LOOP); V(min,min,max); V(min,min,min); V(max,min,min); V(max,min,max); glEnd(); glBegin(GL_LINE_LOOP); V(min,min,min); V(min,max,min); V(max,max,min); V(max,min,min); glEnd(); #undef V } void GLRenderer::setCamera(const ProjectiveCamera *camera) { GLfloat temp1[16], temp2[16]; const Matrix4x4 &view = camera->getViewTransform().getMatrix(); const Matrix4x4 &proj = camera->getGLProjectionTransform().getMatrix(); int pos=0; for (int j=0; j<4; j++) { for (int i=0; i<4; i++) { temp1[pos]=(GLfloat) proj.m[i][j]; temp2[pos++]=(GLfloat) view.m[i][j]; } } glMatrixMode(GL_PROJECTION); glLoadMatrixf(temp1); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glScalef(1.0f, 1.0f, -1.0f); glMultMatrixf(temp2); } void GLRenderer::setCamera(const ProjectiveCamera *camera, const Point2 &jitter) { GLfloat temp1[16], temp2[16]; const Matrix4x4 &view = camera->getViewTransform().getMatrix(); const Matrix4x4 &proj = camera->getGLProjectionTransform(jitter).getMatrix(); int pos=0; for (int j=0; j<4; j++) { for (int i=0; i<4; i++) { temp1[pos]=(GLfloat) proj.m[i][j]; temp2[pos++]=(GLfloat) view.m[i][j]; } } glMatrixMode(GL_PROJECTION); glLoadMatrixf(temp1); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glScalef(1.0f, 1.0f, -1.0f); glMultMatrixf(temp2); } void GLRenderer::setCamera(const Matrix4x4 &proj, const Matrix4x4 &view) { GLfloat temp1[16], temp2[16]; int pos=0; for (int j=0; j<4; j++) { for (int i=0; i<4; i++) { temp1[pos]=(GLfloat) proj.m[i][j]; temp2[pos++]=(GLfloat) view.m[i][j]; } } glMatrixMode(GL_PROJECTION); glLoadMatrixf(temp1); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glScalef(1.0f, 1.0f, -1.0f); glMultMatrixf(temp2); } void GLRenderer::setDepthOffset(Float value) { if (value == 0) glDisable(GL_POLYGON_OFFSET_FILL); glEnable(GL_POLYGON_OFFSET_FILL); glPolygonOffset(4.0f, (GLfloat) value); } void GLRenderer::setDepthMask(bool value) { glDepthMask(value ? GL_TRUE : GL_FALSE); } void GLRenderer::setDepthTest(bool value) { if (value) glEnable(GL_DEPTH_TEST); else glDisable(GL_DEPTH_TEST); } void GLRenderer::setColorMask(bool value) { GLboolean flag = value ? GL_TRUE : GL_FALSE; glColorMask(flag, flag, flag, flag); } void GLRenderer::pushTransform(const Transform &trafo) { const Matrix4x4 &matrix = trafo.getMatrix(); GLfloat temp[16]; int pos=0; for (int j=0; j<4; j++) for (int i=0; i<4; i++) temp[pos++] = (GLfloat) matrix.m[i][j]; glMatrixMode(GL_MODELVIEW); glPushMatrix(); glMultMatrixf(temp); } void GLRenderer::popTransform() { glMatrixMode(GL_MODELVIEW); glPopMatrix(); } void GLRenderer::flush() { glFlush(); } void GLRenderer::finish() { glFinish(); m_queuedTriangles = 0; } void GLRenderer::setColor(const Spectrum &spec, Float alpha) { Float r, g, b; spec.toLinearRGB(r, g, b); glColor4f(r, g, b, alpha); } void GLRenderer::setBlendMode(EBlendMode mode) { switch (mode) { case EBlendNone: glDisable(GL_BLEND); break; case EBlendAdditive: glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE); break; case EBlendAlpha: glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); break; default: Log(EError, "Invalid blend mode!"); } } void GLRenderer::setCullMode(ECullMode mode) { switch (mode) { case ECullNone: glDisable(GL_CULL_FACE); break; case ECullFront: glEnable(GL_CULL_FACE); glCullFace(GL_FRONT); break; case ECullBack: glEnable(GL_CULL_FACE); glCullFace(GL_BACK); break; default: Log(EError, "Invalid culling mode!"); } } MTS_IMPLEMENT_CLASS(GLRenderer, true, Renderer) MTS_NAMESPACE_END