1 // OpenCSG - library for image-based CSG rendering for OpenGL
2 // Copyright (C) 2002-2016, Florian Kirsch,
3 // Hasso-Plattner-Institute at the University of Potsdam, Germany
4 //
5 // This library is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License,
7 // Version 2, as published by the Free Software Foundation.
8 // As a special exception, you have permission to link this library
9 // with the CGAL library and distribute executables.
10 //
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software Foundation,
18 // Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 
20 //
21 // channelManager.cpp
22 //
23 
24 #include "opencsgConfig.h"
25 #include "channelManager.h"
26 
27 #include <GL/glew.h>
28 #ifdef _WIN32
29 #include <GL/wglew.h>
30 #elif !defined(__APPLE__)
31 #include <GL/glxew.h>
32 #endif
33 
34 #include "context.h"
35 #include "offscreenBuffer.h"
36 #include "openglHelper.h"
37 #include "settings.h"
38 #include <cassert>
39 
40 namespace OpenCSG {
41 
42     bool ChannelManager::gInUse = false;
43 
44     namespace {
45 
nextPow2(int value)46         int nextPow2(int value) {
47             if(value <= 0) { return 0; }
48             int result = 1;
49             while(result < value) {
50                 result <<= 1;
51             }
52             return result;
53         }
54 
defaults()55         void defaults() {
56             glViewport(OpenGL::canvasPos[0], OpenGL::canvasPos[1], OpenGL::canvasPos[2], OpenGL::canvasPos[3]);
57             glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
58             glClearDepth(1.0);
59             glClearStencil(0);
60             glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
61             glDepthMask(GL_TRUE);
62             glStencilMask(0xffffffff);
63             glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
64             glEnable(GL_DEPTH_TEST);
65         }
66 
67         template<int FRAMES>
68         class MaximumMemorizer {
69             int mMax;
70             int mSecondMax;
71             int mCounter;
72         public:
MaximumMemorizer()73             MaximumMemorizer() : mMax(0), mSecondMax(-1), mCounter(0) { }
newValue(int v)74             void newValue(int v) {
75                 if (v>=mMax) {
76                     mMax = v;
77                     mSecondMax = -1;
78                     mCounter = 0;
79                 } else {
80                     if (v>mSecondMax) {
81                         mSecondMax = v;
82                     }
83                     if (++mCounter >= FRAMES) {
84                         mMax = mSecondMax;
85                         mSecondMax = -1;
86                         mCounter = 0;
87                     }
88                 }
89             }
getMax() const90             int getMax() const {
91                 return mMax;
92             }
93         };
94 
95     } // unnamed namespace
96 
ChannelManager()97     ChannelManager::ChannelManager()
98       : mOffscreenBuffer(0)
99       , mInOffscreenBuffer(false)
100       , mFaceOrientation(GL_CCW)
101       , mCurrentChannel(NoChannel)
102       , mOccupiedChannels(NoChannel)
103     {
104         glPushAttrib(GL_ALL_ATTRIB_BITS);
105         glDisable(GL_LIGHTING);
106         glDisable(GL_TEXTURE_1D);
107         glDisable(GL_TEXTURE_2D);
108         if (GLEW_ARB_texture_rectangle || GLEW_EXT_texture_rectangle || GLEW_NV_texture_rectangle)
109             glDisable(GL_TEXTURE_RECTANGLE_ARB);
110         glDisable(GL_TEXTURE_3D); // OpenGL 1.2 - take this as given
111         if (GLEW_ARB_texture_cube_map)
112             glDisable(GL_TEXTURE_CUBE_MAP_ARB);
113         glDisable(GL_BLEND);
114 
115         GLint faceOrientation;
116         glGetIntegerv(GL_FRONT_FACE, &faceOrientation);
117         mFaceOrientation = static_cast<GLenum>(faceOrientation);
118 
119         glGetFloatv(GL_MODELVIEW_MATRIX, OpenGL::modelview);
120         glGetFloatv(GL_PROJECTION_MATRIX, OpenGL::projection);
121         glGetIntegerv(GL_VIEWPORT, OpenGL::canvasPos);
122 
123         if (glIsEnabled(GL_SCISSOR_TEST)) {
124             glGetIntegerv(GL_SCISSOR_BOX, OpenGL::scissorPos);
125         } else {
126             OpenGL::scissorPos[0] = OpenGL::canvasPos[0];
127             OpenGL::scissorPos[1] = OpenGL::canvasPos[1];
128             OpenGL::scissorPos[2] = OpenGL::canvasPos[2];
129             OpenGL::scissorPos[3] = OpenGL::canvasPos[3];
130         }
131     }
132 
init()133     bool ChannelManager::init() {
134 
135         assert(!gInUse);
136         if (gInUse)
137             return false;
138         gInUse = true;
139 
140         OffscreenType newOffscreenType = static_cast<OffscreenType>(getOption(OffscreenSetting));
141 
142         if (   newOffscreenType == OpenCSG::AutomaticOffscreenType
143             || newOffscreenType == OpenCSG::FrameBufferObject
144         ) {
145             if (GLEW_ARB_framebuffer_object) {
146                 newOffscreenType = OpenCSG::FrameBufferObjectARB;
147             }
148             else
149             if (   GLEW_EXT_framebuffer_object
150                 && GLEW_EXT_packed_depth_stencil
151             ) {
152                 newOffscreenType = OpenCSG::FrameBufferObjectEXT;
153             }
154             else
155             if (newOffscreenType == OpenCSG::AutomaticOffscreenType
156 #ifndef OPENCSG_HAVE_PBUFFER
157                 && false
158 #else
159 #ifdef WIN32
160                 && WGLEW_ARB_pbuffer
161                 && WGLEW_ARB_pixel_format
162 #elif !defined(__APPLE__)
163                 && GLXEW_SGIX_pbuffer
164                 && GLXEW_SGIX_fbconfig
165 #else
166                 && false
167 #endif
168 #endif // OPENCSG_HAVE_PBUFFER
169             ) {
170                 newOffscreenType = OpenCSG::PBuffer;
171             }
172             else {
173                 // At least one set of the above OpenGL extensions is required
174                 return false;
175             }
176         }
177 
178         mOffscreenBuffer = OpenGL::getOffscreenBuffer(newOffscreenType);
179 
180         if (!mOffscreenBuffer)
181         {
182             // Creating the offscreen buffer failed, maybe the OpenGL extension
183             // for the specific offscreen buffer type is not supported
184             return false;
185         }
186 
187         if (!mOffscreenBuffer->ReadCurrent())
188         {
189             return false;
190         }
191 
192         const int dx = OpenGL::canvasPos[2] - OpenGL::canvasPos[0];
193         const int dy = OpenGL::canvasPos[3] - OpenGL::canvasPos[1];
194 
195         int tx = dx;
196         int ty = dy;
197         // We don't need to enlarge the texture to the next largest power-of-two size if:
198         // - any of the texture rectangle extensions is supported
199         //   (texture rectangle is no problem for FBO; for pbuffers we fallback to copy-to-texture
200         //    if the required WGL-extensions for texture rectangle are missing - always on Linux)
201         // - Otherwise for FBO, if we have the GLEW_ARB_texture_non_power_of_two extension
202         // Negating this gives the following expression from hell:
203         if (   !GLEW_ARB_texture_rectangle
204             && !GLEW_EXT_texture_rectangle
205             && !GLEW_NV_texture_rectangle
206             && (newOffscreenType == OpenCSG::PBuffer || !GLEW_ARB_texture_non_power_of_two)
207         ) {
208             // blow up the texture to legal power-of-two size :-(
209             tx = nextPow2(dx);
210             ty = nextPow2(dy);
211         }
212 
213         // The following implements a heuristic that makes the offscreen buffer
214         // smaller if the size of the buffer has been bigger than necessary
215         // in x- or y- direction for resizeOffscreenBufferLimit frames.
216         //
217         // this permits to use OpenCSG for CSG rendering in different
218         // canvases with different sizes without permanent expensive
219         // resizing of the offscreen buffer for every frame.
220         //
221         // possible improvements:
222         //   - allow the user to define the resizeOffscreenBufferLimit?
223         static const unsigned int resizeOffscreenBufferLimit = 64;
224 
225         static MaximumMemorizer<resizeOffscreenBufferLimit> sizeX;
226         static MaximumMemorizer<resizeOffscreenBufferLimit> sizeY;
227         // tx == ty == 0 happens if the window is minimized, in this case don't touch a thing
228         if (tx != 0 && ty != 0) {
229             sizeX.newValue(tx);
230             sizeY.newValue(ty);
231         }
232 
233         bool rebuild = false;
234 
235         if (!mOffscreenBuffer->IsInitialized())
236         {
237             if (!mOffscreenBuffer->Initialize(sizeX.getMax(), sizeY.getMax(), true, false)) {
238                 // Initializing the offscreen buffer failed, maybe the OpenGL extension
239                 // for the specific offscreen buffer type is not supported
240                 return false;
241             }
242             rebuild = true;
243         }
244         // tx == ty == 0 happens if the window is minimized, in this case don't touch a thing
245         else if (tx != 0 && ty != 0 &&
246                     (   mOffscreenBuffer->GetWidth() != sizeX.getMax()
247                      || mOffscreenBuffer->GetHeight() != sizeY.getMax()
248                 )   )
249         {
250             if (!mOffscreenBuffer->Resize(sizeX.getMax(), sizeY.getMax())) {
251                 // Resizing the offscreen buffer failed, maybe the OpenGL extension
252                 // for the specific offscreen buffer type is not supported. More
253                 // likely this is a programming error in Resize().
254                 return false;
255             }
256             rebuild = true;
257         }
258 
259         if (rebuild) {
260             // assert(gOffscreenBuffer->HasStencil());
261             mOffscreenBuffer->BeginCapture();
262             defaults();
263             glGetIntegerv(GL_STENCIL_BITS, &OpenGL::stencilBits);
264             OpenGL::stencilMax = 1 << OpenGL::stencilBits;
265             OpenGL::stencilMask = OpenGL::stencilMax - 1;
266             mOffscreenBuffer->EndCapture();
267             mOffscreenBuffer->Bind();
268             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
269             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
270         }
271 
272         mInOffscreenBuffer = false;
273         mCurrentChannel = NoChannel;
274         mOccupiedChannels = NoChannel;
275 
276         return true;
277     }
278 
~ChannelManager()279     ChannelManager::~ChannelManager() {
280         glPopAttrib();
281         assert(gInUse);
282         gInUse = false;
283     }
284 
find() const285     Channel ChannelManager::find() const {
286 
287         Channel channel = NoChannel;
288 
289         // find free channel
290         if ((mOccupiedChannels & Alpha) == 0) {
291             channel = Alpha;
292         }  else if (GLEW_ARB_texture_env_dot3) {
293             if ((mOccupiedChannels & Red) == 0)   {
294                 channel = Red;
295             } else if ((mOccupiedChannels & Green) == 0) {
296                 channel = Green;
297             } else if ((mOccupiedChannels & Blue) == 0)  {
298                 channel = Blue;
299             }
300         }
301 
302         return channel;
303     }
304 
request()305     Channel ChannelManager::request() {
306         if (!mInOffscreenBuffer) {
307             mOffscreenBuffer->BeginCapture();
308             if (mOffscreenBuffer->haveSeparateContext()) {
309                 glFrontFace(mFaceOrientation);
310             }
311 
312             mInOffscreenBuffer = true;
313 
314             mCurrentChannel = NoChannel;
315             mOccupiedChannels = NoChannel;
316         }
317 
318         if (mOffscreenBuffer->haveSeparateContext()) {
319             glViewport(OpenGL::canvasPos[0], OpenGL::canvasPos[1], OpenGL::canvasPos[2], OpenGL::canvasPos[3]);
320             glMatrixMode(GL_PROJECTION);
321             glLoadMatrixf(OpenGL::projection);
322             glMatrixMode(GL_MODELVIEW);
323             glLoadMatrixf(OpenGL::modelview);
324         }
325 
326         mCurrentChannel = find();
327         mOccupiedChannels |= mCurrentChannel;
328         return mCurrentChannel;
329     }
330 
current() const331     Channel ChannelManager::current() const {
332         return mCurrentChannel;
333     }
334 
occupied() const335     std::vector<Channel> ChannelManager::occupied() const {
336 
337         std::vector<Channel> result;
338         result.reserve(4);
339 
340         if ((mOccupiedChannels & Alpha) != 0) {
341             result.push_back(Alpha);
342         }
343         if ((mOccupiedChannels & Red) != 0) {
344             result.push_back(Red);
345         }
346         if ((mOccupiedChannels & Green) != 0) {
347             result.push_back(Green);
348         }
349         if ((mOccupiedChannels & Blue) != 0) {
350             result.push_back(Blue);
351         }
352 
353         return result;
354     }
355 
free()356     void ChannelManager::free() {
357         if (mInOffscreenBuffer) {
358             mOffscreenBuffer->EndCapture();
359             mInOffscreenBuffer = false;
360         }
361 
362         merge();
363     }
364 
renderToChannel(bool on)365     void ChannelManager::renderToChannel(bool on) {
366 
367         if (on) {
368             switch (mCurrentChannel) {
369                 case NoChannel:
370                     glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
371                     break;
372                 case Alpha:
373                     glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE);
374                     break;
375                 case Blue:
376                     glColorMask(GL_FALSE, GL_FALSE, GL_TRUE, GL_FALSE);
377                     break;
378                 case Green:
379                     glColorMask(GL_FALSE, GL_TRUE, GL_FALSE, GL_FALSE);
380                     break;
381                 case Red:
382                     glColorMask(GL_TRUE, GL_FALSE, GL_FALSE, GL_FALSE);
383                     break;
384                 case AllChannels:
385                     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
386                     break;
387                 }
388             }
389         else {
390             glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
391         }
392     }
393 
394 
setupProjectiveTexture(bool fixedFunction)395     void ChannelManager::setupProjectiveTexture(bool fixedFunction)
396     {
397         static const float splane[4] = { 1.0f, 0.0f, 0.0f, 0.0f };
398         static const float tplane[4] = { 0.0f, 1.0f, 0.0f, 0.0f };
399         static const float rplane[4] = { 0.0f, 0.0f, 1.0f, 0.0f };
400         static const float qplane[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
401 
402         mOffscreenBuffer->Bind();
403         mOffscreenBuffer->EnableTextureTarget();
404 
405         if (fixedFunction)
406         {
407             glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
408             glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
409             glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
410             glTexGeni(GL_Q, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
411             glTexGenfv(GL_S, GL_EYE_PLANE, splane);
412             glTexGenfv(GL_T, GL_EYE_PLANE, tplane);
413             glTexGenfv(GL_R, GL_EYE_PLANE, rplane);
414             glTexGenfv(GL_Q, GL_EYE_PLANE, qplane);
415             glEnable(GL_TEXTURE_GEN_S);
416             glEnable(GL_TEXTURE_GEN_T);
417             glEnable(GL_TEXTURE_GEN_R);
418             glEnable(GL_TEXTURE_GEN_Q);
419         }
420 
421         glMatrixMode(GL_TEXTURE);
422 
423         const int dx = OpenGL::canvasPos[2] - OpenGL::canvasPos[0];
424         const int dy = OpenGL::canvasPos[3] - OpenGL::canvasPos[1];
425 
426         // with NV_texture_rectangle texture coordinates range between
427         // 0 and dx resp. dy
428         float factorX = static_cast<float>(dx);
429         float factorY = static_cast<float>(dy);
430 
431         // RenderTexture has a slight flaw: if you request a power-of-two texture
432         // by coincidence even though you expected a NPOT texture, you will not
433         // automatically notice that. That's for a bug that only happened at
434         // 512x512 canvas resolution.
435         // Therefore we do not check for the extension, but simply for the texture format
436         // Update (08.04.2004): Fixed the flaw, but kept checking the texture format.
437         // Actually that seems safer, since it should work always
438         if (!isRectangularTexture()) {
439             // with ordinary pow-of-two texture coordinates are between 0 and 1
440             // but we must assure only the used part of the texture is taken.
441             factorX /= static_cast<float>(mOffscreenBuffer->GetWidth());
442             factorY /= static_cast<float>(mOffscreenBuffer->GetHeight());
443         }
444 
445         float   texCorrect[16] = { factorX, 0.0f, 0.0f, 0.0f,
446                                    0.0f, factorY, 0.0f, 0.0f,
447                                    0.0f,    0.0f, 1.0f, 0.0f,
448                                    0.0f,    0.0f, 0.0f, 1.0f };
449 
450         static const float p2ndc[16] = { 0.5f, 0.0f, 0.0f, 0.0f,
451                                          0.0f, 0.5f, 0.0f, 0.0f,
452                                          0.0f, 0.0f, 0.5f, 0.0f,
453                                          0.5f, 0.5f, 0.5f, 1.0f };
454         glPushMatrix();
455         glLoadMatrixf(texCorrect);
456         glMultMatrixf(p2ndc);
457         if (fixedFunction)
458         {
459             glMultMatrixf(OpenGL::projection);
460             glMultMatrixf(OpenGL::modelview);
461         }
462         glMatrixMode(GL_MODELVIEW);
463     }
464 
resetProjectiveTexture(bool fixedFunction)465     void ChannelManager::resetProjectiveTexture(bool fixedFunction)
466     {
467         if (fixedFunction && !mOffscreenBuffer->haveSeparateContext())
468         {
469             glDisable(GL_TEXTURE_GEN_S);
470             glDisable(GL_TEXTURE_GEN_T);
471             glDisable(GL_TEXTURE_GEN_R);
472             glDisable(GL_TEXTURE_GEN_Q);
473         }
474 
475         glMatrixMode(GL_TEXTURE);
476         glPopMatrix();
477         glMatrixMode(GL_MODELVIEW);
478 
479         mOffscreenBuffer->DisableTextureTarget();
480     }
481 
setupTexEnv(Channel channel)482     void ChannelManager::setupTexEnv(Channel channel) {
483 
484         if (channel == Alpha) {
485             glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
486         } else {
487             // replicate color into alpha
488             if (GLEW_ARB_texture_env_dot3) {
489                 switch (channel) {
490                 case Red:
491                     glColor3f(1.0f, 0.5f, 0.5f);
492                     break;
493                 case Green:
494                     glColor3f(0.5f, 1.0f, 0.5f);
495                     break;
496                 case Blue:
497                     glColor3f(0.5f, 0.5f, 1.0f);
498                     break;
499                 default:
500                     // should not happen!
501                     assert(0);
502                 }
503             } else {
504                 // should not happen!
505                 assert(0);
506             }
507 
508             glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
509             glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_DOT3_RGBA_ARB);
510             glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
511             glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
512             glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_PRIMARY_COLOR);
513             glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_EXT, GL_SRC_COLOR);
514         }
515     }
516 
isRectangularTexture() const517     bool ChannelManager::isRectangularTexture() const
518     {
519         return mOffscreenBuffer->GetTextureTarget() != GL_TEXTURE_2D;
520     }
521 
522 
523 
524 
ChannelManagerForBatches()525     ChannelManagerForBatches::ChannelManagerForBatches() :
526         ChannelManager(),
527         mPrimitives(std::vector<std::pair<std::vector<Primitive*>, int> >(AllChannels + 1)) {
528     }
529 
store(Channel channel,const std::vector<Primitive * > & primitives,int layer)530     void ChannelManagerForBatches::store(Channel channel, const std::vector<Primitive*>& primitives, int layer) {
531         mPrimitives[channel] = std::make_pair(primitives, layer);
532     }
533 
getPrimitives(Channel channel) const534     const std::vector<Primitive*> ChannelManagerForBatches::getPrimitives(Channel channel) const {
535         return mPrimitives[channel].first;
536     }
537 
getLayer(Channel channel) const538     int ChannelManagerForBatches::getLayer(Channel channel) const {
539         return mPrimitives[channel].second;
540     }
541 
clear()542     void ChannelManagerForBatches::clear() {
543         mPrimitives = std::vector<std::pair<std::vector<Primitive*>, int> >(AllChannels + 1);
544     }
545 
546 } // namespace OpenCSG
547 
548