Graphics.cpp 27 KB

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