blob: 06aa4fd83bd95c82f0c4623e3f672011d5cb36c0 [file] [log] [blame]
/**
* projectM -- Milkdrop-esque visualisation SDK
* Copyright (C)2003-2004 projectM Team
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
* See 'LICENSE.txt' included within this release
*
*/
/**
* $Id: FBO.c,v 1.1.1.1 2005/12/23 18:05:00 psperl Exp $
*
* Render this methods
*/
#include <stdio.h>
#include <string.h>
//#include <GL/gl.h>
#include <iostream>
#include "Common.hpp"
#include "FBO.hpp"
RenderTarget::~RenderTarget()
{
glDeleteTextures (1, &this->textureID[0]);
#ifdef USE_FBO
if (useFBO)
{
glDeleteTextures (1, &this->textureID[1]);
glDeleteRenderbuffers (1, &this->depthb[0]);
glDeleteFramebuffers (1, &this->fbuffer[0]);
if (renderToTexture)
{
glDeleteTextures (1, &this->textureID[2]);
glDeleteRenderbuffers (1, &this->depthb[1]);
glDeleteFramebuffers (1, &this->fbuffer[1]);
}
}
#endif
}
GLuint RenderTarget::initRenderToTexture()
{
#ifdef USE_FBO
if (this->useFBO==1)
{
this->renderToTexture=1;
GLuint fb2, depth_rb2;
glGenFramebuffers (1, &fb2);
glBindFramebuffer (GL_FRAMEBUFFER, fb2);
glGenRenderbuffers (1, &depth_rb2);
glBindRenderbuffer (GL_RENDERBUFFER, depth_rb2);
glRenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, this->texsize,this->texsize );
glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depth_rb2);
this->fbuffer[1] = fb2;
this->depthb[1]= depth_rb2;
glGenTextures (1, &this->textureID[2]);
glBindTexture (GL_TEXTURE_2D, this->textureID[2]);
glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, texsize, texsize, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
#ifdef USE_GLES1
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
#else
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
#endif
glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, this->textureID[2], 0);
return this->textureID[2];
}
#endif
return -1;
}
/** Creates new pbuffers */
RenderTarget::RenderTarget (int texsize, int width, int height)
: useFBO (false)
{
int mindim = 0;
int origtexsize = 0;
this->renderToTexture = 0;
this->texsize = texsize;
#ifdef USE_FBO
// Forcibly disable FBO if user requested it but the video card / driver lacks
// the appropraite frame buffer extension.
#ifdef USE_GLES1
/*
* FBO support is never part of GLES1 core; check for extension.
*/
useFBO = !!strstr ((char const *) glGetString (GL_EXTENSIONS),
"GL_OES_framebuffer_object");
#else
/*
* If GL 3.0 or later is in scope, then framebuffer object support is present
* by default, and we just use the user's preference.
*/
{
long majorvers;
char const *glvers;
char *endptr;
glvers = glGetString (GL_VERSION);
majorvers = strtol (glvers, &endptr, 10);
if (endptr == glvers || *endptr != '.') {
/* Unparseable version number; force extension check. */
majorvers = 0;
}
if (majorvers < 3) {
/* GL 3.0 not in scope; check for FBO support by extension. */
useFBO = !!strstr ((char const *) glGetString (GL_EXTENSIONS),
"GL_EXT_framebuffer_object");
}
}
#endif /* USE_GLES1 */
if (useFBO)
{
GLuint fb, depth_rb, rgba_tex, other_tex;
glGenFramebuffers (1, &fb);
glBindFramebuffer (GL_FRAMEBUFFER, fb);
glGenRenderbuffers (1, &depth_rb);
glBindRenderbuffer (GL_RENDERBUFFER, depth_rb);
glRenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, this->texsize,this->texsize );
glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depth_rb);
this->fbuffer[0] = fb;
this->depthb[0]= depth_rb;
glGenTextures (1, &other_tex);
glBindTexture (GL_TEXTURE_2D,other_tex);
glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, texsize, texsize, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
//glGenerateMipmap (GL_TEXTURE_2D);
#ifdef USE_GLES1
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
#else
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
#endif
glGenTextures (1, &rgba_tex);
glBindTexture (GL_TEXTURE_2D, rgba_tex);
glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, texsize, texsize, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
//glGenerateMipmap (GL_TEXTURE_2D);
#ifdef USE_GLES1
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
#else
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
#endif
glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rgba_tex, 0);
this->textureID[0] = rgba_tex;
this->textureID[1] = other_tex;
GLenum status = glCheckFramebufferStatus (GL_FRAMEBUFFER);
glBindFramebuffer (GL_FRAMEBUFFER, 0);
if (status == GL_FRAMEBUFFER_COMPLETE) {
return;
}
std::cerr << "[projecM] warning: FBO support not detected. Using fallback." << std::endl;
}
#endif /* USE_FBO */
// Can reach here via two code paths:
// (1) useFBO was set to false externally by cmake / system setting / etc.
// (2) useFBO was true but forced to false as it failed to pass all the GLU extension checks.
/** Fallback pbuffer creation via teximage hack */
/** Check the texture size against the viewport size */
/** If the viewport is smaller, then we'll need to scale the texture size down */
/** If the viewport is larger, scale it up */
mindim = width < height ? width : height;
origtexsize = this->texsize;
this->texsize = nearestPower2 (mindim, SCALE_MINIFY);
glGenTextures (1, &this->textureID[0]);
glBindTexture (GL_TEXTURE_2D, this->textureID[0]);
//glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
//glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D (GL_TEXTURE_2D,
0,
GL_RGBA,
this->texsize, this->texsize,
0,
GL_RGBA,
GL_UNSIGNED_BYTE,
NULL);
return;
}
void
RenderTarget::fallbackRescale (int width, int height)
{
int mindim = width < height ? width : height;
int origtexsize = this->texsize;
this->texsize = nearestPower2 (mindim, SCALE_MINIFY);
if (origtexsize == texsize)
return;
/* Create the texture that will be bound to the render this */
/*
if (this->texsize != origtexsize) {
glDeleteTextures (1, &this->textureID[0]);
}
*/
glGenTextures (1, &this->textureID[0]);
glBindTexture (GL_TEXTURE_2D, this->textureID[0]);
//glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
//glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D (GL_TEXTURE_2D,
0,
GL_RGBA,
this->texsize, this->texsize,
0,
GL_RGBA,
GL_UNSIGNED_BYTE,
NULL);
}
/** Destroys the pbuffer */
/** Locks the pbuffer */
void
RenderTarget::lock()
{
#ifdef USE_FBO
if (this->useFBO)
{
glBindFramebuffer (GL_FRAMEBUFFER, this->fbuffer[0]);
}
#endif
}
/** Unlocks the pbuffer */
void
RenderTarget::unlock()
{
#ifdef USE_FBO
if (this->useFBO)
{
glBindTexture (GL_TEXTURE_2D, this->textureID[1]);
glCopyTexSubImage2D (GL_TEXTURE_2D,
0, 0, 0, 0, 0,
this->texsize, this->texsize);
glBindFramebuffer (GL_FRAMEBUFFER, 0);
return;
}
#endif
/** Fallback texture path */
glBindTexture (GL_TEXTURE_2D, this->textureID[0]);
glCopyTexSubImage2D (GL_TEXTURE_2D, 0, 0, 0, 0, 0, this->texsize, this->texsize);
}
/**
* Calculates the nearest power of two to the given number using the
* appropriate rule
*/
int
RenderTarget::nearestPower2 (int value, TextureScale scaleRule)
{
int x = value;
int power = 0;
while ((x & 0x01) != 1) {
x >>= 1;
}
if (x == 1) {
return value;
} else {
x = value;
while (x != 0) {
x >>= 1;
power++;
}
switch (scaleRule) {
case SCALE_NEAREST:
if (((1 << power) - value) <= (value - (1 << (power - 1)))) {
return 1 << power;
} else {
return 1 << (power - 1);
}
case SCALE_MAGNIFY:
return 1 << power;
case SCALE_MINIFY:
return 1 << (power - 1);
default:
break;
}
}
return 0;
}