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