FrameBufferObject.cc 11.4 KB
Newer Older
1
2
3
4
5
/***********************************************************************
 * Copyright 2011-2012 Computer Graphics Group RWTH Aachen University. *
 * All rights reserved.                                                *
 * Distributed under the terms of the MIT License (see LICENSE.TXT).   *
 **********************************************************************/
Robert Menzel's avatar
Robert Menzel committed
6

7
#include <ACGL/OpenGL/Objects/FrameBufferObject.hh>
Robert Menzel's avatar
Robert Menzel committed
8

9
using namespace ACGL;
Robert Menzel's avatar
Robert Menzel committed
10
using namespace ACGL::OpenGL;
Robert Menzel's avatar
Robert Menzel committed
11

12
int_t FrameBufferObject::getColorAttachmentIndexByName(const std::string& _name) const
13
14
15
16
{
     for(AttachmentVec::size_type i = 0; i < mColorAttachments.size(); i++)
     {
         if(mColorAttachments[i].name == _name)
Robert Menzel's avatar
Robert Menzel committed
17
             return (int_t) i;
18
19
20
21
     }

     return -1;
 }
22

Robert Menzel's avatar
Robert Menzel committed
23
24
bool FrameBufferObject::isFrameBufferObjectComplete() const
{
Janis Born's avatar
Janis Born committed
25
    bind();
Robert Menzel's avatar
Robert Menzel committed
26
27
28
29
30
31
32
33
34
35
    GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
    if (status != GL_FRAMEBUFFER_COMPLETE)
    {
        Utils::error() << "Failed to make complete FrameBufferObject object: ";
        if (status == GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT) {
            Utils::error() << "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT";
        } else if (status == GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT) {
            Utils::error() << "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT";
        } else if (status == GL_FRAMEBUFFER_UNSUPPORTED) {
            Utils::error() << "GL_FRAMEBUFFER_UNSUPPORTED";
36
#ifndef ACGL_OPENGLES_VERSION_20
Robert Menzel's avatar
Robert Menzel committed
37
38
        } else if (status == GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE) {
            Utils::error() << "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE";
39
#endif
Robert Menzel's avatar
Robert Menzel committed
40
41
42
43
44
45
46
47
48
        } else {
            Utils::error() << status;
        }
        Utils::error() << std::endl;
        return false;
    }
    return true;
}

49
void FrameBufferObject::validate(void) const
50
{
Robert Menzel's avatar
Robert Menzel committed
51
52
53
54
    // if OpenGL says were ok, return
    if (isFrameBufferObjectComplete()) return;

    // the above call will create some output, but let's try to get more infos:
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
    if(mColorAttachments.size() > 0)
    {
        int width  = -1;
        int height = -1;

        if(mColorAttachments[0].texture)
        {
            width  = mColorAttachments[0].texture->getWidth();
            height = mColorAttachments[0].texture->getHeight();
        }
        else
        {
            width  = mColorAttachments[0].renderBuffer->getWidth();
            height = mColorAttachments[0].renderBuffer->getHeight();
        }

        for(AttachmentVec::size_type k = 0; k < mColorAttachments.size(); k++)
        {
            bool fail = false;

            if(mColorAttachments[k].texture)
                fail = (mColorAttachments[k].texture->getWidth() != width || mColorAttachments[k].texture->getHeight() != height);
            else //otherwise its a RenderBuffer
                fail = (mColorAttachments[k].renderBuffer->getWidth() != width || mColorAttachments[k].renderBuffer->getHeight() != height);

            if(fail)
81
                Utils::error() << "FrameBufferObject validation failed: Color attachment "<< k << " has different size." << std::endl;
82
83
84
        }
    }
    else
85
        Utils::error() << "FrameBufferObject validation failed: No color attachments."<< std::endl;
86
}
87

Robert Menzel's avatar
Robert Menzel committed
88
bool FrameBufferObject::attachColorAttachment( const Attachment &_attachment )
89
{
Robert Menzel's avatar
Robert Menzel committed
90
    int realLocation = -1;
91
    GLint maxColorBuffers;
92
93
94
#ifdef ACGL_OPENGLES_VERSION_20
    maxColorBuffers = 1;
#else
95
    glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxColorBuffers);
96
97
#endif
    
Robert Menzel's avatar
Robert Menzel committed
98
99
100
    for (unsigned int i = 0; i < mColorAttachments.size(); ++i) {
        if (mColorAttachments[i].name == _attachment.name) {
            // replace this attachment
101
            GLuint newLocation = _attachment.location;
102
            if (newLocation > (GLuint) maxColorBuffers) {
103
104
105
                // we have to find a place, but luckily we can use the old location of the same-named attachment:
                newLocation = mColorAttachments[i].location;
            }
Robert Menzel's avatar
Robert Menzel committed
106
            mColorAttachments[i] = _attachment;
107
            mColorAttachments[i].location = newLocation;
Robert Menzel's avatar
Robert Menzel committed
108
109
110
111
112
            realLocation = i;
        }
    }
    if (realLocation == -1) {
        // it's a new attachment
113
        GLuint newLocation = _attachment.location;
114
        if (newLocation > (GLuint) maxColorBuffers) {
115
116
117
118
            // we have to find a place:
            newLocation = mColorAttachments.size();
        }

Robert Menzel's avatar
Robert Menzel committed
119
        realLocation = (int) mColorAttachments.size();
Robert Menzel's avatar
Robert Menzel committed
120
        mColorAttachments.push_back(_attachment);
121
        mColorAttachments[mColorAttachments.size()-1].location = newLocation;
Robert Menzel's avatar
Robert Menzel committed
122
    }
123

Robert Menzel's avatar
Robert Menzel committed
124
125
    // attach it to the OpenGL object:
    bind();
Robert Menzel's avatar
Robert Menzel committed
126
    openGLCriticalError();
Robert Menzel's avatar
Robert Menzel committed
127
128
129
130
131
    if (_attachment.renderBuffer) {
        // it's a renderBuffer
        glFramebufferRenderbuffer( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + realLocation, GL_RENDERBUFFER, _attachment.renderBuffer->getObjectName() );
    } else {
        // it's a texture
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
        GLenum textureTarget = _attachment.texture->getTarget();
        int dimensionality = 0;

        if (textureTarget == GL_TEXTURE_1D) {
            dimensionality = 1;
        } else if (textureTarget == GL_TEXTURE_2D || textureTarget == GL_TEXTURE_2D_MULTISAMPLE) {
            dimensionality = 2;
        } else if (textureTarget == GL_TEXTURE_3D) {
            dimensionality = 3;
        } else if (textureTarget == GL_TEXTURE_1D_ARRAY) {
            dimensionality = 2;
        } else if (textureTarget == GL_TEXTURE_2D_ARRAY || textureTarget == GL_TEXTURE_2D_MULTISAMPLE_ARRAY) {
            dimensionality = 3;
        } else if (textureTarget == GL_TEXTURE_RECTANGLE) {
            dimensionality = 2;
        } else if (textureTarget == GL_TEXTURE_BINDING_CUBE_MAP) {
            dimensionality = 2;
            textureTarget = _attachment.image.cubeMapFace;
        } else if (textureTarget == GL_TEXTURE_BINDING_CUBE_MAP_ARRAY) {
            dimensionality = 3;
            textureTarget = _attachment.image.cubeMapFace;
        } else {
            Utils::error() << "attachColorAttachment failed, texture target not supported" << std::endl;
        }

        if (dimensionality == 1) {
            glFramebufferTexture1D( GL_FRAMEBUFFER,
                                    GL_COLOR_ATTACHMENT0 + realLocation,
                                    textureTarget,
                                    _attachment.texture->getObjectName(),
                                    _attachment.image.mipmapLevel );
        } else if (dimensionality == 2) {
            glFramebufferTexture2D( GL_FRAMEBUFFER,
                                    GL_COLOR_ATTACHMENT0 + realLocation,
                                    textureTarget,
                                    _attachment.texture->getObjectName(),
                                    _attachment.image.mipmapLevel );
        } else if (dimensionality == 3) {
            glFramebufferTexture3D( GL_FRAMEBUFFER,
                                    GL_COLOR_ATTACHMENT0 + realLocation,
                                    textureTarget,
                                    _attachment.texture->getObjectName(),
                                    _attachment.image.layer,
                                    _attachment.image.mipmapLevel );
        } else {
            Utils::error() << "attachColorAttachment failed, texture target not supported" << std::endl;
        }

Robert Menzel's avatar
Robert Menzel committed
180
        //Utils::debug() << "glFramebufferTexture2D( " << _attachment.name << " to " << realLocation <<" )" << std::endl;
Robert Menzel's avatar
Robert Menzel committed
181
182
    }
    if (openGLCommonErrorOccured())
183
    {
Robert Menzel's avatar
Robert Menzel committed
184
185
186
        Utils::error() << "Attaching of render target to the FBO failed" << std::endl;
        return false;
    }
187

Robert Menzel's avatar
Robert Menzel committed
188
189
190
    remapAttachments();
    return true;
}
191

Robert Menzel's avatar
Robert Menzel committed
192
193
void FrameBufferObject::remapAttachments()
{
Janis Born's avatar
Janis Born committed
194
    GLint maxColorBuffers;
195
196
197
#ifdef ACGL_OPENGLES_VERSION_20
    maxColorBuffers = 1;
#else
Janis Born's avatar
Janis Born committed
198
    glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxColorBuffers);
199
200
#endif
    
Janis Born's avatar
Janis Born committed
201
202
    GLenum* bufferMappings = new GLenum[maxColorBuffers];

Robert Menzel's avatar
Robert Menzel committed
203
    int attachments = std::min( maxColorBuffers, (int) mColorAttachments.size() );
204

Robert Menzel's avatar
Robert Menzel committed
205
    for (int i = 0; i < maxColorBuffers; ++i) {
Robert Menzel's avatar
Robert Menzel committed
206
207
        bufferMappings[i] = GL_NONE;
    }
Robert Menzel's avatar
Robert Menzel committed
208
    for (int i = 0; i < attachments; ++i) {
Janis Born's avatar
Janis Born committed
209
210
211
212
213
        if (bufferMappings[ mColorAttachments[i].location ] != GL_NONE) {
            Utils::warning() << "FBO: Attachment mapping collision: Location " << mColorAttachments[i].location;
            Utils::warning() << " maps to both attachments " << mColorAttachments[i].name;
            Utils::warning() << " and " << mColorAttachments[bufferMappings[mColorAttachments[i].location] - GL_COLOR_ATTACHMENT0].name << std::endl;
        }
Robert Menzel's avatar
Robert Menzel committed
214
215
        bufferMappings[ mColorAttachments[i].location ] = GL_COLOR_ATTACHMENT0 + i;
    }
216

Robert Menzel's avatar
Robert Menzel committed
217
    // debug:
218
219
220
/*
    if (bufferMappings[0] == GL_NONE) {
    Utils::error() << "remapAttachments: " << std::endl;
Robert Menzel's avatar
Robert Menzel committed
221
222
    for (unsigned int i = 0; i < maxColorBuffers; ++i) {
        if (bufferMappings[i] == GL_NONE) {
223
            Utils::error() << "bufferMappings["<<i<<"] GL_NONE" << std::endl;
Robert Menzel's avatar
Robert Menzel committed
224
        } else {
225
            Utils::error() << "bufferMappings["<<i<<"] "<< bufferMappings[i]-GL_COLOR_ATTACHMENT0 << std::endl;
226
227
        }
    }
228
229
    }*/

Robert Menzel's avatar
Robert Menzel committed
230
231
232
    // end debug

    bind(); // glDrawBuffers will get part of the FBO state!
233
234
235
236
237
238
239
240
241
242
243
244
245

#ifdef __APPLE__
    // Somehow, Apple's current (OSX 10.7) OpenGL implementation skips over all
    // GL_NONE entries in the bufferMappings array if the only color attachment
    // specified is GL_COLOR_ATTACHMENT0.
    // This however causes that no change from the initial FBO state is
    // detected when the only color attachment should be assigned to a FragData
    // location other than 0.
    // Hotfix: Temporarily set all draw buffers to GL_NONE before setting the
    // actual configuration:
    glDrawBuffers(0, (GLenum*)NULL);
#endif // __APPLE__

246
#ifndef ACGL_OPENGLES_VERSION_20
Robert Menzel's avatar
Robert Menzel committed
247
    glDrawBuffers( maxColorBuffers, bufferMappings );
248
#endif
Janis Born's avatar
Janis Born committed
249
250

    delete[] bufferMappings;
251
252
}

Robert Menzel's avatar
Robert Menzel committed
253

254
255
256
257
258
259
260
261
void FrameBufferObject::setAttachmentLocations(ConstSharedLocationMappings _locationMappings)
{
    bool needsUpdate = false;

    for(AttachmentVec::size_type i = 0; i < mColorAttachments.size(); i++)
    {
        int_t location = _locationMappings->getLocation(mColorAttachments[i].name);

Robert Menzel's avatar
Robert Menzel committed
262
        if (location != -1) // is a mapping by that name specified?
263
        {
Robert Menzel's avatar
Robert Menzel committed
264
            mColorAttachments[i].location = location;
265
266
267
268
            needsUpdate = true;
        }
    }

Robert Menzel's avatar
Robert Menzel committed
269
    if(needsUpdate) remapAttachments();
270
271
272
}


273
SharedLocationMappings FrameBufferObject::getAttachmentLocations() const
Robert Menzel's avatar
Robert Menzel committed
274
275
{
    SharedLocationMappings locationMap = SharedLocationMappings( new LocationMappings() );
276

Robert Menzel's avatar
Robert Menzel committed
277
    for (AttachmentVec::size_type i = 0; i < mColorAttachments.size(); i++)
278
    {
Robert Menzel's avatar
Robert Menzel committed
279
280
        locationMap->setLocation( mColorAttachments[i].name, mColorAttachments[i].location );
        //ACGL::Utils::debug() << "locationMap->setLocation( "<<mColorAttachments[i].name<<", "<<mColorAttachments[i].location<<" );"<<std::endl;
281
282
    }

Robert Menzel's avatar
Robert Menzel committed
283
    return locationMap;
284
}
285
286
287
288
289
290

SharedTextureData FrameBufferObject::getImageData(GLsizei _width, GLsizei _height, GLint _x, GLint _y, GLenum _readBuffer)
{
    GLubyte* frameBufferData = new GLubyte[_width * _height * 3];

    glBindFramebuffer(GL_FRAMEBUFFER, 0);
291
292
    
#ifndef ACGL_OPENGLES_VERSION_20
293
294
    if(_readBuffer != GL_INVALID_ENUM)
        glReadBuffer(_readBuffer);
295
#endif
296
297
298
299
300
301
302
303
304
305
306
307
308
309

    glReadPixels(_x, _y, _width, _height, GL_RGB, GL_UNSIGNED_BYTE, frameBufferData);

    SharedTextureData texData = SharedTextureData(new TextureData());
    texData->setWidth(_width);
    texData->setHeight(_height);
    texData->setFormat(GL_RGB);
    texData->setType(GL_UNSIGNED_BYTE);
    texData->setData(frameBufferData); // frameBufferData memory will be managed by texData

    openGLRareError();

    return texData;
}