Graphics.cpp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101
  1. /**
  2. * Copyright (c) 2006-2011 LOVE Development Team
  3. *
  4. * This software is provided 'as-is', without any express or implied
  5. * warranty. In no event will the authors be held liable for any damages
  6. * arising from the use of this software.
  7. *
  8. * Permission is granted to anyone to use this software for any purpose,
  9. * including commercial applications, and to alter it and redistribute it
  10. * freely, subject to the following restrictions:
  11. *
  12. * 1. The origin of this software must not be misrepresented; you must not
  13. * claim that you wrote the original software. If you use this software
  14. * in a product, an acknowledgment in the product documentation would be
  15. * appreciated but is not required.
  16. * 2. Altered source versions must be plainly marked as such, and must not be
  17. * misrepresented as being the original software.
  18. * 3. This notice may not be removed or altered from any source distribution.
  19. **/
  20. #include <common/config.h>
  21. #include <common/math.h>
  22. #include <common/Vector.h>
  23. #include "Graphics.h"
  24. #include <vector>
  25. #include <sstream>
  26. #include <algorithm>
  27. #include <iterator>
  28. namespace love
  29. {
  30. namespace graphics
  31. {
  32. namespace opengl
  33. {
  34. Graphics::Graphics()
  35. : currentFont(0), lineWidth(1), matrixLimit(0), userMatrices(0)
  36. {
  37. // Indicates that there is no screen
  38. // created yet.
  39. currentMode.width = 0;
  40. currentMode.height = 0;
  41. currentMode.fullscreen = 0;
  42. // Window should be centered.
  43. SDL_putenv(const_cast<char *>("SDL_VIDEO_CENTERED=center"));
  44. if(SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
  45. throw Exception(SDL_GetError());
  46. }
  47. Graphics::~Graphics()
  48. {
  49. if(currentFont != 0)
  50. currentFont->release();
  51. SDL_QuitSubSystem(SDL_INIT_VIDEO);
  52. }
  53. const char * Graphics::getName() const
  54. {
  55. return "love.graphics.opengl";
  56. }
  57. bool Graphics::checkMode(int width, int height, bool fullscreen)
  58. {
  59. Uint32 sdlflags = fullscreen ? (SDL_OPENGL | SDL_FULLSCREEN) : SDL_OPENGL;
  60. // Check if mode is supported
  61. int bpp = SDL_VideoModeOK(width, height, 32, sdlflags);
  62. return (bpp >= 16);
  63. }
  64. DisplayState Graphics::saveState()
  65. {
  66. DisplayState s;
  67. //create a table in which to store the color data in float format, before converting it
  68. float color[4];
  69. //get the color
  70. glGetFloatv(GL_CURRENT_COLOR, color);
  71. s.color.set( (color[0]*255.0f), (color[1]*255.0f), (color[2]*255.0f), (color[3]*255.0f) );
  72. //get the background color
  73. glGetFloatv(GL_COLOR_CLEAR_VALUE, color);
  74. s.backgroundColor.set( color[0]*255.0f, color[1]*255.0f, color[2]*255.0f, color[3]*255.0f );
  75. //store modes here
  76. GLint mode;
  77. //get blend mode
  78. glGetIntegerv(GL_BLEND_DST, &mode);
  79. //following syntax seems better than if-else every time
  80. s.blendMode = (mode == GL_ONE) ? Graphics::BLEND_ADDITIVE : Graphics::BLEND_ALPHA;
  81. //get color mode
  82. glGetTexEnviv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, &mode);
  83. s.colorMode = (mode == GL_MODULATE) ? Graphics::COLOR_MODULATE : Graphics::COLOR_REPLACE;
  84. //get line style
  85. //s.lineStyle = (glIsEnabled(GL_POLYGON_SMOOTH) == GL_TRUE) ? Graphics::LINE_SMOOTH : Graphics::LINE_ROUGH;
  86. s.lineStyle = Graphics::LINE_ROUGH;
  87. //get the point size
  88. glGetFloatv(GL_POINT_SIZE, &s.pointSize);
  89. //get point style
  90. s.pointStyle = (glIsEnabled(GL_POINT_SMOOTH) == GL_TRUE) ? Graphics::POINT_SMOOTH : Graphics::POINT_ROUGH;
  91. //get scissor status
  92. s.scissor = (glIsEnabled(GL_SCISSOR_TEST) == GL_TRUE);
  93. //do we have scissor, if so, store the box
  94. if (s.scissor)
  95. glGetIntegerv(GL_SCISSOR_BOX, s.scissorBox);
  96. char *cap = 0;
  97. SDL_WM_GetCaption(&cap, 0);
  98. s.caption = cap;
  99. s.mouseVisible = (SDL_ShowCursor(SDL_QUERY) == SDL_ENABLE) ? true : false;
  100. return s;
  101. }
  102. void Graphics::restoreState(const DisplayState & s)
  103. {
  104. setColor(s.color);
  105. setBackgroundColor(s.backgroundColor);
  106. setBlendMode(s.blendMode);
  107. setColorMode(s.colorMode);
  108. setLine(lineWidth, s.lineStyle);
  109. setPoint(s.pointSize, s.pointStyle);
  110. if (s.scissor)
  111. setScissor(s.scissorBox[0], s.scissorBox[1], s.scissorBox[2], s.scissorBox[3]);
  112. else
  113. setScissor();
  114. setCaption(s.caption.c_str());
  115. SDL_ShowCursor(s.mouseVisible ? SDL_ENABLE : SDL_DISABLE);
  116. }
  117. bool Graphics::setMode(int width, int height, bool fullscreen, bool vsync, int fsaa)
  118. {
  119. // This operation destroys the OpenGL context, so
  120. // we must save the state.
  121. DisplayState tempState;
  122. if (isCreated())
  123. tempState = saveState();
  124. // Unlad all volatile objects. These must be reloaded after
  125. // the display mode change.
  126. Volatile::unloadAll();
  127. // Get caption.
  128. // We need to restart the subsystem for two reasons:
  129. // 1) Special case for fullscreen -> windowed. Windows XP did not
  130. // work well with "normal" display mode change in this case.
  131. // The application window does leave fullscreen, but the desktop
  132. // resolution does not revert to the correct one. Restarting the
  133. // SDL video subsystem does the trick, though.
  134. // 2) Restart the event system (for whatever reason the event system
  135. // started and stopped with SDL_INIT_VIDEO, see:
  136. // http://sdl.beuc.net/sdl.wiki/Introduction_to_Events)
  137. // because the mouse position will not be able to exceed
  138. // the previous' video mode window size (i.e. alway
  139. // love.mouse.getX() < 800 when switching from 800x600 to a
  140. // higher resolution)
  141. SDL_QuitSubSystem(SDL_INIT_VIDEO);
  142. if(SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
  143. {
  144. std::cout << "Could not init SDL_VIDEO: " << SDL_GetError() << std::endl;
  145. return false;
  146. }
  147. // Set caption.
  148. // Set GL attributes
  149. SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 0);
  150. SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 0);
  151. SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 0);
  152. SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
  153. SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, (vsync ? 1 : 0));
  154. SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 1);
  155. // FSAA
  156. if(fsaa > 0)
  157. {
  158. SDL_GL_SetAttribute( SDL_GL_MULTISAMPLEBUFFERS, 1 ) ;
  159. SDL_GL_SetAttribute( SDL_GL_MULTISAMPLESAMPLES, fsaa ) ;
  160. glEnable(GL_MULTISAMPLE);
  161. }
  162. // Fullscreen?
  163. Uint32 sdlflags = fullscreen ? (SDL_OPENGL | SDL_FULLSCREEN) : SDL_OPENGL;
  164. if(!isCreated())
  165. setCaption("");
  166. // Have SDL set the video mode.
  167. if(SDL_SetVideoMode(width, height, 32, sdlflags ) == 0)
  168. {
  169. bool failed = true;
  170. if(fsaa > 0)
  171. {
  172. // FSAA might have failed, disable it and try again
  173. SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0);
  174. failed = SDL_SetVideoMode(width, height, 32, sdlflags ) == 0;
  175. if (failed)
  176. {
  177. // There might be no FSAA at all
  178. SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0);
  179. failed = SDL_SetVideoMode(width, height, 32, sdlflags ) == 0;
  180. }
  181. }
  182. if(failed)
  183. {
  184. std::cerr << "Could not set video mode: " << SDL_GetError() << std::endl;
  185. return false;
  186. }
  187. }
  188. if (width == 0 || height == 0)
  189. {
  190. const SDL_VideoInfo* videoinfo = SDL_GetVideoInfo();
  191. width = videoinfo->current_w;
  192. height = videoinfo->current_h;
  193. }
  194. GLint buffers;
  195. GLint samples;
  196. glGetIntegerv( GL_SAMPLE_BUFFERS_ARB, & buffers ) ;
  197. glGetIntegerv( GL_SAMPLES_ARB, & samples ) ;
  198. // Don't fail because of this, but issue a warning.
  199. if ( (! buffers && fsaa) || (samples != fsaa))
  200. {
  201. std::cerr << "Warning, quality setting failed! (Result: buffers: " << buffers << ", samples: " << samples << ")" << std::endl;
  202. fsaa = !buffers ? 0 : samples;
  203. }
  204. // Okay, setup OpenGL.
  205. // Enable blending
  206. glEnable(GL_BLEND);
  207. // "Normal" blending
  208. glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  209. // Enable line/point smoothing.
  210. setLineStyle(LINE_SMOOTH);
  211. glEnable(GL_POINT_SMOOTH);
  212. glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
  213. // Enable textures
  214. glEnable(GL_TEXTURE_2D);
  215. // Set the viewport to top-left corner
  216. glViewport(0,0, width, height);
  217. // Reset the projection matrix
  218. glMatrixMode(GL_PROJECTION);
  219. glLoadIdentity();
  220. // Set up orthographic view (no depth)
  221. glOrtho(0.0, width, height,0.0, -1.0, 1.0);
  222. // Reset modelview matrix
  223. glMatrixMode(GL_MODELVIEW);
  224. glLoadIdentity();
  225. // Set pixel row alignment
  226. glPixelStorei(GL_UNPACK_ALIGNMENT, 2);
  227. // Get the actual vsync status
  228. int real_vsync;
  229. SDL_GL_GetAttribute(SDL_GL_SWAP_CONTROL, &real_vsync);
  230. // Set the new display mode as the current display mode.
  231. currentMode.width = width;
  232. currentMode.height = height;
  233. currentMode.colorDepth = 32;
  234. currentMode.fsaa = fsaa;
  235. currentMode.fullscreen = fullscreen;
  236. currentMode.vsync = real_vsync;
  237. // Reload all volatile objects.
  238. if(!Volatile::loadAll())
  239. std::cerr << "Could not reload all volatile objects." << std::endl;
  240. // Restore the display state.
  241. restoreState(tempState);
  242. // Get the maximum number of matrices
  243. // subtract a few to give the engine some room.
  244. glGetIntegerv(GL_MAX_MODELVIEW_STACK_DEPTH, &matrixLimit);
  245. matrixLimit -= 5;
  246. return true;
  247. }
  248. void Graphics::getMode(int *width, int *height, bool *fullscreen, bool *vsync, int *fsaa)
  249. {
  250. *width = currentMode.width;
  251. *height = currentMode.height;
  252. *fullscreen = currentMode.fullscreen;
  253. *vsync = currentMode.vsync;
  254. *fsaa = currentMode.fsaa;
  255. }
  256. bool Graphics::toggleFullscreen()
  257. {
  258. // Try to do the change.
  259. return setMode(currentMode.width,
  260. currentMode.height,
  261. !currentMode.fullscreen,
  262. currentMode.vsync,
  263. currentMode.fsaa);
  264. }
  265. void Graphics::reset()
  266. {
  267. DisplayState s;
  268. discardMask();
  269. Framebuffer::bindDefaultBuffer();
  270. restoreState(s);
  271. }
  272. void Graphics::clear()
  273. {
  274. glClear(GL_COLOR_BUFFER_BIT);
  275. glLoadIdentity();
  276. PixelEffect::detach();
  277. }
  278. void Graphics::present()
  279. {
  280. SDL_GL_SwapBuffers();
  281. }
  282. void Graphics::setIcon(Image * image)
  283. {
  284. Uint32 rmask, gmask, bmask, amask;
  285. #ifdef LOVE_BIG_ENDIAN
  286. rmask = 0xFF000000;
  287. gmask = 0x00FF0000;
  288. bmask = 0x0000FF00;
  289. amask = 0x000000FF;
  290. #else
  291. rmask = 0x000000FF;
  292. gmask = 0x0000FF00;
  293. bmask = 0x00FF0000;
  294. amask = 0xFF000000;
  295. #endif
  296. int w = static_cast<int>(image->getWidth());
  297. int h = static_cast<int>(image->getHeight());
  298. int pitch = static_cast<int>(image->getWidth() * 4);
  299. SDL_Surface * icon = SDL_CreateRGBSurfaceFrom(image->getData()->getData(), w, h, 32, pitch, rmask, gmask, bmask, amask);
  300. SDL_WM_SetIcon(icon, NULL);
  301. SDL_FreeSurface(icon);
  302. }
  303. void Graphics::setCaption(const char * caption)
  304. {
  305. SDL_WM_SetCaption(caption, 0);
  306. }
  307. int Graphics::getCaption(lua_State * L)
  308. {
  309. char * title = 0;
  310. SDL_WM_GetCaption(&title, 0);
  311. lua_pushstring(L, title);
  312. return 1;
  313. }
  314. int Graphics::getWidth()
  315. {
  316. return currentMode.width;
  317. }
  318. int Graphics::getHeight()
  319. {
  320. return currentMode.height;
  321. }
  322. int Graphics::getRenderHeight()
  323. {
  324. if (Framebuffer::current)
  325. return Framebuffer::current->getHeight();
  326. return currentMode.height;
  327. }
  328. bool Graphics::isCreated()
  329. {
  330. return (currentMode.width > 0) || (currentMode.height > 0);
  331. }
  332. int Graphics::getModes(lua_State * L)
  333. {
  334. SDL_Rect ** modes = SDL_ListModes(0, SDL_OPENGL | SDL_FULLSCREEN);
  335. if(modes == (SDL_Rect **)0 || modes == (SDL_Rect **)-1)
  336. return 0;
  337. int index = 1;
  338. lua_newtable(L);
  339. for(int i=0;modes[i];++i)
  340. {
  341. lua_pushinteger(L, index);
  342. lua_newtable(L);
  343. // Inner table attribs.
  344. lua_pushstring(L, "width");
  345. lua_pushinteger(L, modes[i]->w);
  346. lua_settable(L, -3);
  347. lua_pushstring(L, "height");
  348. lua_pushinteger(L, modes[i]->h);
  349. lua_settable(L, -3);
  350. // Inner table attribs end.
  351. lua_settable(L, -3);
  352. index++;
  353. }
  354. return 1;
  355. }
  356. void Graphics::setScissor(int x, int y, int width, int height)
  357. {
  358. glEnable(GL_SCISSOR_TEST);
  359. glScissor(x, getRenderHeight() - (y + height), width, height); // Compensates for the fact that our y-coordinate is reverse of OpenGLs.
  360. }
  361. void Graphics::setScissor()
  362. {
  363. glDisable(GL_SCISSOR_TEST);
  364. }
  365. int Graphics::getScissor(lua_State * L)
  366. {
  367. if(glIsEnabled(GL_SCISSOR_TEST) == GL_FALSE)
  368. return 0;
  369. GLint scissor[4];
  370. glGetIntegerv(GL_SCISSOR_BOX, scissor);
  371. lua_pushnumber(L, scissor[0]);
  372. lua_pushnumber(L, getRenderHeight() - (scissor[1] + scissor[3])); // Compensates for the fact that our y-coordinate is reverse of OpenGLs.
  373. lua_pushnumber(L, scissor[2]);
  374. lua_pushnumber(L, scissor[3]);
  375. return 4;
  376. }
  377. void Graphics::defineMask()
  378. {
  379. glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
  380. glEnable(GL_STENCIL_TEST);
  381. glClear(GL_STENCIL_BUFFER_BIT);
  382. glStencilFunc(GL_ALWAYS, 1, 1);
  383. glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
  384. }
  385. void Graphics::useMask(bool invert)
  386. {
  387. glStencilFunc(GL_EQUAL, (int)(!invert), 1); // invert ? 0 : 1
  388. glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
  389. glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
  390. }
  391. void Graphics::discardMask()
  392. {
  393. glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
  394. glDisable(GL_STENCIL_TEST);
  395. }
  396. Image * Graphics::newImage(love::image::ImageData * data)
  397. {
  398. // Create the image.
  399. Image * image = new Image(data);
  400. bool success;
  401. try {
  402. success = image->load();
  403. } catch (love::Exception & e) {
  404. image->release();
  405. throw love::Exception(e.what());
  406. }
  407. if (!success) {
  408. image->release();
  409. return 0;
  410. }
  411. return image;
  412. }
  413. Quad * Graphics::newQuad(int x, int y, int w, int h, int sw, int sh)
  414. {
  415. Quad::Viewport v;
  416. v.x = x;
  417. v.y = y;
  418. v.w = w;
  419. v.h = h;
  420. return new Quad(v, sw, sh);
  421. }
  422. Font * Graphics::newFont(love::font::Rasterizer * r, const Image::Filter& filter)
  423. {
  424. Font * font = new Font(r, filter);
  425. // Load it and check for errors.
  426. if(!font)
  427. {
  428. delete font;
  429. return 0;
  430. }
  431. return font;
  432. }
  433. SpriteBatch * Graphics::newSpriteBatch(Image * image, int size, int usage)
  434. {
  435. SpriteBatch * t = NULL;
  436. try {
  437. t = new SpriteBatch(image, size, usage);
  438. } catch (love::Exception& e) {
  439. if (t) delete t;
  440. throw e;
  441. }
  442. return t;
  443. }
  444. ParticleSystem * Graphics::newParticleSystem(Image * image, int size)
  445. {
  446. return new ParticleSystem(image, size);
  447. }
  448. Framebuffer * Graphics::newFramebuffer(int width, int height)
  449. {
  450. return new Framebuffer(width, height);
  451. }
  452. PixelEffect * Graphics::newPixelEffect(const std::string& code)
  453. {
  454. PixelEffect * effect = NULL;
  455. try {
  456. effect = new PixelEffect(code);
  457. } catch (love::Exception& e) {
  458. if (effect)
  459. delete effect;
  460. throw(e);
  461. }
  462. return effect;
  463. }
  464. void Graphics::setColor(const Color& c)
  465. {
  466. glColor4ubv(&c.r);
  467. }
  468. Color Graphics::getColor()
  469. {
  470. float c[4];
  471. glGetFloatv(GL_CURRENT_COLOR, c);
  472. Color t;
  473. t.r = (unsigned char)(255.0f*c[0]);
  474. t.g = (unsigned char)(255.0f*c[1]);
  475. t.b = (unsigned char)(255.0f*c[2]);
  476. t.a = (unsigned char)(255.0f*c[3]);
  477. return t;
  478. }
  479. void Graphics::setBackgroundColor(const Color& c)
  480. {
  481. glClearColor((float)c.r/255.0f, (float)c.g/255.0f, (float)c.b/255.0f, (float)c.a/255.0f);
  482. }
  483. Color Graphics::getBackgroundColor()
  484. {
  485. float c[4];
  486. glGetFloatv(GL_COLOR_CLEAR_VALUE, c);
  487. Color t;
  488. t.r = (unsigned char)(255.0f*c[0]);
  489. t.g = (unsigned char)(255.0f*c[1]);
  490. t.b = (unsigned char)(255.0f*c[2]);
  491. t.a = (unsigned char)(255.0f*c[3]);
  492. return t;
  493. }
  494. void Graphics::setFont( Font * font )
  495. {
  496. if(currentFont != 0)
  497. currentFont->release();
  498. currentFont = font;
  499. if(font != 0)
  500. currentFont->retain();
  501. }
  502. Font * Graphics::getFont()
  503. {
  504. return currentFont;
  505. }
  506. void Graphics::setBlendMode( Graphics::BlendMode mode )
  507. {
  508. glAlphaFunc(GL_GEQUAL, 0);
  509. if (mode == BLEND_SUBTRACTIVE)
  510. glBlendEquation(GL_FUNC_REVERSE_SUBTRACT);
  511. else
  512. glBlendEquation(GL_FUNC_ADD);
  513. if (mode == BLEND_ALPHA)
  514. glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  515. else if (mode == BLEND_MULTIPLICATIVE)
  516. glBlendFunc(GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA);
  517. else // mode == BLEND_ADDITIVE || mode == BLEND_SUBTRACTIVE
  518. glBlendFunc(GL_SRC_ALPHA, GL_ONE);
  519. }
  520. void Graphics::setColorMode ( Graphics::ColorMode mode )
  521. {
  522. if(mode == COLOR_MODULATE)
  523. glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
  524. else // mode = COLOR_REPLACE
  525. glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
  526. }
  527. Graphics::BlendMode Graphics::getBlendMode ()
  528. {
  529. GLint dst, src, equation;
  530. glGetIntegerv(GL_BLEND_DST, &dst);
  531. glGetIntegerv(GL_BLEND_SRC, &src);
  532. glGetIntegerv(GL_BLEND_EQUATION, &equation);
  533. if (equation == GL_FUNC_REVERSE_SUBTRACT) // && src == GL_SRC_ALPHA && dst == GL_ONE
  534. return BLEND_SUBTRACTIVE;
  535. else if(src == GL_SRC_ALPHA && dst == GL_ONE) // && equation == GL_FUNC_ADD
  536. return BLEND_ADDITIVE;
  537. else if (src == GL_SRC_ALPHA && dst == GL_ONE_MINUS_SRC_ALPHA) // && equation == GL_FUNC_ADD
  538. return BLEND_ALPHA;
  539. else // src == GL_DST_COLOR && dst == GL_ONE_MINUS_SRC_ALPHA && equation == GL_FUNC_ADD
  540. return BLEND_MULTIPLICATIVE;
  541. }
  542. Graphics::ColorMode Graphics::getColorMode()
  543. {
  544. GLint mode;
  545. glGetTexEnviv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, &mode);
  546. if(mode == GL_MODULATE)
  547. return COLOR_MODULATE;
  548. else // // mode == GL_REPLACE
  549. return COLOR_REPLACE;
  550. }
  551. void Graphics::setLineWidth( float width )
  552. {
  553. lineWidth = width;
  554. }
  555. void Graphics::setLineStyle(Graphics::LineStyle style )
  556. {
  557. //// XXX: actually enables antialiasing for _all_ polygons.
  558. //// may need investigation if wanted or not
  559. //// maybe rename to something else?
  560. //if(style == LINE_ROUGH)
  561. // glDisable (GL_POLYGON_SMOOTH);
  562. //else // type == LINE_SMOOTH
  563. //{
  564. // glEnable (GL_POLYGON_SMOOTH);
  565. // glHint (GL_POLYGON_SMOOTH_HINT, GL_NICEST);
  566. //}
  567. }
  568. void Graphics::setLine( float width, Graphics::LineStyle style )
  569. {
  570. setLineWidth(width);
  571. if(style == 0)
  572. return;
  573. setLineStyle(style);
  574. }
  575. float Graphics::getLineWidth()
  576. {
  577. float w;
  578. glGetFloatv(GL_LINE_WIDTH, &w);
  579. return w;
  580. }
  581. Graphics::LineStyle Graphics::getLineStyle()
  582. {
  583. //if(glIsEnabled(GL_POLYGON_SMOOTH) == GL_TRUE)
  584. // return LINE_SMOOTH;
  585. //else
  586. return LINE_ROUGH;
  587. }
  588. void Graphics::setPointSize( float size )
  589. {
  590. glPointSize((GLfloat)size);
  591. }
  592. void Graphics::setPointStyle( Graphics::PointStyle style )
  593. {
  594. if( style == POINT_SMOOTH )
  595. glEnable(GL_POINT_SMOOTH);
  596. else // love::POINT_ROUGH
  597. glDisable(GL_POINT_SMOOTH);
  598. }
  599. void Graphics::setPoint( float size, Graphics::PointStyle style )
  600. {
  601. if( style == POINT_SMOOTH )
  602. glEnable(GL_POINT_SMOOTH);
  603. else // POINT_ROUGH
  604. glDisable(GL_POINT_SMOOTH);
  605. glPointSize((GLfloat)size);
  606. }
  607. float Graphics::getPointSize()
  608. {
  609. GLfloat size;
  610. glGetFloatv(GL_POINT_SIZE, &size);
  611. return (float)size;
  612. }
  613. Graphics::PointStyle Graphics::getPointStyle()
  614. {
  615. if(glIsEnabled(GL_POINT_SMOOTH) == GL_TRUE)
  616. return POINT_SMOOTH;
  617. else
  618. return POINT_ROUGH;
  619. }
  620. int Graphics::getMaxPointSize()
  621. {
  622. GLint max;
  623. glGetIntegerv(GL_POINT_SIZE_MAX, &max);
  624. return (int)max;
  625. }
  626. void Graphics::print( const char * str, float x, float y , float angle, float sx, float sy, float ox, float oy, float kx, float ky)
  627. {
  628. if(currentFont != 0)
  629. {
  630. std::string text(str);
  631. currentFont->print(text, x, y, angle, sx, sy, ox, oy, kx, ky);
  632. }
  633. }
  634. void Graphics::printf( const char * str, float x, float y, float wrap, AlignMode align)
  635. {
  636. if (currentFont == 0)
  637. return;
  638. using namespace std;
  639. string text(str);
  640. const float width_space = static_cast<float>(currentFont->getWidth(' '));
  641. vector<string> lines_to_draw;
  642. //split text at newlines
  643. istringstream iss( text );
  644. string line;
  645. while (getline(iss, line, '\n')) {
  646. // split line into words
  647. vector<string> words;
  648. istringstream word_iss(line);
  649. copy(istream_iterator<string>(word_iss), istream_iterator<string>(),
  650. back_inserter< vector<string> >(words));
  651. // put words back together until a wrap occurs
  652. float width = 0.0f;
  653. float oldwidth = 0.0f;
  654. ostringstream string_builder;
  655. vector<string>::const_iterator word_iter;
  656. for (word_iter = words.begin(); word_iter != words.end(); ++word_iter) {
  657. string word( *word_iter );
  658. width += currentFont->getWidth( word );
  659. // on wordwrap, push line to line buffer and clear string builder
  660. if (width >= wrap && oldwidth > 0) {
  661. lines_to_draw.push_back( string_builder.str() );
  662. string_builder.str( "" );
  663. width = static_cast<float>(currentFont->getWidth( word ));
  664. }
  665. string_builder << word << " ";
  666. width += width_space;
  667. oldwidth = width;
  668. }
  669. // push last line
  670. lines_to_draw.push_back( string_builder.str() );
  671. }
  672. // now for the actual printing
  673. vector<string>::const_iterator line_iter, line_end = lines_to_draw.end();
  674. for (line_iter = lines_to_draw.begin(); line_iter != line_end; ++line_iter) {
  675. float width = static_cast<float>(currentFont->getWidth( *line_iter ));
  676. switch (align) {
  677. case ALIGN_RIGHT:
  678. currentFont->print(*line_iter, ceil(x + wrap - width), ceil(y));
  679. break;
  680. case ALIGN_CENTER:
  681. currentFont->print(*line_iter, ceil(x + (wrap - width) / 2), ceil(y));
  682. break;
  683. case ALIGN_LEFT:
  684. default:
  685. currentFont->print(*line_iter, ceil(x), ceil(y));
  686. break;
  687. }
  688. y += currentFont->getHeight() * currentFont->getLineHeight();
  689. }
  690. }
  691. /**
  692. * Primitives
  693. **/
  694. void Graphics::point( float x, float y )
  695. {
  696. glDisable(GL_TEXTURE_2D);
  697. glBegin(GL_POINTS);
  698. glVertex2f(x, y);
  699. glEnd();
  700. glEnable(GL_TEXTURE_2D);
  701. }
  702. // calculate line boundary intersection vertices for current line
  703. // dependent on the current *and next* line segment
  704. static void pushIntersectionPoints(Vector *vertices, int pos, float halfwidth, const Vector& p, const Vector& q, const Vector& r)
  705. {
  706. // calculate line directions
  707. Vector s = (q - p);
  708. Vector t = (r - q);
  709. // calculate vertex displacement vectors
  710. Vector d1 = s.getNormal();
  711. Vector d2 = t.getNormal();
  712. d1.normalize();
  713. d2.normalize();
  714. float det_norm = d1 ^ d2;
  715. d1 *= halfwidth;
  716. d2 *= halfwidth;
  717. // lines parallel -> assume intersection at displacement points
  718. if (fabs(det_norm) <= .03) {
  719. vertices[pos] = q - d2;
  720. vertices[pos + 1] = q + d2;
  721. return;
  722. }
  723. // real intersection -> calculate boundary intersection points
  724. float det = s ^ t;
  725. Vector d = d1 - d2;
  726. Vector b = s - d; // s = q - p
  727. Vector c = s + d;
  728. float lambda = (b ^ t) / det;
  729. float mu = (c ^ t) / det;
  730. // ordering for GL_TRIANGLE_STRIP
  731. vertices[pos] = p - d1 + s * mu;
  732. vertices[pos+1] = p + d1 + s * lambda;
  733. }
  734. void Graphics::polyline(const float* coords, size_t count, bool looping)
  735. {
  736. Vector *vertices = new Vector[count]; // two vertices for every line end-point
  737. Vector p,q,r;
  738. r = Vector(coords[0], coords[1]);
  739. if (looping) q = Vector(coords[count-4], coords[count-3]);
  740. else q = r * 2 - Vector(coords[2], coords[3]);
  741. for (size_t i = 0; i+3 < count; i += 2) {
  742. p = q;
  743. q = r;
  744. r = Vector(coords[i+2], coords[i+3]);
  745. pushIntersectionPoints(vertices, i, lineWidth/2, p,q,r);
  746. }
  747. p = q;
  748. q = r;
  749. if (looping) r = Vector(coords[2], coords[3]);
  750. else r += (q-p);
  751. pushIntersectionPoints(vertices, count-2, lineWidth/2, p,q,r);
  752. glDisable(GL_TEXTURE_2D);
  753. glEnableClientState(GL_VERTEX_ARRAY);
  754. glVertexPointer(2, GL_FLOAT, 0, (const GLvoid*)vertices);
  755. glDrawArrays(GL_TRIANGLE_STRIP, 0, count);
  756. glDisableClientState(GL_VERTEX_ARRAY);
  757. glEnable(GL_TEXTURE_2D);
  758. delete[] vertices;
  759. }
  760. void Graphics::triangle(DrawMode mode, float x1, float y1, float x2, float y2, float x3, float y3 )
  761. {
  762. float coords[] = { x1,y1, x2,y2, x3,y3, x1,y1 };
  763. polygon(mode, coords, 4 * 2);
  764. }
  765. void Graphics::rectangle(DrawMode mode, float x, float y, float w, float h)
  766. {
  767. quad(mode, x,y, x,y+h, x+w,y+h, x+w,y);
  768. }
  769. void Graphics::quad(DrawMode mode, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4 )
  770. {
  771. float coords[] = { x1,y1, x2,y2, x3,y3, x4,y4, x1,y1 };
  772. polygon(mode, coords, 5 * 2);
  773. }
  774. void Graphics::circle(DrawMode mode, float x, float y, float radius, int points)
  775. {
  776. float two_pi = static_cast<float>(LOVE_M_PI * 2);
  777. if(points <= 0) points = 1;
  778. float angle_shift = (two_pi / points);
  779. float phi = .0f;
  780. float *coords = new float[2 * (points + 1)];
  781. for (int i = 0; i < points; ++i, phi += angle_shift) {
  782. coords[2*i] = x + radius * cos(phi);
  783. coords[2*i+1] = y + radius * sin(phi);
  784. }
  785. coords[2*points] = coords[0];
  786. coords[2*points+1] = coords[1];
  787. polygon(mode, coords, (points + 1) * 2);
  788. delete[] coords;
  789. }
  790. void Graphics::arc(DrawMode mode, float x, float y, float radius, float angle1, float angle2, int points)
  791. {
  792. angle1 = fmod(angle1, 2.0f * (float)LOVE_M_PI);
  793. angle2 = fmod(angle2, 2.0f * (float)LOVE_M_PI);
  794. if (angle1 == angle2)
  795. return;
  796. else if (angle1 > angle2)
  797. angle2 += (float)LOVE_M_PI * 2.0f;
  798. if(points <= 0) points = 1;
  799. float angle_shift = ((angle2 - angle1) / points);
  800. float phi = angle1;
  801. // GL_POLYGON can only fill-draw convex polygons, so we need to do stuff manually here
  802. if (mode == DRAW_LINE) {
  803. float *coords = new float[(points + 3) * 2];
  804. coords[0] = coords[2 * points + 4] = x;
  805. coords[1] = coords[2 * points + 5] = y;
  806. for (int i = 0; i <= points; ++i, phi += angle_shift) {
  807. coords[2 * (i+1)] = x + radius * cos(phi);
  808. coords[2 * (i+1) + 1] = y - radius * sin(phi);
  809. }
  810. polyline(coords, (points + 3) * 2); // artifacts at sharp angles if set to looping
  811. delete[] coords;
  812. } else {
  813. glDisable(GL_TEXTURE_2D);
  814. glBegin(GL_TRIANGLE_FAN);
  815. glVertex2f(x, y);
  816. for (int i = 0; i <= points; ++i, phi += angle_shift)
  817. glVertex2f(x + radius * cos(phi), y - radius * sin(phi));
  818. glEnd();
  819. glEnable(GL_TEXTURE_2D);
  820. }
  821. }
  822. /// @param mode the draw mode
  823. /// @param coords the coordinate array
  824. /// @param count the number of coordinates/size of the array
  825. void Graphics::polygon(DrawMode mode, const float* coords, size_t count)
  826. {
  827. // coords is an array of a closed loop of vertices, i.e.
  828. // coords[count-2] = coords[0], coords[count-1] = coords[1]
  829. if (mode == DRAW_LINE) {
  830. polyline(coords, count, true);
  831. } else {
  832. glDisable(GL_TEXTURE_2D);
  833. glEnableClientState(GL_VERTEX_ARRAY);
  834. glVertexPointer(2, GL_FLOAT, 0, (const GLvoid*)coords);
  835. glDrawArrays(GL_POLYGON, 0, count/2-1); // opengl will close the polygon for us
  836. glDisableClientState(GL_VERTEX_ARRAY);
  837. glEnable(GL_TEXTURE_2D);
  838. }
  839. }
  840. love::image::ImageData * Graphics::newScreenshot(love::image::Image * image)
  841. {
  842. int w = getWidth();
  843. int h = getHeight();
  844. int row = 4*w;
  845. int size = row*h;
  846. GLubyte * pixels = new GLubyte[size];
  847. GLubyte * screenshot = new GLubyte[size];
  848. glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
  849. // OpenGL sucks and reads pixels from the lower-left. Let's fix that.
  850. GLubyte *src = pixels - row, *dst = screenshot + size;
  851. for (int i = 0; i < h; ++i) {
  852. memcpy(dst-=row, src+=row, row);
  853. }
  854. love::image::ImageData * img = image->newImageData(w, h, (void*)screenshot);
  855. delete [] pixels;
  856. delete [] screenshot;
  857. return img;
  858. }
  859. void Graphics::push()
  860. {
  861. if (userMatrices == matrixLimit)
  862. throw Exception("Maximum stack depth reached.");
  863. glPushMatrix();
  864. ++userMatrices;
  865. }
  866. void Graphics::pop()
  867. {
  868. if (userMatrices < 1)
  869. throw Exception("Minimum stack depth reached. (More pops than pushes?)");
  870. glPopMatrix();
  871. --userMatrices;
  872. }
  873. void Graphics::rotate(float r)
  874. {
  875. glRotatef(LOVE_TODEG(r), 0, 0, 1);
  876. }
  877. void Graphics::scale(float x, float y)
  878. {
  879. glScalef(x, y, 1);
  880. }
  881. void Graphics::translate(float x, float y)
  882. {
  883. glTranslatef(x, y, 0);
  884. }
  885. void Graphics::shear(float kx, float ky)
  886. {
  887. Matrix t;
  888. t.setShear(kx, ky);
  889. glMultMatrixf((const GLfloat*)t.getElements());
  890. }
  891. void Graphics::drawTest(Image * image, float x, float y, float a, float sx, float sy, float ox, float oy)
  892. {
  893. image->bind();
  894. // Buffer for transforming the image.
  895. vertex buf[4];
  896. Matrix t;
  897. t.translate(x, y);
  898. t.rotate(a);
  899. t.scale(sx, sy);
  900. t.translate(ox, oy);
  901. t.transform(buf, image->getVertices(), 4);
  902. const vertex * vertices = image->getVertices();
  903. glEnableClientState(GL_VERTEX_ARRAY);
  904. glEnableClientState(GL_TEXTURE_COORD_ARRAY);
  905. glVertexPointer(2, GL_FLOAT, sizeof(vertex), (GLvoid*)&buf[0].x);
  906. glTexCoordPointer(2, GL_FLOAT, sizeof(vertex), (GLvoid*)&vertices[0].s);
  907. glDrawArrays(GL_QUADS, 0, 4);
  908. glDisableClientState(GL_TEXTURE_COORD_ARRAY);
  909. glDisableClientState(GL_VERTEX_ARRAY);
  910. }
  911. bool Graphics::hasFocus()
  912. {
  913. return (SDL_GetAppState() & SDL_APPINPUTFOCUS) != 0;
  914. }
  915. } // opengl
  916. } // graphics
  917. } // love