661 lines
20 KiB
C++
661 lines
20 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>
|
|
#else
|
|
#include <GL/glew.h>
|
|
#endif
|
|
#include <mitsuba/hw/gltexture.h>
|
|
|
|
#ifndef GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_EXT
|
|
#define GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_EXT 0x8CD8
|
|
#endif
|
|
|
|
MTS_NAMESPACE_BEGIN
|
|
|
|
GLTexture::GLTexture(const std::string &name, Bitmap *bitmap)
|
|
: GPUTexture(name, bitmap), m_id(0), m_needsUpdate(true) {
|
|
}
|
|
|
|
void GLTexture::init() {
|
|
if (m_fbType == ENone)
|
|
Log(ETrace, "Uploading a texture : %s", toString().c_str());
|
|
else
|
|
Log(ETrace, "Creating a framebuffer : %s", toString().c_str());
|
|
|
|
lookupGLConstants();
|
|
|
|
/* Generate an identifier */
|
|
glGenTextures(1, &m_id);
|
|
|
|
/* Bind to the texture */
|
|
glBindTexture(m_glType, m_id);
|
|
|
|
/* Set the texture filtering / wrapping modes
|
|
(don't do this for multisample textures)*/
|
|
if (!((m_fbType & EColorBuffer) && m_samples > 1))
|
|
configureTexture(); /* Multisample textures don't have parameters */
|
|
|
|
if (m_samples > 1) {
|
|
int samples;
|
|
glGetIntegerv(GL_MAX_SAMPLES_EXT, &samples);
|
|
if (m_samples > samples) {
|
|
Log(EWarn, "Attempted to create a multisample framebuffer "
|
|
"with an unsupported # of samples (requested=%i, supported=%i)",
|
|
m_samples, samples);
|
|
m_samples = samples;
|
|
}
|
|
}
|
|
|
|
|
|
if (m_fbType == ENone) {
|
|
Assert(m_samples == 1);
|
|
refresh();
|
|
} else {
|
|
/* Create the FBO and bind it */
|
|
glGenFramebuffersEXT(1, &m_fboId);
|
|
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_fboId);
|
|
|
|
AssertEx(glIsFramebufferEXT(m_fboId), "Creating an FBO failed");
|
|
bool depthAsTexture = m_fbType & EDepthBuffer;
|
|
|
|
switch (m_fbType) {
|
|
case EColorAndDepthBuffer:
|
|
case EColorBuffer: {
|
|
if (m_type == ETexture2D) {
|
|
if (!depthAsTexture) {
|
|
glGenRenderbuffersEXT(1, &m_depthId);
|
|
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, m_depthId);
|
|
if (m_samples == 1)
|
|
glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT,
|
|
GL_DEPTH_COMPONENT, m_size.x, m_size.y);
|
|
else
|
|
glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT,
|
|
m_samples, GL_DEPTH_COMPONENT, m_size.x, m_size.y);
|
|
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT,
|
|
GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, m_depthId);
|
|
} else {
|
|
glGenTextures(1, &m_depthId);
|
|
glBindTexture(m_glType, m_depthId);
|
|
configureTexture();
|
|
glTexParameteri(m_glType, GL_TEXTURE_COMPARE_MODE, GL_NONE);
|
|
glTexParameteri(m_glType, GL_DEPTH_TEXTURE_MODE, GL_LUMINANCE);
|
|
if (m_samples == 1)
|
|
glTexImage2D(m_glType, 0, GL_DEPTH_COMPONENT24, m_size.x, m_size.y,
|
|
0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL);
|
|
else
|
|
glTexImage2DMultisample(m_glType,
|
|
m_samples, GL_DEPTH_COMPONENT24, m_size.x, m_size.y, GL_FALSE);
|
|
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,
|
|
GL_DEPTH_ATTACHMENT, m_glType, m_depthId, 0);
|
|
glBindTexture(m_glType, m_id);
|
|
}
|
|
|
|
if (m_samples == 1)
|
|
glTexImage2D(m_glType, 0, m_internalFormat, m_size.x, m_size.y,
|
|
0, m_format, m_dataFormat, NULL);
|
|
else
|
|
glTexImage2DMultisample(m_glType,
|
|
m_samples, m_internalFormat, m_size.x, m_size.y, GL_FALSE);
|
|
|
|
if (isMipMapped())
|
|
glGenerateMipmapEXT(m_glType);
|
|
|
|
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,
|
|
GL_COLOR_ATTACHMENT0_EXT, m_glType, m_id, 0);
|
|
} else if (m_type == ETextureCubeMap) {
|
|
Assert(m_size.x == m_size.y && isPow2(m_size.x));
|
|
Assert(m_fbType == EColorBuffer);
|
|
Assert(m_samples == 1);
|
|
|
|
for (int i=0; i<6; i++)
|
|
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, m_internalFormat,
|
|
m_size.x, m_size.y, 0, m_format, m_dataFormat, NULL);
|
|
|
|
if (isMipMapped())
|
|
glGenerateMipmapEXT(m_glType);
|
|
|
|
/* Generate an identifier */
|
|
glGenTextures(1, &m_depthId);
|
|
glBindTexture(m_glType, m_depthId);
|
|
glTexParameteri(m_glType, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
glTexParameteri(m_glType, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
|
|
for (int i=0; i<6; i++)
|
|
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_DEPTH_COMPONENT24,
|
|
m_size.x, m_size.y, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL);
|
|
|
|
if (glewIsSupported("GL_EXT_geometry_shader4"))
|
|
activateSide(-1);
|
|
else
|
|
activateSide(0);
|
|
} else {
|
|
Log(EError, "Unsupported texture type!");
|
|
}
|
|
}
|
|
break;
|
|
case EDepthBuffer:
|
|
Assert(m_samples == 1);
|
|
if (m_depthMode == ECompare) {
|
|
glTexParameteri(m_glType, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
|
|
glTexParameteri(m_glType, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE);
|
|
}
|
|
|
|
if (m_type == ETexture2D) {
|
|
/* Allocate the texture memory */
|
|
glTexImage2D(m_glType, 0, m_internalFormat,
|
|
m_size.x, m_size.y, 0, GL_DEPTH_COMPONENT,
|
|
GL_UNSIGNED_BYTE, NULL);
|
|
|
|
/* Attach the texture as a depth target */
|
|
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,
|
|
GL_DEPTH_ATTACHMENT_EXT, m_glType, m_id, 0);
|
|
} else if (m_type == ETextureCubeMap) {
|
|
Assert(m_size.x == m_size.y && isPow2(m_size.x));
|
|
for (int i=0; i<6; i++)
|
|
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, m_internalFormat,
|
|
m_size.x, m_size.y, 0, m_format, m_dataFormat, NULL);
|
|
|
|
if (glewIsSupported("GL_EXT_geometry_shader4"))
|
|
activateSide(-1);
|
|
else
|
|
activateSide(0);
|
|
} else {
|
|
Log(EError, "Unsupported texture type!");
|
|
}
|
|
|
|
glDrawBuffer(GL_NONE);
|
|
glReadBuffer(GL_NONE);
|
|
break;
|
|
default:
|
|
Log(EError, "Invalid render buffer type!");
|
|
}
|
|
|
|
GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
|
|
switch (status) {
|
|
case GL_FRAMEBUFFER_COMPLETE_EXT:
|
|
break;
|
|
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:
|
|
Log(EError, "FBO Error: Incomplete attachment!");
|
|
case GL_FRAMEBUFFER_UNSUPPORTED_EXT:
|
|
Log(EError, "FBO Error: Unsupported framebuffer format!");
|
|
case GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_EXT:
|
|
Log(EError, "FBO Error: Incomplete framebuffer - duplicate attachment!");
|
|
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT:
|
|
Log(EError, "FBO Error: Incomplete framebuffer - missing attachment!");
|
|
case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:
|
|
Log(EError, "FBO Error: Incomplete framebuffer - invalid dimensions!");
|
|
case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:
|
|
Log(EError, "FBO Error: Incomplete framebuffer - no draw buffer!");
|
|
case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:
|
|
Log(EError, "FBO Error: Incomplete framebuffer - invalid formats!");
|
|
case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:
|
|
Log(EError, "FBO Error: Incomplete framebuffer - no readbuffer!");
|
|
case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT:
|
|
Log(EError, "FBO Error: Incomplete multisample framebuffer!");
|
|
case GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS:
|
|
Log(EError, "FBO Error: Incomplete layer targets!");
|
|
default:
|
|
Log(EError, "FBO Error: Unknown error status (0x%x)!", status);
|
|
}
|
|
|
|
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, GL_NONE);
|
|
}
|
|
}
|
|
|
|
void GLTexture::refresh() {
|
|
Bitmap *bitmap = getBitmap();
|
|
|
|
/* Bind to the texture */
|
|
glBindTexture(m_glType, m_id);
|
|
|
|
if (m_type == ETexture1D) {
|
|
Assert((isPow2(m_size.x) && m_size.y == 1)
|
|
|| (isPow2(m_size.y) && m_size.x == 1));
|
|
|
|
if (isMipMapped()) {
|
|
/* Let GLU generate mipmaps for us */
|
|
gluBuild1DMipmaps(m_glType, m_internalFormat, m_size.x == 1 ? m_size.y : m_size.x,
|
|
m_format, m_dataFormat, bitmap->getData());
|
|
} else {
|
|
glTexImage1D(m_glType, 0, m_internalFormat, m_size.x == 1 ? m_size.y : m_size.x,
|
|
0, m_format, m_dataFormat, bitmap->getData());
|
|
}
|
|
} else if (m_type == ETexture2D) {
|
|
//Assert(m_size.x == m_size.y);
|
|
|
|
/* Anisotropic texture filtering */
|
|
float anisotropy = (float) getMaxAnisotropy();
|
|
if (anisotropy > 1.0f) {
|
|
if (isMipMapped() && m_filterType == EMipMapLinear) {
|
|
/* Only use anisotropy when it makes sense - otherwise some
|
|
GL implementations will enforce mipmapping */
|
|
glTexParameterf(m_glType, GL_TEXTURE_MAX_ANISOTROPY_EXT,
|
|
anisotropy);
|
|
}
|
|
}
|
|
|
|
glTexImage2D(m_glType, 0, m_internalFormat, m_size.x, m_size.y,
|
|
0, m_format, m_dataFormat, bitmap->getData());
|
|
|
|
if (isMipMapped())
|
|
glGenerateMipmapEXT(m_glType);
|
|
} else if (m_type == ETextureCubeMap) {
|
|
Assert(bitmap != NULL);
|
|
Assert(bitmap->getWidth() == bitmap->getHeight());
|
|
Assert(isPow2(bitmap->getWidth()));
|
|
if (isMipMapped())
|
|
glTexParameteri(m_glType, GL_GENERATE_MIPMAP, GL_TRUE);
|
|
|
|
for (int i=0; i<6; i++) {
|
|
Bitmap *bitmap = getBitmap(i);
|
|
|
|
GLuint pos;
|
|
switch (i) {
|
|
case ECubeMapPositiveX: pos = GL_TEXTURE_CUBE_MAP_POSITIVE_X; break;
|
|
case ECubeMapNegativeX: pos = GL_TEXTURE_CUBE_MAP_NEGATIVE_X; break;
|
|
case ECubeMapPositiveY: pos = GL_TEXTURE_CUBE_MAP_POSITIVE_Y_EXT; break;
|
|
case ECubeMapNegativeY: pos = GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_EXT; break;
|
|
case ECubeMapPositiveZ: pos = GL_TEXTURE_CUBE_MAP_POSITIVE_Z_EXT; break;
|
|
case ECubeMapNegativeZ: pos = GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_EXT; break;
|
|
default: Log(EError, "Unknown cube map index"); return;
|
|
};
|
|
|
|
glTexImage2D(pos, 0, m_internalFormat, bitmap->getWidth(), bitmap->getHeight(),
|
|
0, m_format, m_dataFormat, bitmap->getData());
|
|
}
|
|
} else {
|
|
Log(EError, "Unknown texture type!");
|
|
}
|
|
}
|
|
|
|
void GLTexture::lookupGLConstants() {
|
|
m_dataFormat = GL_UNSIGNED_BYTE;
|
|
|
|
/* Convert the texture type */
|
|
switch (m_type) {
|
|
case ETexture1D: m_glType = GL_TEXTURE_1D; break;
|
|
case ETexture2D:
|
|
if (m_samples == 1)
|
|
m_glType = GL_TEXTURE_2D;
|
|
else
|
|
m_glType = GL_TEXTURE_2D_MULTISAMPLE;
|
|
break;
|
|
case ETexture3D: m_glType = GL_TEXTURE_3D; break;
|
|
case ETextureCubeMap: m_glType = GL_TEXTURE_CUBE_MAP_EXT; break;
|
|
default: Log(EError, "Invalid texture type specified"); return;
|
|
}
|
|
|
|
/* Convert the texture format */
|
|
switch (getFormat()) {
|
|
case ER8G8B8:
|
|
m_format = GL_RGB;
|
|
m_internalFormat = GL_RGB;
|
|
break;
|
|
case ER8G8B8A8:
|
|
m_format = GL_RGBA;
|
|
m_internalFormat = GL_RGBA;
|
|
break;
|
|
case EL8:
|
|
m_format = GL_LUMINANCE;
|
|
m_internalFormat = GL_LUMINANCE8;
|
|
break;
|
|
case EL8A8:
|
|
m_format = GL_LUMINANCE_ALPHA;
|
|
m_internalFormat = GL_LUMINANCE8_ALPHA8;
|
|
break;
|
|
case EDepth:
|
|
m_format = GL_DEPTH_COMPONENT;
|
|
m_internalFormat = GL_DEPTH_COMPONENT32;
|
|
break;
|
|
case EFloat16L:
|
|
m_format = GL_LUMINANCE;
|
|
m_internalFormat = GL_RGBA16F_ARB;
|
|
m_dataFormat = GL_FLOAT;
|
|
break;
|
|
case EFloat16RGB:
|
|
m_format = GL_RGB;
|
|
m_internalFormat = GL_RGBA16F_ARB;
|
|
m_dataFormat = GL_FLOAT;
|
|
break;
|
|
case EFloat16RGBA:
|
|
m_format = GL_RGBA;
|
|
m_internalFormat = GL_RGBA16F_ARB;
|
|
m_dataFormat = GL_FLOAT;
|
|
break;
|
|
case EFloat32L:
|
|
m_format = GL_LUMINANCE;
|
|
m_internalFormat = GL_RGBA32F_ARB;
|
|
m_dataFormat = GL_FLOAT;
|
|
break;
|
|
case EFloat32RGB:
|
|
m_format = GL_RGB;
|
|
m_internalFormat = GL_RGBA32F_ARB;
|
|
m_dataFormat = GL_FLOAT;
|
|
break;
|
|
case EFloat32RGBA:
|
|
m_format = GL_RGBA;
|
|
m_internalFormat = GL_RGBA32F_ARB;
|
|
m_dataFormat = GL_FLOAT;
|
|
break;
|
|
default: Log(EError, "Invalid texture format specified"); return;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
void GLTexture::configureTexture() {
|
|
int wrap, mag_filter, min_filter;
|
|
|
|
/* Convert the texture coordinate wrapping type */
|
|
switch (getWrapType()) {
|
|
case EClamp: wrap = GL_CLAMP; break;
|
|
case EClampToEdge: wrap = GL_CLAMP_TO_EDGE; break;
|
|
case EClampToBorder: wrap = GL_CLAMP_TO_BORDER; break;
|
|
case ERepeat: wrap = GL_REPEAT; break;
|
|
case EMirroredRepeat: wrap = GL_MIRRORED_REPEAT_ARB; break;
|
|
default: Log(EError, "Invalid texture wrap type specified"); return;
|
|
}
|
|
|
|
/* Convert the texture filter type */
|
|
switch (m_filterType) {
|
|
case ENearest:
|
|
mag_filter = GL_NEAREST;
|
|
min_filter = isMipMapped() ? GL_NEAREST_MIPMAP_NEAREST : GL_NEAREST;
|
|
break;
|
|
case ELinear:
|
|
mag_filter = GL_LINEAR;
|
|
min_filter = isMipMapped() ? GL_NEAREST_MIPMAP_LINEAR : GL_LINEAR;
|
|
break;
|
|
case EMipMapNearest:
|
|
if (!isMipMapped()) {
|
|
mag_filter = GL_LINEAR;
|
|
min_filter = GL_LINEAR;
|
|
break;
|
|
}
|
|
|
|
mag_filter = GL_LINEAR;
|
|
min_filter = GL_LINEAR_MIPMAP_NEAREST;
|
|
break;
|
|
case EMipMapLinear:
|
|
if (!isMipMapped()) {
|
|
mag_filter = GL_LINEAR;
|
|
min_filter = GL_LINEAR;
|
|
break;
|
|
}
|
|
|
|
mag_filter = GL_LINEAR;
|
|
min_filter = GL_LINEAR_MIPMAP_LINEAR;
|
|
break;
|
|
default:
|
|
Log(EError, "Invalid filter type specified");
|
|
return;
|
|
}
|
|
|
|
/* Set the filter type */
|
|
glTexParameteri(m_glType, GL_TEXTURE_MAG_FILTER, mag_filter);
|
|
glTexParameteri(m_glType, GL_TEXTURE_MIN_FILTER, min_filter);
|
|
|
|
/* Set the texcoord wrapping type */
|
|
if (m_type == ETexture1D) {
|
|
glTexParameteri(m_glType, GL_TEXTURE_WRAP_S, wrap);
|
|
} else if (m_type == ETexture2D) {
|
|
glTexParameteri(m_glType, GL_TEXTURE_WRAP_S, wrap);
|
|
glTexParameteri(m_glType, GL_TEXTURE_WRAP_T, wrap);
|
|
} else if (m_type == ETextureCubeMap) {
|
|
glTexParameteri(m_glType, GL_TEXTURE_WRAP_S, wrap);
|
|
glTexParameteri(m_glType, GL_TEXTURE_WRAP_T, wrap);
|
|
glTexParameteri(m_glType, GL_TEXTURE_WRAP_R, wrap);
|
|
}
|
|
}
|
|
|
|
void GLTexture::download(Bitmap *bitmap) {
|
|
Assert(m_type == ETexture2D); // Only type supported so far
|
|
|
|
if (bitmap == NULL)
|
|
bitmap = getBitmap();
|
|
|
|
Assert(bitmap != NULL);
|
|
unsigned char *temp, *pixels = bitmap->getData();
|
|
|
|
activateTarget();
|
|
GLenum format = 0, dataFormat = GL_UNSIGNED_BYTE;
|
|
switch (bitmap->getBitsPerPixel()) {
|
|
case 8:
|
|
if (m_fbType == EDepthBuffer)
|
|
format = GL_DEPTH_COMPONENT;
|
|
else
|
|
format = GL_LUMINANCE;
|
|
break;
|
|
case 24: format = GL_RGB; break;
|
|
case 32: format = GL_RGBA; break;
|
|
case 96: format = GL_RGB; dataFormat = GL_FLOAT; break;
|
|
case 128: format = GL_RGBA; dataFormat = GL_FLOAT; break;
|
|
}
|
|
|
|
glReadPixels(0, 0, bitmap->getWidth(), bitmap->getHeight(), format,
|
|
dataFormat, pixels);
|
|
releaseTarget();
|
|
|
|
/* OpenGL has associates (0, 0) with the lower left position
|
|
and the resulting bitmap is thus vertically flipped. */
|
|
int rowSize = bitmap->getWidth() * bitmap->getBitsPerPixel() / 8,
|
|
halfHeight= bitmap->getHeight()/2;
|
|
temp = new unsigned char[rowSize];
|
|
for (int i=0, j=bitmap->getHeight()-1; i<halfHeight; ++i) {
|
|
memcpy(temp, pixels + i * rowSize, rowSize);
|
|
memcpy(pixels + i * rowSize, pixels + j * rowSize, rowSize);
|
|
memcpy(pixels + j * rowSize, temp, rowSize);
|
|
j--;
|
|
}
|
|
delete[] temp;
|
|
}
|
|
|
|
Spectrum GLTexture::getPixel(int x, int y) const {
|
|
Assert(m_fbType == EColorBuffer);
|
|
float pixels[3];
|
|
Spectrum result;
|
|
|
|
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_fboId);
|
|
glPushAttrib(GL_VIEWPORT_BIT);
|
|
glViewport(0, 0, m_size.x, m_size.y);
|
|
glReadPixels(x, y, 1, 1, GL_RGB, GL_FLOAT, &pixels);
|
|
glPopAttrib();
|
|
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, GL_NONE);
|
|
|
|
result.fromLinearRGB(pixels[0], pixels[1], pixels[2]);
|
|
return result;
|
|
}
|
|
|
|
|
|
void GLTexture::activateTarget() {
|
|
Assert(m_fbType != ENone);
|
|
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_fboId);
|
|
glPushAttrib(GL_VIEWPORT_BIT);
|
|
glViewport(0, 0, m_size.x, m_size.y);
|
|
}
|
|
|
|
void GLTexture::activateSide(int side) {
|
|
if (side == -1) {
|
|
if (m_fbType == EColorBuffer) {
|
|
glFramebufferTextureEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, m_id, 0);
|
|
glFramebufferTextureEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, m_depthId, 0);
|
|
} else if (m_fbType == EDepthBuffer) {
|
|
glFramebufferTextureEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, m_id, 0);
|
|
} else {
|
|
Log(EError, "Unsupported framebuffer type!");
|
|
}
|
|
} else {
|
|
if (m_fbType == EColorBuffer) {
|
|
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
|
|
GL_TEXTURE_CUBE_MAP_POSITIVE_X + side, m_id, 0);
|
|
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
|
|
GL_TEXTURE_CUBE_MAP_POSITIVE_X + side, m_depthId, 0);
|
|
} else if (m_fbType == EDepthBuffer) {
|
|
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
|
|
GL_TEXTURE_CUBE_MAP_POSITIVE_X + side, m_id, 0);
|
|
} else {
|
|
Log(EError, "Unsupported framebuffer type!");
|
|
}
|
|
}
|
|
}
|
|
|
|
void GLTexture::setTargetRegion(const Point2i &offset, const Vector2i &size) {
|
|
glViewport(offset.x, offset.y, size.x, size.y);
|
|
}
|
|
|
|
void GLTexture::releaseTarget() {
|
|
Assert(m_fbType != ENone);
|
|
glPopAttrib();
|
|
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, GL_NONE);
|
|
if (isMipMapped())
|
|
m_needsUpdate = true;
|
|
}
|
|
|
|
void GLTexture::bind(int textureUnit, int textureIndex) const {
|
|
/* Bind to the texture */
|
|
if (GLEW_VERSION_1_3) {
|
|
m_textureUnits.get().insert(textureUnit);
|
|
glActiveTexture(GL_TEXTURE0 + textureUnit);
|
|
} else {
|
|
if (textureUnit != 0)
|
|
Log(EError, "Multitexturing is not supported");
|
|
}
|
|
|
|
glEnable(m_glType);
|
|
if (textureIndex == 1 && m_fbType == EColorAndDepthBuffer) {
|
|
glBindTexture(m_glType, m_depthId);
|
|
} else {
|
|
glBindTexture(m_glType, m_id);
|
|
}
|
|
|
|
if (isMipMapped() && m_needsUpdate) {
|
|
glGenerateMipmapEXT(m_glType);
|
|
m_needsUpdate = false;
|
|
}
|
|
}
|
|
|
|
void GLTexture::unbind() const {
|
|
if (GLEW_VERSION_1_3) {
|
|
std::set<int> &textureUnits = m_textureUnits.get();
|
|
for (std::set<int>::iterator it = textureUnits.begin();
|
|
it != textureUnits.end(); ++it) {
|
|
glActiveTexture(GL_TEXTURE0 + *it);
|
|
glDisable(m_glType);
|
|
}
|
|
textureUnits.clear();
|
|
} else {
|
|
glDisable(m_glType);
|
|
}
|
|
}
|
|
|
|
void GLTexture::cleanup() {
|
|
if (m_id == 0)
|
|
return;
|
|
if (m_fbType != ENone) {
|
|
Log(ETrace, "Freeing framebuffer \"%s\"", m_name.c_str());
|
|
if (m_fbType == EColorAndDepthBuffer || (m_fbType == EColorBuffer && m_type == ETextureCubeMap)) {
|
|
glDeleteTextures(1, &m_depthId);
|
|
} else if (m_fbType == EColorBuffer) {
|
|
glDeleteRenderbuffersEXT(1, &m_depthId);
|
|
}
|
|
|
|
glDeleteFramebuffersEXT(1, &m_fboId);
|
|
} else {
|
|
Log(ETrace, "Freeing texture \"%s\"", m_name.c_str());
|
|
}
|
|
glDeleteTextures(1, &m_id);
|
|
m_id = 0;
|
|
}
|
|
|
|
void GLTexture::blit(GPUTexture *target, int what) const {
|
|
GLTexture *dest = static_cast<GLTexture *>(target);
|
|
Assert(m_fbType != ENone && (dest == NULL || dest->m_fbType != ENone));
|
|
|
|
if (!GLEW_EXT_framebuffer_blit)
|
|
Log(EError, "Your OpenGL driver does not support fast "
|
|
"framebuffer blitting!");
|
|
|
|
glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, m_fboId);
|
|
glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT,
|
|
(dest == NULL) ? GL_NONE : dest->m_fboId);
|
|
|
|
int flags = 0;
|
|
if (what & EDepthBuffer)
|
|
flags |= GL_DEPTH_BUFFER_BIT;
|
|
if (what & EColorBuffer)
|
|
flags |= GL_COLOR_BUFFER_BIT;
|
|
|
|
if (!dest) {
|
|
glBlitFramebufferEXT(0, 0, m_size.x, m_size.y, 0, 0,
|
|
m_size.x, m_size.y, flags, GL_NEAREST);
|
|
} else {
|
|
glBlitFramebufferEXT(0, 0, m_size.x, m_size.y, 0, 0,
|
|
dest->m_size.x, dest->m_size.y, flags,
|
|
(m_size == dest->m_size) ? GL_NEAREST : GL_LINEAR);
|
|
}
|
|
|
|
glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, GL_NONE);
|
|
glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, GL_NONE);
|
|
}
|
|
|
|
void GLTexture::blit(GPUTexture *target, int what,
|
|
const Point2i &sourceOffset, const Vector2i &sourceSize,
|
|
const Point2i &destOffset, const Vector2i &destSize) const {
|
|
GLTexture *dest = static_cast<GLTexture *>(target);
|
|
Assert(m_fbType != ENone && (dest == NULL || dest->m_fbType != ENone));
|
|
|
|
if (!GLEW_EXT_framebuffer_blit)
|
|
Log(EError, "Your OpenGL driver does not support fast "
|
|
"framebuffer blitting!");
|
|
|
|
glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, m_fboId);
|
|
glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT,
|
|
(dest == NULL) ? GL_NONE : dest->m_fboId);
|
|
|
|
int flags = 0;
|
|
if (what & EDepthBuffer)
|
|
flags |= GL_DEPTH_BUFFER_BIT;
|
|
if (what & EColorBuffer)
|
|
flags |= GL_COLOR_BUFFER_BIT;
|
|
|
|
glBlitFramebufferEXT(sourceOffset.x, sourceOffset.y,
|
|
sourceOffset.x + sourceSize.x, sourceOffset.x + sourceSize.y,
|
|
destOffset.x, destOffset.y, destOffset.x + destSize.x,
|
|
destOffset.y + destSize.y, flags,
|
|
(sourceSize == destSize) ? GL_NEAREST : GL_LINEAR);
|
|
|
|
glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, GL_NONE);
|
|
glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, GL_NONE);
|
|
}
|
|
|
|
void GLTexture::clear() {
|
|
Assert(m_fbType != ENone);
|
|
glClear(GL_DEPTH_BUFFER_BIT
|
|
| ((m_fbType & EColorBuffer) ? GL_COLOR_BUFFER_BIT : 0));
|
|
}
|
|
|
|
GLTexture::~GLTexture() {
|
|
cleanup();
|
|
}
|
|
|
|
MTS_IMPLEMENT_CLASS(GLTexture, false, Texture)
|
|
MTS_NAMESPACE_END
|