[5] | 1 | /* |
---|
| 2 | ----------------------------------------------------------------------------- |
---|
| 3 | This source file is part of OGRE |
---|
| 4 | (Object-oriented Graphics Rendering Engine) |
---|
| 5 | For the latest info, see http://www.ogre3d.org/ |
---|
| 6 | |
---|
| 7 | Copyright (c) 2000-2006 Torus Knot Software Ltd |
---|
| 8 | Also see acknowledgements in Readme.html |
---|
| 9 | |
---|
| 10 | This program is free software; you can redistribute it and/or modify it under |
---|
| 11 | the terms of the GNU Lesser General Public License as published by the Free Software |
---|
| 12 | Foundation; either version 2 of the License, or (at your option) any later |
---|
| 13 | version. |
---|
| 14 | |
---|
| 15 | This program is distributed in the hope that it will be useful, but WITHOUT |
---|
| 16 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
---|
| 17 | FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. |
---|
| 18 | |
---|
| 19 | You should have received a copy of the GNU Lesser General Public License along with |
---|
| 20 | this program; if not, write to the Free Software Foundation, Inc., 59 Temple |
---|
| 21 | Place - Suite 330, Boston, MA 02111-1307, USA, or go to |
---|
| 22 | http://www.gnu.org/copyleft/lesser.txt. |
---|
| 23 | |
---|
| 24 | You may alternatively use this source under the terms of a specific version of |
---|
| 25 | the OGRE Unrestricted License provided you have obtained such a license from |
---|
| 26 | Torus Knot Software Ltd. |
---|
| 27 | ----------------------------------------------------------------------------- |
---|
| 28 | */ |
---|
| 29 | |
---|
| 30 | #include "OgreException.h" |
---|
| 31 | #include "OgreLogManager.h" |
---|
| 32 | #include "OgreStringConverter.h" |
---|
| 33 | #include "OgreRoot.h" |
---|
| 34 | |
---|
| 35 | #include "OgreOSXGLSupport.h" |
---|
| 36 | #include "OgreOSXCarbonWindow.h" |
---|
| 37 | #include "OgreOSXCocoaWindow.h" |
---|
| 38 | |
---|
| 39 | #include "OgreGLTexture.h" |
---|
| 40 | #include "OgreOSXRenderTexture.h" |
---|
| 41 | |
---|
| 42 | #include <OpenGL/OpenGL.h> |
---|
| 43 | #include <mach-o/dyld.h> |
---|
| 44 | |
---|
| 45 | namespace Ogre { |
---|
| 46 | |
---|
| 47 | OSXGLSupport::OSXGLSupport() : mAPI(""), mContextType("") |
---|
| 48 | { |
---|
| 49 | } |
---|
| 50 | |
---|
| 51 | OSXGLSupport::~OSXGLSupport() |
---|
| 52 | { |
---|
| 53 | } |
---|
| 54 | |
---|
| 55 | void OSXGLSupport::addConfig( void ) |
---|
| 56 | { |
---|
| 57 | ConfigOption optFullScreen; |
---|
| 58 | ConfigOption optVideoMode; |
---|
| 59 | ConfigOption optBitDepth; |
---|
| 60 | ConfigOption optFSAA; |
---|
| 61 | ConfigOption optRTTMode; |
---|
| 62 | |
---|
| 63 | // FS setting possiblities |
---|
| 64 | optFullScreen.name = "Full Screen"; |
---|
| 65 | optFullScreen.possibleValues.push_back( "Yes" ); |
---|
| 66 | optFullScreen.possibleValues.push_back( "No" ); |
---|
| 67 | optFullScreen.currentValue = "No"; |
---|
| 68 | optFullScreen.immutable = false; |
---|
| 69 | |
---|
| 70 | optBitDepth.name = "Colour Depth"; |
---|
| 71 | optBitDepth.possibleValues.push_back( "32" ); |
---|
| 72 | optBitDepth.possibleValues.push_back( "16" ); |
---|
| 73 | optBitDepth.currentValue = "32"; |
---|
| 74 | optBitDepth.immutable = false; |
---|
| 75 | |
---|
| 76 | mOptions[ optFullScreen.name ] = optFullScreen; |
---|
| 77 | mOptions[ optBitDepth.name ] = optBitDepth; |
---|
| 78 | |
---|
| 79 | CGLRendererInfoObj rend; |
---|
| 80 | long nrend; |
---|
| 81 | CGLQueryRendererInfo(CGDisplayIDToOpenGLDisplayMask(kCGDirectMainDisplay), &rend, &nrend); |
---|
| 82 | |
---|
| 83 | long maxSamples; |
---|
| 84 | CGLDescribeRenderer(rend, 0, kCGLRPMaxSamples, &maxSamples); |
---|
| 85 | |
---|
| 86 | //FSAA possibilities |
---|
| 87 | optFSAA.name = "FSAA"; |
---|
| 88 | optFSAA.possibleValues.push_back( "0" ); |
---|
| 89 | |
---|
| 90 | switch( maxSamples ) |
---|
| 91 | { |
---|
| 92 | case 6: |
---|
| 93 | optFSAA.possibleValues.push_back( "2" ); |
---|
| 94 | optFSAA.possibleValues.push_back( "4" ); |
---|
| 95 | optFSAA.possibleValues.push_back( "6" ); |
---|
| 96 | break; |
---|
| 97 | case 4: |
---|
| 98 | optFSAA.possibleValues.push_back( "2" ); |
---|
| 99 | optFSAA.possibleValues.push_back( "4" ); |
---|
| 100 | break; |
---|
| 101 | case 2: |
---|
| 102 | optFSAA.possibleValues.push_back( "2" ); |
---|
| 103 | break; |
---|
| 104 | default: break; |
---|
| 105 | } |
---|
| 106 | |
---|
| 107 | optFSAA.currentValue = "0"; |
---|
| 108 | optFSAA.immutable = false; |
---|
| 109 | |
---|
| 110 | mOptions[ optFSAA.name ] = optFSAA; |
---|
| 111 | |
---|
| 112 | // Video mode possiblities |
---|
| 113 | optVideoMode.name = "Video Mode"; |
---|
| 114 | optVideoMode.immutable = false; |
---|
| 115 | |
---|
| 116 | CFArrayRef displayModes = CGDisplayAvailableModes(CGMainDisplayID()); |
---|
| 117 | CFIndex numModes = CFArrayGetCount(displayModes); |
---|
| 118 | CFMutableArrayRef goodModes = NULL; |
---|
| 119 | goodModes = CFArrayCreateMutable(kCFAllocatorDefault, numModes, NULL); |
---|
| 120 | |
---|
| 121 | // Grab all the available display modes, then weed out duplicates... |
---|
| 122 | for(int i = 0; i < numModes; ++i) |
---|
| 123 | { |
---|
| 124 | CFDictionaryRef modeInfo = (CFDictionaryRef)CFArrayGetValueAtIndex(displayModes, i); |
---|
| 125 | |
---|
| 126 | Boolean safeForHardware = _getDictionaryBoolean(modeInfo, kCGDisplayModeIsSafeForHardware); |
---|
| 127 | Boolean stretched = _getDictionaryBoolean(modeInfo, kCGDisplayModeIsStretched); |
---|
| 128 | Boolean skipped = false; |
---|
| 129 | |
---|
| 130 | if((safeForHardware) || (!stretched)) |
---|
| 131 | { |
---|
| 132 | long width = _getDictionaryLong(modeInfo, kCGDisplayWidth); |
---|
| 133 | long height = _getDictionaryLong(modeInfo, kCGDisplayHeight); |
---|
| 134 | |
---|
| 135 | for(int j = 0; j < CFArrayGetCount(goodModes); ++j) |
---|
| 136 | { |
---|
| 137 | CFDictionaryRef otherMode = (CFDictionaryRef)CFArrayGetValueAtIndex(goodModes, j); |
---|
| 138 | |
---|
| 139 | long otherWidth = _getDictionaryLong(otherMode, kCGDisplayWidth); |
---|
| 140 | long otherHeight = _getDictionaryLong(otherMode, kCGDisplayHeight); |
---|
| 141 | |
---|
| 142 | // If we find a duplicate then skip this mode |
---|
| 143 | if((otherWidth == width) && (otherHeight == height)) |
---|
| 144 | skipped = true; |
---|
| 145 | } |
---|
| 146 | |
---|
| 147 | // This is a new mode, so add it to our goodModes array |
---|
| 148 | if(!skipped) |
---|
| 149 | CFArrayAppendValue(goodModes, modeInfo); |
---|
| 150 | } |
---|
| 151 | } |
---|
| 152 | |
---|
| 153 | // Sort the modes... |
---|
| 154 | CFArraySortValues(goodModes, CFRangeMake(0, CFArrayGetCount(goodModes)), |
---|
| 155 | (CFComparatorFunction)_compareModes, NULL); |
---|
| 156 | |
---|
| 157 | // Now pull the modes out and put them into optVideoModes |
---|
| 158 | for(int i = 0; i < CFArrayGetCount(goodModes); ++i) |
---|
| 159 | { |
---|
| 160 | CFDictionaryRef resolution = (CFDictionaryRef)CFArrayGetValueAtIndex(goodModes, i); |
---|
| 161 | |
---|
| 162 | long fWidth = _getDictionaryLong(resolution, kCGDisplayWidth); |
---|
| 163 | long fHeight = _getDictionaryLong(resolution, kCGDisplayHeight); |
---|
| 164 | |
---|
| 165 | String resoString = StringConverter::toString(fWidth) + " x " + StringConverter::toString(fHeight); |
---|
| 166 | optVideoMode.possibleValues.push_back(resoString); |
---|
| 167 | |
---|
| 168 | //LogManager::getSingleton().logMessage( "Added resolution: " + resoString); |
---|
| 169 | } |
---|
| 170 | |
---|
| 171 | optRTTMode.name = "RTT Preferred Mode"; |
---|
| 172 | optRTTMode.possibleValues.push_back( "FBO" ); |
---|
| 173 | optRTTMode.possibleValues.push_back( "PBuffer" ); |
---|
| 174 | optRTTMode.possibleValues.push_back( "Copy" ); |
---|
| 175 | optRTTMode.currentValue = "PBuffer"; |
---|
| 176 | optRTTMode.immutable = false; |
---|
| 177 | |
---|
| 178 | |
---|
| 179 | mOptions[optFullScreen.name] = optFullScreen; |
---|
| 180 | mOptions[optVideoMode.name] = optVideoMode; |
---|
| 181 | mOptions[optFSAA.name] = optFSAA; |
---|
| 182 | mOptions[optRTTMode.name] = optRTTMode; |
---|
| 183 | |
---|
| 184 | } |
---|
| 185 | |
---|
| 186 | String OSXGLSupport::validateConfig( void ) |
---|
| 187 | { |
---|
| 188 | return String( "" ); |
---|
| 189 | } |
---|
| 190 | |
---|
| 191 | RenderWindow* OSXGLSupport::createWindow( bool autoCreateWindow, GLRenderSystem* renderSystem, const String& windowTitle ) |
---|
| 192 | { |
---|
| 193 | if( autoCreateWindow ) |
---|
| 194 | { |
---|
| 195 | ConfigOptionMap::iterator opt = mOptions.find( "Full Screen" ); |
---|
| 196 | if( opt == mOptions.end() ) |
---|
| 197 | OGRE_EXCEPT( Exception::ERR_RENDERINGAPI_ERROR, "Can't find full screen options!", "OSXGLSupport::createWindow" ); |
---|
| 198 | bool fullscreen = ( opt->second.currentValue == "Yes" ); |
---|
| 199 | |
---|
| 200 | opt = mOptions.find( "Video Mode" ); |
---|
| 201 | if( opt == mOptions.end() ) |
---|
| 202 | OGRE_EXCEPT( Exception::ERR_RENDERINGAPI_ERROR, "Can't find video mode options!", "OSXGLSupport::createWindow" ); |
---|
| 203 | String val = opt->second.currentValue; |
---|
| 204 | String::size_type pos = val.find( 'x' ); |
---|
| 205 | if( pos == String::npos ) |
---|
| 206 | OGRE_EXCEPT( Exception::ERR_RENDERINGAPI_ERROR, "Invalid Video Mode provided", "OSXGLSupport::createWindow" ); |
---|
| 207 | |
---|
| 208 | unsigned int w = StringConverter::parseUnsignedInt( val.substr( 0, pos ) ); |
---|
| 209 | unsigned int h = StringConverter::parseUnsignedInt( val.substr( pos + 1 ) ); |
---|
| 210 | |
---|
| 211 | // Parse FSAA config |
---|
| 212 | NameValuePairList winOptions; |
---|
| 213 | winOptions[ "title" ] = windowTitle; |
---|
| 214 | int fsaa_x_samples = 0; |
---|
| 215 | opt = mOptions.find( "FSAA" ); |
---|
| 216 | if( opt != mOptions.end() ) |
---|
| 217 | { |
---|
| 218 | winOptions[ "FSAA" ] = opt->second.currentValue; |
---|
| 219 | } |
---|
| 220 | |
---|
| 221 | return renderSystem->createRenderWindow( windowTitle, w, h, fullscreen, &winOptions ); |
---|
| 222 | } |
---|
| 223 | else |
---|
| 224 | { |
---|
| 225 | // XXX What is the else? |
---|
| 226 | return NULL; |
---|
| 227 | } |
---|
| 228 | } |
---|
| 229 | |
---|
| 230 | RenderWindow* OSXGLSupport::newWindow( const String &name, unsigned int width, unsigned int height, |
---|
| 231 | bool fullScreen, const NameValuePairList *miscParams ) |
---|
| 232 | { |
---|
| 233 | // Does the user want Cocoa or Carbon, default to carbon... |
---|
| 234 | mAPI = "carbon"; |
---|
| 235 | mContextType = "AGL"; |
---|
| 236 | |
---|
| 237 | if(miscParams) |
---|
| 238 | { |
---|
| 239 | NameValuePairList::const_iterator opt = NULL; |
---|
| 240 | |
---|
| 241 | // First we must determine if this is a carbon or a cocoa window |
---|
| 242 | // that we wish to create |
---|
| 243 | opt = miscParams->find("macAPI"); |
---|
| 244 | if(opt != miscParams->end() && opt->second == "cocoa") |
---|
| 245 | { |
---|
| 246 | // Our user wants a cocoa compatable system |
---|
| 247 | mAPI = "cocoa"; |
---|
| 248 | mContextType = "NSOpenGL"; |
---|
| 249 | } |
---|
| 250 | } |
---|
| 251 | |
---|
| 252 | // Create the window, if cocoa return a cocoa window |
---|
| 253 | if(mAPI == "cocoa") |
---|
| 254 | { |
---|
| 255 | LogManager::getSingleton().logMessage("Creating a Cocoa Compatible Render System"); |
---|
| 256 | OSXCocoaWindow* window = new OSXCocoaWindow(); |
---|
| 257 | window->create(name, width, height, fullScreen, miscParams); |
---|
| 258 | return window; |
---|
| 259 | } |
---|
| 260 | |
---|
| 261 | // Otherwise default to carbon |
---|
| 262 | LogManager::getSingleton().logMessage("Creating a Carbon Compatible Render System"); |
---|
| 263 | OSXCarbonWindow* window = new OSXCarbonWindow(); |
---|
| 264 | window->create(name, width, height, fullScreen, miscParams); |
---|
| 265 | return window; |
---|
| 266 | } |
---|
| 267 | |
---|
| 268 | void OSXGLSupport::start() |
---|
| 269 | { |
---|
| 270 | LogManager::getSingleton().logMessage( |
---|
| 271 | "*******************************************\n" |
---|
| 272 | "*** Starting OSX OpenGL Subsystem ***\n" |
---|
| 273 | "*******************************************"); |
---|
| 274 | } |
---|
| 275 | |
---|
| 276 | void OSXGLSupport::stop() |
---|
| 277 | { |
---|
| 278 | LogManager::getSingleton().logMessage( |
---|
| 279 | "*******************************************\n" |
---|
| 280 | "*** Stopping OSX OpenGL Subsystem ***\n" |
---|
| 281 | "*******************************************"); |
---|
| 282 | } |
---|
| 283 | |
---|
| 284 | void* OSXGLSupport::getProcAddress( const char* name ) |
---|
| 285 | { |
---|
| 286 | NSSymbol symbol; |
---|
| 287 | char *symbolName; |
---|
| 288 | // Prepend a '_' for the Unix C symbol mangling convention |
---|
| 289 | symbolName = (char*)malloc (strlen (name) + 2); |
---|
| 290 | strcpy(symbolName + 1, name); |
---|
| 291 | symbolName[0] = '_'; |
---|
| 292 | symbol = NULL; |
---|
| 293 | // TODO: dlopen calls, dyld |
---|
| 294 | if (NSIsSymbolNameDefined (symbolName)) |
---|
| 295 | symbol = NSLookupAndBindSymbol (symbolName); |
---|
| 296 | free (symbolName); |
---|
| 297 | return symbol ? NSAddressOfSymbol (symbol) : NULL; |
---|
| 298 | } |
---|
| 299 | |
---|
| 300 | void* OSXGLSupport::getProcAddress( const String& procname ) |
---|
| 301 | { |
---|
| 302 | return getProcAddress( procname.c_str() ); |
---|
| 303 | } |
---|
| 304 | |
---|
| 305 | bool OSXGLSupport::supportsPBuffers() |
---|
| 306 | { |
---|
| 307 | return true; |
---|
| 308 | } |
---|
| 309 | |
---|
| 310 | GLPBuffer* OSXGLSupport::createPBuffer(PixelComponentType format, size_t width, size_t height) |
---|
| 311 | { |
---|
| 312 | // if(mContextType == "NSOpenGL") |
---|
| 313 | // return new OSXCocoaPBuffer(format, width, height); |
---|
| 314 | // if(mContextType == "CGL") |
---|
| 315 | // return new OSXCGLPBuffer(format, width, height); |
---|
| 316 | // else |
---|
| 317 | return new OSXPBuffer(format, width, height); |
---|
| 318 | } |
---|
| 319 | |
---|
| 320 | CFComparisonResult OSXGLSupport::_compareModes (const void *val1, const void *val2, void *context) |
---|
| 321 | { |
---|
| 322 | // These are the values we will be interested in... |
---|
| 323 | /* |
---|
| 324 | _getDictionaryLong((mode), kCGDisplayWidth) |
---|
| 325 | _getDictionaryLong((mode), kCGDisplayHeight) |
---|
| 326 | _getDictionaryLong((mode), kCGDisplayRefreshRate) |
---|
| 327 | _getDictionaryLong((mode), kCGDisplayBitsPerPixel) |
---|
| 328 | _getDictionaryBoolean((mode), kCGDisplayModeIsSafeForHardware) |
---|
| 329 | _getDictionaryBoolean((mode), kCGDisplayModeIsStretched) |
---|
| 330 | */ |
---|
| 331 | |
---|
| 332 | // CFArray comparison callback for sorting display modes. |
---|
| 333 | #pragma unused(context) |
---|
| 334 | CFDictionaryRef thisMode = (CFDictionaryRef)val1; |
---|
| 335 | CFDictionaryRef otherMode = (CFDictionaryRef)val2; |
---|
| 336 | |
---|
| 337 | long width = _getDictionaryLong(thisMode, kCGDisplayWidth); |
---|
| 338 | long otherWidth = _getDictionaryLong(otherMode, kCGDisplayWidth); |
---|
| 339 | |
---|
| 340 | long height = _getDictionaryLong(thisMode, kCGDisplayHeight); |
---|
| 341 | long otherHeight = _getDictionaryLong(otherMode, kCGDisplayHeight); |
---|
| 342 | |
---|
| 343 | // sort modes in screen size order |
---|
| 344 | if (width * height < otherWidth * otherHeight) |
---|
| 345 | { |
---|
| 346 | return kCFCompareLessThan; |
---|
| 347 | } |
---|
| 348 | else if (width * height > otherWidth * otherHeight) |
---|
| 349 | { |
---|
| 350 | return kCFCompareGreaterThan; |
---|
| 351 | } |
---|
| 352 | |
---|
| 353 | // sort modes by bits per pixel |
---|
| 354 | long bitsPerPixel = _getDictionaryLong(thisMode, kCGDisplayBitsPerPixel); |
---|
| 355 | long otherBitsPerPixel = _getDictionaryLong(otherMode, kCGDisplayBitsPerPixel); |
---|
| 356 | |
---|
| 357 | if (bitsPerPixel < otherBitsPerPixel) |
---|
| 358 | { |
---|
| 359 | return kCFCompareLessThan; |
---|
| 360 | } |
---|
| 361 | else if (bitsPerPixel > otherBitsPerPixel) |
---|
| 362 | { |
---|
| 363 | return kCFCompareGreaterThan; |
---|
| 364 | } |
---|
| 365 | |
---|
| 366 | // sort modes by refresh rate. |
---|
| 367 | long refreshRate = _getDictionaryLong(thisMode, kCGDisplayRefreshRate); |
---|
| 368 | long otherRefreshRate = _getDictionaryLong(otherMode, kCGDisplayRefreshRate); |
---|
| 369 | |
---|
| 370 | if (refreshRate < otherRefreshRate) |
---|
| 371 | { |
---|
| 372 | return kCFCompareLessThan; |
---|
| 373 | } |
---|
| 374 | else if (refreshRate > otherRefreshRate) |
---|
| 375 | { |
---|
| 376 | return kCFCompareGreaterThan; |
---|
| 377 | } |
---|
| 378 | |
---|
| 379 | return kCFCompareEqualTo; |
---|
| 380 | } |
---|
| 381 | |
---|
| 382 | Boolean OSXGLSupport::_getDictionaryBoolean(CFDictionaryRef dict, const void* key) |
---|
| 383 | { |
---|
| 384 | Boolean value = false; |
---|
| 385 | CFBooleanRef boolRef; |
---|
| 386 | boolRef = (CFBooleanRef)CFDictionaryGetValue(dict, key); |
---|
| 387 | |
---|
| 388 | if (boolRef != NULL) |
---|
| 389 | value = CFBooleanGetValue(boolRef); |
---|
| 390 | |
---|
| 391 | return value; |
---|
| 392 | } |
---|
| 393 | |
---|
| 394 | long OSXGLSupport::_getDictionaryLong(CFDictionaryRef dict, const void* key) |
---|
| 395 | { |
---|
| 396 | long value = 0; |
---|
| 397 | CFNumberRef numRef; |
---|
| 398 | numRef = (CFNumberRef)CFDictionaryGetValue(dict, key); |
---|
| 399 | |
---|
| 400 | if (numRef != NULL) |
---|
| 401 | CFNumberGetValue(numRef, kCFNumberLongType, &value); |
---|
| 402 | |
---|
| 403 | return value; |
---|
| 404 | } |
---|
| 405 | |
---|
| 406 | |
---|
| 407 | } |
---|