mitsuba/src/libhw/glrenderer.cpp

1018 lines
30 KiB
C++

/*
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 <http://www.gnu.org/licenses/>.
*/
#include <mitsuba/mitsuba.h>
#if defined(__OSX__)
#include <OpenGL/glew.h>
#include <Carbon/Carbon.h>
#else
#include <GL/glew.h>
#endif
#include <mitsuba/hw/glrenderer.h>
#include <mitsuba/hw/gltexture.h>
#include <mitsuba/hw/glgeometry.h>
#include <mitsuba/hw/glprogram.h>
#include <mitsuba/hw/glsync.h>
#include <mitsuba/hw/font.h>
static mitsuba::PrimitiveThreadLocal<GLEWContextStruct> 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<const TriMesh *, GPUGeometry *>::iterator it = m_geometry.find(mesh);
if (it != m_geometry.end()) {
/* Draw using vertex buffer objects (bindless if supported) */
GLGeometry *geometry = static_cast<GLGeometry *>((*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<std::pair<const GPUGeometry *, Transform> > &geo) {
GLfloat temp[16];
GLRenderer::beginDrawingMeshes(true);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
std::vector<std::pair<const GPUGeometry *, Transform> >::const_iterator it;
if (m_capabilities->isSupported(RendererCapabilities::EBindless)) {
for (it = geo.begin(); it != geo.end(); ++it) {
const GLGeometry *geometry = static_cast<const GLGeometry *>(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<const GLGeometry *>(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; i<text.length(); i++) {
char character = text[i];
if (character == '\r')
continue;
if (character == '\n') {
pos.x = initial;
pos.y += (int) (font->getMaxVerticalBearing()*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 &center,
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