FrameBufferObject.cc 17.9 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>
8
#include <ACGL/OpenGL/HiLevelObjects/Viewport.hh>
Robert Menzel's avatar
Robert Menzel committed
9

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

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

     return -1;
 }
23

Robert Menzel's avatar
Robert Menzel committed
24
25
bool FrameBufferObject::isFrameBufferObjectComplete() const
{
Janis Born's avatar
Janis Born committed
26
    bind();
Robert Menzel's avatar
Robert Menzel committed
27
28
29
30
31
32
33
34
35
36
    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";
37
#ifndef ACGL_OPENGLES_VERSION_20
Robert Menzel's avatar
Robert Menzel committed
38
39
        } else if (status == GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE) {
            Utils::error() << "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE";
40
#endif
Robert Menzel's avatar
Robert Menzel committed
41
42
43
44
45
46
47
48
49
        } else {
            Utils::error() << status;
        }
        Utils::error() << std::endl;
        return false;
    }
    return true;
}

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

    // the above call will create some output, but let's try to get more infos:
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
81
    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)
82
                Utils::error() << "FrameBufferObject validation failed: Color attachment "<< k << " has different size." << std::endl;
83
84
85
        }
    }
    else
86
        Utils::error() << "FrameBufferObject validation failed: No color attachments."<< std::endl;
87
}
88

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

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

Robert Menzel's avatar
Robert Menzel committed
125
126
    // attach it to the OpenGL object:
    bind();
Robert Menzel's avatar
Robert Menzel committed
127
    openGLCriticalError();
Robert Menzel's avatar
Robert Menzel committed
128
129
130
131
132
    if (_attachment.renderBuffer) {
        // it's a renderBuffer
        glFramebufferRenderbuffer( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + realLocation, GL_RENDERBUFFER, _attachment.renderBuffer->getObjectName() );
    } else {
        // it's a texture
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
        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;
Robert Menzel's avatar
Robert Menzel committed
151
#if (ACGL_OPENGL_VERSION >= 40)
152
153
154
        } else if (textureTarget == GL_TEXTURE_BINDING_CUBE_MAP_ARRAY) {
            dimensionality = 3;
            textureTarget = _attachment.image.cubeMapFace;
Robert Menzel's avatar
Robert Menzel committed
155
#endif // OpenGL 4.0+
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
        } 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) {
173
174
175
176
177
178
            glFramebufferTextureLayer( GL_FRAMEBUFFER,
                                       GL_COLOR_ATTACHMENT0 + realLocation,
                                       _attachment.texture->getObjectName(),
                                       _attachment.image.mipmapLevel,
                                       _attachment.image.layer
                                       );
179
180
181
182
        } else {
            Utils::error() << "attachColorAttachment failed, texture target not supported" << std::endl;
        }

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

Robert Menzel's avatar
Robert Menzel committed
191
192
193
    remapAttachments();
    return true;
}
194

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

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

Robert Menzel's avatar
Robert Menzel committed
208
    for (int i = 0; i < maxColorBuffers; ++i) {
Robert Menzel's avatar
Robert Menzel committed
209
210
        bufferMappings[i] = GL_NONE;
    }
Robert Menzel's avatar
Robert Menzel committed
211
    for (int i = 0; i < attachments; ++i) {
Janis Born's avatar
Janis Born committed
212
213
214
215
216
        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
217
218
        bufferMappings[ mColorAttachments[i].location ] = GL_COLOR_ATTACHMENT0 + i;
    }
219

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

Robert Menzel's avatar
Robert Menzel committed
233
234
235
    // end debug

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

#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__

249
#ifndef ACGL_OPENGLES_VERSION_20
Robert Menzel's avatar
Robert Menzel committed
250
    glDrawBuffers( maxColorBuffers, bufferMappings );
251
#endif
Janis Born's avatar
Janis Born committed
252
253

    delete[] bufferMappings;
254
255
}

Robert Menzel's avatar
Robert Menzel committed
256

257
258
259
260
261
262
263
264
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
265
        if (location != -1) // is a mapping by that name specified?
266
        {
Robert Menzel's avatar
Robert Menzel committed
267
            mColorAttachments[i].location = location;
268
269
270
271
            needsUpdate = true;
        }
    }

Robert Menzel's avatar
Robert Menzel committed
272
    if(needsUpdate) remapAttachments();
273
274
275
}


276
SharedLocationMappings FrameBufferObject::getAttachmentLocations() const
Robert Menzel's avatar
Robert Menzel committed
277
278
{
    SharedLocationMappings locationMap = SharedLocationMappings( new LocationMappings() );
279

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

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

SharedTextureData FrameBufferObject::getImageData(GLsizei _width, GLsizei _height, GLint _x, GLint _y, GLenum _readBuffer)
{
Robert Menzel's avatar
Robert Menzel committed
291
292
293
294
295
296
297
298
299
300
    GLenum format = GL_RGB;

    //
    // glReadPixels aligns each pixel row for 4 bytes which can create gaps in each row
    // in case of 1-3 channel data (and one byte per channel)
    //
    size_t packingAlignment = 4; // can be 1,2,4,8
    size_t bytesOfDataPerRow = _width*getNumberOfChannels(format);
    size_t paddingPerRow = ( packingAlignment - (bytesOfDataPerRow % packingAlignment) ) % packingAlignment;
    GLubyte* frameBufferData = new GLubyte[_height * (bytesOfDataPerRow + paddingPerRow) ];
301
302

    glBindFramebuffer(GL_FRAMEBUFFER, 0);
303
304
    
#ifndef ACGL_OPENGLES_VERSION_20
305
306
    if(_readBuffer != GL_INVALID_ENUM)
        glReadBuffer(_readBuffer);
307
#endif
308

Robert Menzel's avatar
Robert Menzel committed
309
310
    glPixelStorei( GL_PACK_ALIGNMENT, (GLint)packingAlignment ); // 4 is the default
    glReadPixels(_x, _y, _width, _height, format, GL_UNSIGNED_BYTE, frameBufferData);
311
312
313
314

    SharedTextureData texData = SharedTextureData(new TextureData());
    texData->setWidth(_width);
    texData->setHeight(_height);
Robert Menzel's avatar
Robert Menzel committed
315
    texData->setFormat(format);
316
317
    texData->setType(GL_UNSIGNED_BYTE);
    texData->setData(frameBufferData); // frameBufferData memory will be managed by texData
Robert Menzel's avatar
Robert Menzel committed
318
    texData->setPadding(paddingPerRow);
319
320
321
322
323

    openGLRareError();

    return texData;
}
324
325
326
327
328
329
330
331
332

SharedTextureData FrameBufferObject::getImageData()
{
    Viewport currentViewport;
    currentViewport.setFromGLContext();

    return FrameBufferObject::getImageData( currentViewport.getWidth(), currentViewport.getHeight(), currentViewport.getOffsetX(), currentViewport.getOffsetY() );
}

333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452

//! clear only the depth buffer:
void FrameBufferObject::clearDepthBuffer()
{
    if (!mDepthAttachment.texture && !mDepthAttachment.renderBuffer) return;

    bind(); // NOTE: if the old binding gets restored (removing this side effect), update clearBuffers() as it relies on it!
    glClear( GL_DEPTH_BUFFER_BIT );
}

//! clear one specific color buffer:
void FrameBufferObject::clearBuffer( const std::string &_name )
{
    int index = getColorAttachmentIndexByName( _name );
    if (index == -1) {
        ACGL::Utils::error() << "can't clear color buffer " << _name << " - attachment does not exist" << std::endl;
    }

    bind();
    if (mColorAttachments[index].clearColor.mType == ClearColor::Float) {
        glClearBufferfv(  GL_COLOR, mColorAttachments[index].location, mColorAttachments[index].clearColor.mColor.floatColor);
    } else if (mColorAttachments[index].clearColor.mType == ClearColor::Integer) {
        glClearBufferiv(  GL_COLOR, mColorAttachments[index].location, mColorAttachments[index].clearColor.mColor.intColor);
    } else {
        glClearBufferuiv( GL_COLOR, mColorAttachments[index].location, mColorAttachments[index].clearColor.mColor.uintColor);
    }
}

//! clear all buffers, color and depth:
void FrameBufferObject::clearBuffers()
{
    clearDepthBuffer(); // will also bind this FBO
    for (unsigned int i = 0; i < mColorAttachments.size(); ++i) {
        if (mColorAttachments[i].clearColor.mType == ClearColor::Float) {
            glClearBufferfv(  GL_COLOR, mColorAttachments[i].location, mColorAttachments[i].clearColor.mColor.floatColor);
        } else if (mColorAttachments[i].clearColor.mType == ClearColor::Integer) {
            glClearBufferiv(  GL_COLOR, mColorAttachments[i].location, mColorAttachments[i].clearColor.mColor.intColor);
        } else {
            glClearBufferuiv( GL_COLOR, mColorAttachments[i].location, mColorAttachments[i].clearColor.mColor.uintColor);
        }
    }
}

//! sets the clear color for one buffer:
void FrameBufferObject::setClearColor( const std::string &_name, const glm::vec4 &_color )
{
    int index = getColorAttachmentIndexByName( _name );
    if (index == -1) {
        ACGL::Utils::error() << "can't set clear color of " << _name << " - attachment does not exist" << std::endl;
    }

    mColorAttachments[index].clearColor.mType = ClearColor::Float;
    mColorAttachments[index].clearColor.mColor.floatColor[0] = _color.r;
    mColorAttachments[index].clearColor.mColor.floatColor[1] = _color.g;
    mColorAttachments[index].clearColor.mColor.floatColor[2] = _color.b;
    mColorAttachments[index].clearColor.mColor.floatColor[3] = _color.a;
}
//! sets the clear color for one buffer:
void FrameBufferObject::setClearColor( const std::string &_name, const glm::ivec4 &_color )
{
    int index = getColorAttachmentIndexByName( _name );
    if (index == -1) {
        ACGL::Utils::error() << "can't set clear color of " << _name << " - attachment does not exist" << std::endl;
    }

    mColorAttachments[index].clearColor.mType = ClearColor::Integer;
    mColorAttachments[index].clearColor.mColor.intColor[0] = _color.r;
    mColorAttachments[index].clearColor.mColor.intColor[1] = _color.g;
    mColorAttachments[index].clearColor.mColor.intColor[2] = _color.b;
    mColorAttachments[index].clearColor.mColor.intColor[3] = _color.a;
}
//! sets the clear color for one buffer:
void FrameBufferObject::setClearColor( const std::string &_name, const glm::uvec4 &_color )
{
    int index = getColorAttachmentIndexByName( _name );
    if (index == -1) {
        ACGL::Utils::error() << "can't set clear color of " << _name << " - attachment does not exist" << std::endl;
    }

    mColorAttachments[index].clearColor.mType = ClearColor::UnsignedInteger;
    mColorAttachments[index].clearColor.mColor.uintColor[0] = _color.r;
    mColorAttachments[index].clearColor.mColor.uintColor[1] = _color.g;
    mColorAttachments[index].clearColor.mColor.uintColor[2] = _color.b;
    mColorAttachments[index].clearColor.mColor.uintColor[3] = _color.a;
}

//! sets the clear color for all color buffers:
void FrameBufferObject::setClearColor( const glm::vec4 &_color )
{
    for (unsigned int i = 0; i < mColorAttachments.size(); ++i) {
        mColorAttachments[i].clearColor.mType = ClearColor::Float;
        mColorAttachments[i].clearColor.mColor.floatColor[0] = _color.r;
        mColorAttachments[i].clearColor.mColor.floatColor[1] = _color.g;
        mColorAttachments[i].clearColor.mColor.floatColor[2] = _color.b;
        mColorAttachments[i].clearColor.mColor.floatColor[3] = _color.a;
    }
}
//! sets the clear color for all color buffers:
void FrameBufferObject::setClearColor( const glm::ivec4 &_color )
{
    for (unsigned int i = 0; i < mColorAttachments.size(); ++i) {
        mColorAttachments[i].clearColor.mType = ClearColor::Integer;
        mColorAttachments[i].clearColor.mColor.intColor[0] = _color.r;
        mColorAttachments[i].clearColor.mColor.intColor[1] = _color.g;
        mColorAttachments[i].clearColor.mColor.intColor[2] = _color.b;
        mColorAttachments[i].clearColor.mColor.intColor[3] = _color.a;
    }
}
//! sets the clear color for all color buffers:
void FrameBufferObject::setClearColor( const glm::uvec4 &_color )
{
    for (unsigned int i = 0; i < mColorAttachments.size(); ++i) {
        mColorAttachments[i].clearColor.mType = ClearColor::UnsignedInteger;
        mColorAttachments[i].clearColor.mColor.uintColor[0] = _color.r;
        mColorAttachments[i].clearColor.mColor.uintColor[1] = _color.g;
        mColorAttachments[i].clearColor.mColor.uintColor[2] = _color.b;
        mColorAttachments[i].clearColor.mColor.uintColor[3] = _color.a;
    }
}