暂无描述
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

olcPixelGameEngine.cpp 39KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313
  1. #include "olcPixelGameEngine.h"
  2. #ifdef _WIN32
  3. static wglSwapInterval_t *wglSwapInterval;
  4. #else
  5. static glSwapInterval_t *glSwapIntervalEXT;
  6. #endif
  7. namespace olc
  8. {
  9. PixelGameEngine::PixelGameEngine()
  10. {
  11. sAppName = "Undefined";
  12. olc::PGEX::pge = this;
  13. }
  14. olc::rcode PixelGameEngine::Construct(uint32_t screen_w, uint32_t screen_h, uint32_t pixel_w, uint32_t pixel_h, bool full_screen)
  15. {
  16. nScreenWidth = screen_w;
  17. nScreenHeight = screen_h;
  18. nPixelWidth = pixel_w;
  19. nPixelHeight = pixel_h;
  20. bFullScreen = full_screen;
  21. fPixelX = 2.0f / (float)(nScreenWidth);
  22. fPixelY = 2.0f / (float)(nScreenHeight);
  23. if (nPixelWidth == 0 || nPixelHeight == 0 || nScreenWidth == 0 || nScreenHeight == 0)
  24. return olc::FAIL;
  25. #ifdef _WIN32
  26. #ifdef UNICODE
  27. #ifndef __MINGW32__
  28. wsAppName = ConvertS2W(sAppName);
  29. #endif
  30. #endif
  31. #endif
  32. // Load the default font sheet
  33. olc_ConstructFontSheet();
  34. // Create a sprite that represents the primary drawing target
  35. pDefaultDrawTarget = new Sprite(nScreenWidth, nScreenHeight);
  36. SetDrawTarget(nullptr);
  37. return olc::OK;
  38. }
  39. olc::rcode PixelGameEngine::ConstructAuto(uint32_t screen_w, uint32_t screen_h, bool fullscreen)
  40. {
  41. uint32_t pixelSize = olc_GetMaximumPixelSize(screen_w, screen_h);
  42. return Construct(screen_w, screen_h, pixelSize, pixelSize, fullscreen);
  43. }
  44. olc::rcode PixelGameEngine::Start()
  45. {
  46. // Construct the window
  47. if (!olc_WindowCreate())
  48. return olc::FAIL;
  49. // Load libraries required for PNG file interaction
  50. //#ifdef _WIN32
  51. // // Windows use GDI+
  52. // Gdiplus::GdiplusStartupInput startupInput;
  53. // ULONG_PTR token;
  54. // Gdiplus::GdiplusStartup(&token, &startupInput, NULL);
  55. //#else
  56. // // Linux use libpng
  57. //
  58. //#endif
  59. // Start the thread
  60. bAtomActive = true;
  61. std::thread t = std::thread(&PixelGameEngine::EngineThread, this);
  62. #ifdef _WIN32
  63. // Handle Windows Message Loop
  64. MSG msg;
  65. while (GetMessage(&msg, NULL, 0, 0) > 0)
  66. {
  67. TranslateMessage(&msg);
  68. DispatchMessage(&msg);
  69. }
  70. #endif
  71. // Wait for thread to be exited
  72. t.join();
  73. return olc::OK;
  74. }
  75. void PixelGameEngine::SetDrawTarget(Sprite *target)
  76. {
  77. if (target)
  78. pDrawTarget = target;
  79. else
  80. pDrawTarget = pDefaultDrawTarget;
  81. }
  82. Sprite* PixelGameEngine::GetDrawTarget()
  83. {
  84. return pDrawTarget;
  85. }
  86. int32_t PixelGameEngine::GetDrawTargetWidth()
  87. {
  88. if (pDrawTarget)
  89. return pDrawTarget->width;
  90. else
  91. return 0;
  92. }
  93. int32_t PixelGameEngine::GetDrawTargetHeight()
  94. {
  95. if (pDrawTarget)
  96. return pDrawTarget->height;
  97. else
  98. return 0;
  99. }
  100. bool PixelGameEngine::IsFocused()
  101. {
  102. return bHasInputFocus;
  103. }
  104. HWButton PixelGameEngine::GetKey(Key k)
  105. {
  106. return pKeyboardState[k];
  107. }
  108. HWButton PixelGameEngine::GetMouse(uint32_t b)
  109. {
  110. return pMouseState[b];
  111. }
  112. int32_t PixelGameEngine::GetMouseX()
  113. {
  114. return nMousePosX;
  115. }
  116. int32_t PixelGameEngine::GetMouseY()
  117. {
  118. return nMousePosY;
  119. }
  120. int32_t PixelGameEngine::GetMouseWheel()
  121. {
  122. return nMouseWheelDelta;
  123. }
  124. int32_t PixelGameEngine::ScreenWidth()
  125. {
  126. return nScreenWidth;
  127. }
  128. int32_t PixelGameEngine::ScreenHeight()
  129. {
  130. return nScreenHeight;
  131. }
  132. bool PixelGameEngine::Draw(int32_t x, int32_t y, Pixel p)
  133. {
  134. if (!pDrawTarget) return false;
  135. if (nPixelMode == Pixel::NORMAL)
  136. {
  137. return pDrawTarget->SetPixel(x, y, p);
  138. }
  139. if (nPixelMode == Pixel::MASK)
  140. {
  141. if(p.a == 255)
  142. return pDrawTarget->SetPixel(x, y, p);
  143. }
  144. if (nPixelMode == Pixel::ALPHA)
  145. {
  146. Pixel d = pDrawTarget->GetPixel(x, y);
  147. float a = (float)(p.a / 255.0f) * fBlendFactor;
  148. float c = 1.0f - a;
  149. float r = a * (float)p.r + c * (float)d.r;
  150. float g = a * (float)p.g + c * (float)d.g;
  151. float b = a * (float)p.b + c * (float)d.b;
  152. return pDrawTarget->SetPixel(x, y, Pixel((uint8_t)r, (uint8_t)g, (uint8_t)b));
  153. }
  154. if (nPixelMode == Pixel::CUSTOM)
  155. {
  156. return pDrawTarget->SetPixel(x, y, funcPixelMode(x, y, p, pDrawTarget->GetPixel(x, y)));
  157. }
  158. return false;
  159. }
  160. void PixelGameEngine::SetSubPixelOffset(float ox, float oy)
  161. {
  162. fSubPixelOffsetX = ox * fPixelX;
  163. fSubPixelOffsetY = oy * fPixelY;
  164. }
  165. void PixelGameEngine::DrawLine(int32_t x1, int32_t y1, int32_t x2, int32_t y2, Pixel p, uint32_t pattern)
  166. {
  167. int x, y, dx, dy, dx1, dy1, px, py, xe, ye, i;
  168. dx = x2 - x1; dy = y2 - y1;
  169. auto rol = [&](void)
  170. {
  171. pattern = (pattern << 1) | (pattern >> 31);
  172. return pattern & 1;
  173. };
  174. // straight lines idea by gurkanctn
  175. if (dx == 0) // Line is vertical
  176. {
  177. if (y2 < y1) std::swap(y1, y2);
  178. for (y = y1; y <= y2; y++)
  179. if (rol()) Draw(x1, y, p);
  180. return;
  181. }
  182. if (dy == 0) // Line is horizontal
  183. {
  184. if (x2 < x1) std::swap(x1, x2);
  185. for (x = x1; x <= x2; x++)
  186. if (rol()) Draw(x, y1, p);
  187. return;
  188. }
  189. // Line is Funk-aye
  190. dx1 = abs(dx); dy1 = abs(dy);
  191. px = 2 * dy1 - dx1; py = 2 * dx1 - dy1;
  192. if (dy1 <= dx1)
  193. {
  194. if (dx >= 0)
  195. {
  196. x = x1; y = y1; xe = x2;
  197. }
  198. else
  199. {
  200. x = x2; y = y2; xe = x1;
  201. }
  202. if (rol()) Draw(x, y, p);
  203. for (i = 0; x<xe; i++)
  204. {
  205. x = x + 1;
  206. if (px<0)
  207. px = px + 2 * dy1;
  208. else
  209. {
  210. if ((dx<0 && dy<0) || (dx>0 && dy>0)) y = y + 1; else y = y - 1;
  211. px = px + 2 * (dy1 - dx1);
  212. }
  213. if (rol()) Draw(x, y, p);
  214. }
  215. }
  216. else
  217. {
  218. if (dy >= 0)
  219. {
  220. x = x1; y = y1; ye = y2;
  221. }
  222. else
  223. {
  224. x = x2; y = y2; ye = y1;
  225. }
  226. if (rol()) Draw(x, y, p);
  227. for (i = 0; y<ye; i++)
  228. {
  229. y = y + 1;
  230. if (py <= 0)
  231. py = py + 2 * dx1;
  232. else
  233. {
  234. if ((dx<0 && dy<0) || (dx>0 && dy>0)) x = x + 1; else x = x - 1;
  235. py = py + 2 * (dx1 - dy1);
  236. }
  237. if (rol()) Draw(x, y, p);
  238. }
  239. }
  240. }
  241. void PixelGameEngine::DrawCircle(int32_t x, int32_t y, int32_t radius, Pixel p, uint8_t mask)
  242. {
  243. int x0 = 0;
  244. int y0 = radius;
  245. int d = 3 - 2 * radius;
  246. if (!radius) return;
  247. while (y0 >= x0) // only formulate 1/8 of circle
  248. {
  249. if (mask & 0x01) Draw(x + x0, y - y0, p);
  250. if (mask & 0x02) Draw(x + y0, y - x0, p);
  251. if (mask & 0x04) Draw(x + y0, y + x0, p);
  252. if (mask & 0x08) Draw(x + x0, y + y0, p);
  253. if (mask & 0x10) Draw(x - x0, y + y0, p);
  254. if (mask & 0x20) Draw(x - y0, y + x0, p);
  255. if (mask & 0x40) Draw(x - y0, y - x0, p);
  256. if (mask & 0x80) Draw(x - x0, y - y0, p);
  257. if (d < 0) d += 4 * x0++ + 6;
  258. else d += 4 * (x0++ - y0--) + 10;
  259. }
  260. }
  261. void PixelGameEngine::FillCircle(int32_t x, int32_t y, int32_t radius, Pixel p)
  262. {
  263. // Taken from wikipedia
  264. int x0 = 0;
  265. int y0 = radius;
  266. int d = 3 - 2 * radius;
  267. if (!radius) return;
  268. auto drawline = [&](int sx, int ex, int ny)
  269. {
  270. for (int i = sx; i <= ex; i++)
  271. Draw(i, ny, p);
  272. };
  273. while (y0 >= x0)
  274. {
  275. // Modified to draw scan-lines instead of edges
  276. drawline(x - x0, x + x0, y - y0);
  277. drawline(x - y0, x + y0, y - x0);
  278. drawline(x - x0, x + x0, y + y0);
  279. drawline(x - y0, x + y0, y + x0);
  280. if (d < 0) d += 4 * x0++ + 6;
  281. else d += 4 * (x0++ - y0--) + 10;
  282. }
  283. }
  284. void PixelGameEngine::DrawRect(int32_t x, int32_t y, int32_t w, int32_t h, Pixel p)
  285. {
  286. DrawLine(x, y, x+w, y, p);
  287. DrawLine(x+w, y, x+w, y+h, p);
  288. DrawLine(x+w, y+h, x, y+h, p);
  289. DrawLine(x, y+h, x, y, p);
  290. }
  291. void PixelGameEngine::Clear(Pixel p)
  292. {
  293. int pixels = GetDrawTargetWidth() * GetDrawTargetHeight();
  294. Pixel* m = GetDrawTarget()->GetData();
  295. for (int i = 0; i < pixels; i++)
  296. m[i] = p;
  297. #ifdef OLC_DBG_OVERDRAW
  298. olc::Sprite::nOverdrawCount += pixels;
  299. #endif
  300. }
  301. void PixelGameEngine::FillRect(int32_t x, int32_t y, int32_t w, int32_t h, Pixel p)
  302. {
  303. int32_t x2 = x + w;
  304. int32_t y2 = y + h;
  305. if (x < 0) x = 0;
  306. if (x >= (int32_t)nScreenWidth) x = (int32_t)nScreenWidth;
  307. if (y < 0) y = 0;
  308. if (y >= (int32_t)nScreenHeight) y = (int32_t)nScreenHeight;
  309. if (x2 < 0) x2 = 0;
  310. if (x2 >= (int32_t)nScreenWidth) x2 = (int32_t)nScreenWidth;
  311. if (y2 < 0) y2 = 0;
  312. if (y2 >= (int32_t)nScreenHeight) y2 = (int32_t)nScreenHeight;
  313. for (int i = x; i < x2; i++)
  314. for (int j = y; j < y2; j++)
  315. Draw(i, j, p);
  316. }
  317. void PixelGameEngine::DrawTriangle(int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t x3, int32_t y3, Pixel p)
  318. {
  319. DrawLine(x1, y1, x2, y2, p);
  320. DrawLine(x2, y2, x3, y3, p);
  321. DrawLine(x3, y3, x1, y1, p);
  322. }
  323. // https://www.avrfreaks.net/sites/default/files/triangles.c
  324. void PixelGameEngine::FillTriangle(int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t x3, int32_t y3, Pixel p)
  325. {
  326. auto SWAP = [](int &x, int &y) { int t = x; x = y; y = t; };
  327. auto drawline = [&](int sx, int ex, int ny) { for (int i = sx; i <= ex; i++) Draw(i, ny, p); };
  328. int t1x, t2x, y, minx, maxx, t1xp, t2xp;
  329. bool changed1 = false;
  330. bool changed2 = false;
  331. int signx1, signx2, dx1, dy1, dx2, dy2;
  332. int e1, e2;
  333. // Sort vertices
  334. if (y1>y2) { SWAP(y1, y2); SWAP(x1, x2); }
  335. if (y1>y3) { SWAP(y1, y3); SWAP(x1, x3); }
  336. if (y2>y3) { SWAP(y2, y3); SWAP(x2, x3); }
  337. t1x = t2x = x1; y = y1; // Starting points
  338. dx1 = (int)(x2 - x1); if (dx1<0) { dx1 = -dx1; signx1 = -1; }
  339. else signx1 = 1;
  340. dy1 = (int)(y2 - y1);
  341. dx2 = (int)(x3 - x1); if (dx2<0) { dx2 = -dx2; signx2 = -1; }
  342. else signx2 = 1;
  343. dy2 = (int)(y3 - y1);
  344. if (dy1 > dx1) { // swap values
  345. SWAP(dx1, dy1);
  346. changed1 = true;
  347. }
  348. if (dy2 > dx2) { // swap values
  349. SWAP(dy2, dx2);
  350. changed2 = true;
  351. }
  352. e2 = (int)(dx2 >> 1);
  353. // Flat top, just process the second half
  354. if (y1 == y2) goto next;
  355. e1 = (int)(dx1 >> 1);
  356. for (int i = 0; i < dx1;) {
  357. t1xp = 0; t2xp = 0;
  358. if (t1x<t2x) { minx = t1x; maxx = t2x; }
  359. else { minx = t2x; maxx = t1x; }
  360. // process first line until y value is about to change
  361. while (i<dx1) {
  362. i++;
  363. e1 += dy1;
  364. while (e1 >= dx1) {
  365. e1 -= dx1;
  366. if (changed1) t1xp = signx1;//t1x += signx1;
  367. else goto next1;
  368. }
  369. if (changed1) break;
  370. else t1x += signx1;
  371. }
  372. // Move line
  373. next1:
  374. // process second line until y value is about to change
  375. while (1) {
  376. e2 += dy2;
  377. while (e2 >= dx2) {
  378. e2 -= dx2;
  379. if (changed2) t2xp = signx2;//t2x += signx2;
  380. else goto next2;
  381. }
  382. if (changed2) break;
  383. else t2x += signx2;
  384. }
  385. next2:
  386. if (minx>t1x) minx = t1x; if (minx>t2x) minx = t2x;
  387. if (maxx<t1x) maxx = t1x; if (maxx<t2x) maxx = t2x;
  388. drawline(minx, maxx, y); // Draw line from min to max points found on the y
  389. // Now increase y
  390. if (!changed1) t1x += signx1;
  391. t1x += t1xp;
  392. if (!changed2) t2x += signx2;
  393. t2x += t2xp;
  394. y += 1;
  395. if (y == y2) break;
  396. }
  397. next:
  398. // Second half
  399. dx1 = (int)(x3 - x2); if (dx1<0) { dx1 = -dx1; signx1 = -1; }
  400. else signx1 = 1;
  401. dy1 = (int)(y3 - y2);
  402. t1x = x2;
  403. if (dy1 > dx1) { // swap values
  404. SWAP(dy1, dx1);
  405. changed1 = true;
  406. }
  407. else changed1 = false;
  408. e1 = (int)(dx1 >> 1);
  409. for (int i = 0; i <= dx1; i++) {
  410. t1xp = 0; t2xp = 0;
  411. if (t1x<t2x) { minx = t1x; maxx = t2x; }
  412. else { minx = t2x; maxx = t1x; }
  413. // process first line until y value is about to change
  414. while (i<dx1) {
  415. e1 += dy1;
  416. while (e1 >= dx1) {
  417. e1 -= dx1;
  418. if (changed1) { t1xp = signx1; break; }//t1x += signx1;
  419. else goto next3;
  420. }
  421. if (changed1) break;
  422. else t1x += signx1;
  423. if (i<dx1) i++;
  424. }
  425. next3:
  426. // process second line until y value is about to change
  427. while (t2x != x3) {
  428. e2 += dy2;
  429. while (e2 >= dx2) {
  430. e2 -= dx2;
  431. if (changed2) t2xp = signx2;
  432. else goto next4;
  433. }
  434. if (changed2) break;
  435. else t2x += signx2;
  436. }
  437. next4:
  438. if (minx>t1x) minx = t1x; if (minx>t2x) minx = t2x;
  439. if (maxx<t1x) maxx = t1x; if (maxx<t2x) maxx = t2x;
  440. drawline(minx, maxx, y);
  441. if (!changed1) t1x += signx1;
  442. t1x += t1xp;
  443. if (!changed2) t2x += signx2;
  444. t2x += t2xp;
  445. y += 1;
  446. if (y>y3) return;
  447. }
  448. }
  449. void PixelGameEngine::DrawSprite(int32_t x, int32_t y, Sprite *sprite, uint32_t scale)
  450. {
  451. if (sprite == nullptr)
  452. return;
  453. if (scale > 1)
  454. {
  455. for (int32_t i = 0; i < sprite->width; i++)
  456. for (int32_t j = 0; j < sprite->height; j++)
  457. for (uint32_t is = 0; is < scale; is++)
  458. for (uint32_t js = 0; js < scale; js++)
  459. Draw(x + (i*scale) + is, y + (j*scale) + js, sprite->GetPixel(i, j));
  460. }
  461. else
  462. {
  463. for (int32_t i = 0; i < sprite->width; i++)
  464. for (int32_t j = 0; j < sprite->height; j++)
  465. Draw(x + i, y + j, sprite->GetPixel(i, j));
  466. }
  467. }
  468. void PixelGameEngine::DrawPartialSprite(int32_t x, int32_t y, Sprite *sprite, int32_t ox, int32_t oy, int32_t w, int32_t h, uint32_t scale)
  469. {
  470. if (sprite == nullptr)
  471. return;
  472. if (scale > 1)
  473. {
  474. for (int32_t i = 0; i < w; i++)
  475. for (int32_t j = 0; j < h; j++)
  476. for (uint32_t is = 0; is < scale; is++)
  477. for (uint32_t js = 0; js < scale; js++)
  478. Draw(x + (i*scale) + is, y + (j*scale) + js, sprite->GetPixel(i + ox, j + oy));
  479. }
  480. else
  481. {
  482. for (int32_t i = 0; i < w; i++)
  483. for (int32_t j = 0; j < h; j++)
  484. Draw(x + i, y + j, sprite->GetPixel(i + ox, j + oy));
  485. }
  486. }
  487. void PixelGameEngine::DrawString(int32_t x, int32_t y, std::string sText, Pixel col, uint32_t scale)
  488. {
  489. int32_t sx = 0;
  490. int32_t sy = 0;
  491. Pixel::Mode m = nPixelMode;
  492. if(col.ALPHA != 255) SetPixelMode(Pixel::ALPHA);
  493. else SetPixelMode(Pixel::MASK);
  494. for (auto c : sText)
  495. {
  496. if (c == '\n')
  497. {
  498. sx = 0; sy += 8 * scale;
  499. }
  500. else
  501. {
  502. int32_t ox = (c - 32) % 16;
  503. int32_t oy = (c - 32) / 16;
  504. if (scale > 1)
  505. {
  506. for (uint32_t i = 0; i < 8; i++)
  507. for (uint32_t j = 0; j < 8; j++)
  508. if (fontSprite->GetPixel(i + ox * 8, j + oy * 8).r > 0)
  509. for (uint32_t is = 0; is < scale; is++)
  510. for (uint32_t js = 0; js < scale; js++)
  511. Draw(x + sx + (i*scale) + is, y + sy + (j*scale) + js, col);
  512. }
  513. else
  514. {
  515. for (uint32_t i = 0; i < 8; i++)
  516. for (uint32_t j = 0; j < 8; j++)
  517. if (fontSprite->GetPixel(i + ox * 8, j + oy * 8).r > 0)
  518. Draw(x + sx + i, y + sy + j, col);
  519. }
  520. sx += 8 * scale;
  521. }
  522. }
  523. SetPixelMode(m);
  524. }
  525. void PixelGameEngine::SetPixelMode(Pixel::Mode m)
  526. {
  527. nPixelMode = m;
  528. }
  529. Pixel::Mode PixelGameEngine::GetPixelMode()
  530. {
  531. return nPixelMode;
  532. }
  533. void PixelGameEngine::SetPixelMode(std::function<olc::Pixel(const int x, const int y, const olc::Pixel&, const olc::Pixel&)> pixelMode)
  534. {
  535. funcPixelMode = pixelMode;
  536. nPixelMode = Pixel::Mode::CUSTOM;
  537. }
  538. void PixelGameEngine::SetPixelBlend(float fBlend)
  539. {
  540. fBlendFactor = fBlend;
  541. if (fBlendFactor < 0.0f) fBlendFactor = 0.0f;
  542. if (fBlendFactor > 1.0f) fBlendFactor = 1.0f;
  543. }
  544. // User must override these functions as required. I have not made
  545. // them abstract because I do need a default behaviour to occur if
  546. // they are not overwritten
  547. bool PixelGameEngine::OnUserCreate()
  548. { return false; }
  549. bool PixelGameEngine::OnUserUpdate(float fElapsedTime)
  550. { return false; }
  551. bool PixelGameEngine::OnUserDestroy()
  552. { return true; }
  553. //////////////////////////////////////////////////////////////////
  554. void PixelGameEngine::olc_UpdateViewport()
  555. {
  556. int32_t ww = nScreenWidth * nPixelWidth;
  557. int32_t wh = nScreenHeight * nPixelHeight;
  558. float wasp = (float)ww / (float)wh;
  559. nViewW = (int32_t)nWindowWidth;
  560. nViewH = (int32_t)((float)nViewW / wasp);
  561. if (nViewH > nWindowHeight)
  562. {
  563. nViewH = nWindowHeight;
  564. nViewW = (int32_t)((float)nViewH * wasp);
  565. }
  566. nViewX = (nWindowWidth - nViewW) / 2;
  567. nViewY = (nWindowHeight - nViewH) / 2;
  568. }
  569. void PixelGameEngine::olc_UpdateWindowSize(int32_t x, int32_t y)
  570. {
  571. nWindowWidth = x;
  572. nWindowHeight = y;
  573. olc_UpdateViewport();
  574. }
  575. void PixelGameEngine::olc_UpdateMouseWheel(int32_t delta)
  576. {
  577. nMouseWheelDeltaCache += delta;
  578. }
  579. void PixelGameEngine::olc_UpdateMouse(int32_t x, int32_t y)
  580. {
  581. // Mouse coords come in screen space
  582. // But leave in pixel space
  583. //if (bFullScreen)
  584. {
  585. // Full Screen mode may have a weird viewport we must clamp to
  586. x -= nViewX;
  587. y -= nViewY;
  588. }
  589. nMousePosXcache = (int32_t)(((float)x / (float)(nWindowWidth - (nViewX * 2)) * (float)nScreenWidth));
  590. nMousePosYcache = (int32_t)(((float)y / (float)(nWindowHeight - (nViewY * 2)) * (float)nScreenHeight));
  591. if (nMousePosXcache >= (int32_t)nScreenWidth)
  592. nMousePosXcache = nScreenWidth - 1;
  593. if (nMousePosYcache >= (int32_t)nScreenHeight)
  594. nMousePosYcache = nScreenHeight - 1;
  595. if (nMousePosXcache < 0)
  596. nMousePosXcache = 0;
  597. if (nMousePosYcache < 0)
  598. nMousePosYcache = 0;
  599. }
  600. #ifdef _WIN32
  601. static int minWidth;
  602. static int minHeight;
  603. static WINAPI BOOL MonitorInfoProc(HMONITOR mon, HDC dc, LPRECT rect, LPARAM param) {
  604. MONITORINFO info;
  605. info.cbSize = sizeof(MONITORINFO);
  606. GetMonitorInfo(mon, &info);
  607. int w = info.rcWork.right - info.rcWork.left;
  608. int h = info.rcWork.bottom - info.rcWork.top;
  609. if (w < minWidth) {
  610. minWidth = w;
  611. }
  612. if (h < minHeight) {
  613. minHeight = h;
  614. }
  615. return TRUE;
  616. }
  617. #endif
  618. uint32_t PixelGameEngine::olc_GetMaximumPixelSize(uint32_t width, uint32_t height)
  619. {
  620. #ifdef _WIN32
  621. minWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);
  622. minHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);
  623. EnumDisplayMonitors(NULL, NULL, &MonitorInfoProc, 0);
  624. #else
  625. Display* d = XOpenDisplay(NULL);
  626. int defaultScreen = DefaultScreen(d);
  627. Screen *s = ScreenOfDisplay(d, defaultScreen);
  628. int minWidth = WidthOfScreen(s);
  629. int minHeight = HeightOfScreen(s);
  630. int screensCount = ScreenCount(d);
  631. for (int i=0; i < screensCount; i++) {
  632. s = ScreenOfDisplay(d, i);
  633. if (WidthOfScreen(s) < minWidth) {
  634. minWidth = WidthOfScreen(s);
  635. }
  636. if (HeightOfScreen(s) < minHeight) {
  637. minHeight = HeightOfScreen(s);
  638. }
  639. }
  640. #endif
  641. uint32_t pixelWidth = minWidth / width;
  642. uint32_t pixelHeight = minHeight / height;
  643. return std::min(pixelWidth, pixelHeight);
  644. }
  645. void PixelGameEngine::EngineThread()
  646. {
  647. // Start OpenGL, the context is owned by the game thread
  648. olc_OpenGLCreate();
  649. // Create Screen Texture - disable filtering
  650. glEnable(GL_TEXTURE_2D);
  651. glGenTextures(1, &glBuffer);
  652. glBindTexture(GL_TEXTURE_2D, glBuffer);
  653. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  654. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  655. glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
  656. glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, nScreenWidth, nScreenHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, pDefaultDrawTarget->GetData());
  657. // Create user resources as part of this thread
  658. if (!OnUserCreate())
  659. bAtomActive = false;
  660. auto tp1 = std::chrono::system_clock::now();
  661. auto tp2 = std::chrono::system_clock::now();
  662. while (bAtomActive)
  663. {
  664. // Run as fast as possible
  665. while (bAtomActive)
  666. {
  667. // Handle Timing
  668. tp2 = std::chrono::system_clock::now();
  669. std::chrono::duration<float> elapsedTime = tp2 - tp1;
  670. tp1 = tp2;
  671. // Our time per frame coefficient
  672. float fElapsedTime = elapsedTime.count();
  673. #ifndef _WIN32
  674. // Handle Xlib Message Loop - we do this in the
  675. // same thread that OpenGL was created so we dont
  676. // need to worry too much about multithreading with X11
  677. XEvent xev;
  678. while (XPending(olc_Display))
  679. {
  680. XNextEvent(olc_Display, &xev);
  681. if (xev.type == Expose)
  682. {
  683. XWindowAttributes gwa;
  684. XGetWindowAttributes(olc_Display, olc_Window, &gwa);
  685. nWindowWidth = gwa.width;
  686. nWindowHeight = gwa.height;
  687. olc_UpdateViewport();
  688. glClear(GL_COLOR_BUFFER_BIT); // Thanks Benedani!
  689. }
  690. else if (xev.type == ConfigureNotify)
  691. {
  692. XConfigureEvent xce = xev.xconfigure;
  693. nWindowWidth = xce.width;
  694. nWindowHeight = xce.height;
  695. }
  696. else if (xev.type == KeyPress)
  697. {
  698. KeySym sym = XLookupKeysym(&xev.xkey, 0);
  699. pKeyNewState[mapKeys[sym]] = true;
  700. XKeyEvent *e = (XKeyEvent *)&xev; // Because DragonEye loves numpads
  701. XLookupString(e, NULL, 0, &sym, NULL);
  702. pKeyNewState[mapKeys[sym]] = true;
  703. }
  704. else if (xev.type == KeyRelease)
  705. {
  706. KeySym sym = XLookupKeysym(&xev.xkey, 0);
  707. pKeyNewState[mapKeys[sym]] = false;
  708. XKeyEvent *e = (XKeyEvent *)&xev;
  709. XLookupString(e, NULL, 0, &sym, NULL);
  710. pKeyNewState[mapKeys[sym]] = false;
  711. }
  712. else if (xev.type == ButtonPress)
  713. {
  714. switch (xev.xbutton.button)
  715. {
  716. case 1: pMouseNewState[0] = true; break;
  717. case 2: pMouseNewState[2] = true; break;
  718. case 3: pMouseNewState[1] = true; break;
  719. case 4: olc_UpdateMouseWheel(120); break;
  720. case 5: olc_UpdateMouseWheel(-120); break;
  721. default: break;
  722. }
  723. }
  724. else if (xev.type == ButtonRelease)
  725. {
  726. switch (xev.xbutton.button)
  727. {
  728. case 1: pMouseNewState[0] = false; break;
  729. case 2: pMouseNewState[2] = false; break;
  730. case 3: pMouseNewState[1] = false; break;
  731. default: break;
  732. }
  733. }
  734. else if (xev.type == MotionNotify)
  735. {
  736. olc_UpdateMouse(xev.xmotion.x, xev.xmotion.y);
  737. }
  738. else if (xev.type == FocusIn)
  739. {
  740. bHasInputFocus = true;
  741. }
  742. else if (xev.type == FocusOut)
  743. {
  744. bHasInputFocus = false;
  745. }
  746. else if (xev.type == ClientMessage)
  747. {
  748. bAtomActive = false;
  749. }
  750. }
  751. #endif
  752. // Handle User Input - Keyboard
  753. for (int i = 0; i < 256; i++)
  754. {
  755. pKeyboardState[i].bPressed = false;
  756. pKeyboardState[i].bReleased = false;
  757. if (pKeyNewState[i] != pKeyOldState[i])
  758. {
  759. if (pKeyNewState[i])
  760. {
  761. pKeyboardState[i].bPressed = !pKeyboardState[i].bHeld;
  762. pKeyboardState[i].bHeld = true;
  763. }
  764. else
  765. {
  766. pKeyboardState[i].bReleased = true;
  767. pKeyboardState[i].bHeld = false;
  768. }
  769. }
  770. pKeyOldState[i] = pKeyNewState[i];
  771. }
  772. // Handle User Input - Mouse
  773. for (int i = 0; i < 5; i++)
  774. {
  775. pMouseState[i].bPressed = false;
  776. pMouseState[i].bReleased = false;
  777. if (pMouseNewState[i] != pMouseOldState[i])
  778. {
  779. if (pMouseNewState[i])
  780. {
  781. pMouseState[i].bPressed = !pMouseState[i].bHeld;
  782. pMouseState[i].bHeld = true;
  783. }
  784. else
  785. {
  786. pMouseState[i].bReleased = true;
  787. pMouseState[i].bHeld = false;
  788. }
  789. }
  790. pMouseOldState[i] = pMouseNewState[i];
  791. }
  792. // Cache mouse coordinates so they remain
  793. // consistent during frame
  794. nMousePosX = nMousePosXcache;
  795. nMousePosY = nMousePosYcache;
  796. nMouseWheelDelta = nMouseWheelDeltaCache;
  797. nMouseWheelDeltaCache = 0;
  798. #ifdef OLC_DBG_OVERDRAW
  799. olc::Sprite::nOverdrawCount = 0;
  800. #endif
  801. // Handle Frame Update
  802. if (!OnUserUpdate(fElapsedTime))
  803. bAtomActive = false;
  804. // Display Graphics
  805. glViewport(nViewX, nViewY, nViewW, nViewH);
  806. // TODO: This is a bit slow (especially in debug, but 100x faster in release mode???)
  807. // Copy pixel array into texture
  808. glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, nScreenWidth, nScreenHeight, GL_RGBA, GL_UNSIGNED_BYTE, pDefaultDrawTarget->GetData());
  809. // Display texture on screen
  810. glBegin(GL_QUADS);
  811. glTexCoord2f(0.0, 1.0); glVertex3f(-1.0f + (fSubPixelOffsetX), -1.0f + (fSubPixelOffsetY), 0.0f);
  812. glTexCoord2f(0.0, 0.0); glVertex3f(-1.0f + (fSubPixelOffsetX), 1.0f + (fSubPixelOffsetY), 0.0f);
  813. glTexCoord2f(1.0, 0.0); glVertex3f( 1.0f + (fSubPixelOffsetX), 1.0f + (fSubPixelOffsetY), 0.0f);
  814. glTexCoord2f(1.0, 1.0); glVertex3f( 1.0f + (fSubPixelOffsetX), -1.0f + (fSubPixelOffsetY), 0.0f);
  815. glEnd();
  816. // Present Graphics to screen
  817. #ifdef _WIN32
  818. SwapBuffers(glDeviceContext);
  819. #else
  820. glXSwapBuffers(olc_Display, olc_Window);
  821. #endif
  822. // Update Title Bar
  823. fFrameTimer += fElapsedTime;
  824. nFrameCount++;
  825. if (fFrameTimer >= 1.0f)
  826. {
  827. fFrameTimer -= 1.0f;
  828. std::string sTitle = "OneLoneCoder.com - Pixel Game Engine - " + sAppName + " - FPS: " + std::to_string(nFrameCount);
  829. #ifdef _WIN32
  830. #ifdef UNICODE
  831. SetWindowText(olc_hWnd, ConvertS2W(sTitle).c_str());
  832. #else
  833. SetWindowText(olc_hWnd, sTitle.c_str());
  834. #endif
  835. #else
  836. XStoreName(olc_Display, olc_Window, sTitle.c_str());
  837. #endif
  838. nFrameCount = 0;
  839. }
  840. }
  841. // Allow the user to free resources if they have overrided the destroy function
  842. if (OnUserDestroy())
  843. {
  844. // User has permitted destroy, so exit and clean up
  845. }
  846. else
  847. {
  848. // User denied destroy for some reason, so continue running
  849. bAtomActive = true;
  850. }
  851. }
  852. #ifdef _WIN32
  853. wglDeleteContext(glRenderContext);
  854. PostMessage(olc_hWnd, WM_DESTROY, 0, 0);
  855. #else
  856. glXMakeCurrent(olc_Display, None, NULL);
  857. glXDestroyContext(olc_Display, glDeviceContext);
  858. XDestroyWindow(olc_Display, olc_Window);
  859. XCloseDisplay(olc_Display);
  860. #endif
  861. }
  862. void PixelGameEngine::olc_ConstructFontSheet()
  863. {
  864. std::string data;
  865. data += "?Q`0001oOch0o01o@F40o0<AGD4090LAGD<090@A7ch0?00O7Q`0600>00000000";
  866. data += "O000000nOT0063Qo4d8>?7a14Gno94AA4gno94AaOT0>o3`oO400o7QN00000400";
  867. data += "Of80001oOg<7O7moBGT7O7lABET024@aBEd714AiOdl717a_=TH013Q>00000000";
  868. data += "720D000V?V5oB3Q_HdUoE7a9@DdDE4A9@DmoE4A;Hg]oM4Aj8S4D84@`00000000";
  869. data += "OaPT1000Oa`^13P1@AI[?g`1@A=[OdAoHgljA4Ao?WlBA7l1710007l100000000";
  870. data += "ObM6000oOfMV?3QoBDD`O7a0BDDH@5A0BDD<@5A0BGeVO5ao@CQR?5Po00000000";
  871. data += "Oc``000?Ogij70PO2D]??0Ph2DUM@7i`2DTg@7lh2GUj?0TO0C1870T?00000000";
  872. data += "70<4001o?P<7?1QoHg43O;`h@GT0@:@LB@d0>:@hN@L0@?aoN@<0O7ao0000?000";
  873. data += "OcH0001SOglLA7mg24TnK7ln24US>0PL24U140PnOgl0>7QgOcH0K71S0000A000";
  874. data += "00H00000@Dm1S007@DUSg00?OdTnH7YhOfTL<7Yh@Cl0700?@Ah0300700000000";
  875. data += "<008001QL00ZA41a@6HnI<1i@FHLM81M@@0LG81?O`0nC?Y7?`0ZA7Y300080000";
  876. data += "O`082000Oh0827mo6>Hn?Wmo?6HnMb11MP08@C11H`08@FP0@@0004@000000000";
  877. data += "00P00001Oab00003OcKP0006@6=PMgl<@440MglH@000000`@000001P00000000";
  878. data += "Ob@8@@00Ob@8@Ga13R@8Mga172@8?PAo3R@827QoOb@820@0O`0007`0000007P0";
  879. data += "O`000P08Od400g`<3V=P0G`673IP0`@3>1`00P@6O`P00g`<O`000GP800000000";
  880. data += "?P9PL020O`<`N3R0@E4HC7b0@ET<ATB0@@l6C4B0O`H3N7b0?P01L3R000000020";
  881. fontSprite = new olc::Sprite(128, 48);
  882. int px = 0, py = 0;
  883. for (int b = 0; b < 1024; b += 4)
  884. {
  885. uint32_t sym1 = (uint32_t)data[b + 0] - 48;
  886. uint32_t sym2 = (uint32_t)data[b + 1] - 48;
  887. uint32_t sym3 = (uint32_t)data[b + 2] - 48;
  888. uint32_t sym4 = (uint32_t)data[b + 3] - 48;
  889. uint32_t r = sym1 << 18 | sym2 << 12 | sym3 << 6 | sym4;
  890. for (int i = 0; i < 24; i++)
  891. {
  892. int k = r & (1 << i) ? 255 : 0;
  893. fontSprite->SetPixel(px, py, olc::Pixel(k, k, k, k));
  894. if (++py == 48) { px++; py = 0; }
  895. }
  896. }
  897. }
  898. #ifdef _WIN32
  899. HWND PixelGameEngine::olc_WindowCreate()
  900. {
  901. WNDCLASS wc;
  902. wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
  903. wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  904. wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
  905. wc.hInstance = GetModuleHandle(nullptr);
  906. wc.lpfnWndProc = olc_WindowEvent;
  907. wc.cbClsExtra = 0;
  908. wc.cbWndExtra = 0;
  909. wc.lpszMenuName = nullptr;
  910. wc.hbrBackground = nullptr;
  911. #ifdef UNICODE
  912. wc.lpszClassName = L"OLC_PIXEL_GAME_ENGINE";
  913. #else
  914. wc.lpszClassName = "OLC_PIXEL_GAME_ENGINE";
  915. #endif
  916. RegisterClass(&wc);
  917. nWindowWidth = (LONG)nScreenWidth * (LONG)nPixelWidth;
  918. nWindowHeight = (LONG)nScreenHeight * (LONG)nPixelHeight;
  919. // Define window furniture
  920. DWORD dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
  921. DWORD dwStyle = WS_CAPTION | WS_SYSMENU | WS_VISIBLE; // | WS_THICKFRAME;
  922. int nCosmeticOffset = 30;
  923. nViewW = nWindowWidth;
  924. nViewH = nWindowHeight;
  925. // Handle Fullscreen
  926. if (bFullScreen)
  927. {
  928. dwExStyle = 0;
  929. dwStyle = WS_VISIBLE | WS_POPUP;
  930. nCosmeticOffset = 0;
  931. HMONITOR hmon = MonitorFromWindow(olc_hWnd, MONITOR_DEFAULTTONEAREST);
  932. MONITORINFO mi = { sizeof(mi) };
  933. if (!GetMonitorInfo(hmon, &mi)) return NULL;
  934. nWindowWidth = mi.rcMonitor.right;
  935. nWindowHeight = mi.rcMonitor.bottom;
  936. }
  937. olc_UpdateViewport();
  938. // Keep client size as requested
  939. RECT rWndRect = { 0, 0, nWindowWidth, nWindowHeight };
  940. AdjustWindowRectEx(&rWndRect, dwStyle, FALSE, dwExStyle);
  941. int width = rWndRect.right - rWndRect.left;
  942. int height = rWndRect.bottom - rWndRect.top;
  943. #ifdef UNICODE
  944. olc_hWnd = CreateWindowEx(dwExStyle, L"OLC_PIXEL_GAME_ENGINE", L"", dwStyle,
  945. nCosmeticOffset, nCosmeticOffset, width, height, NULL, NULL, GetModuleHandle(nullptr), this);
  946. #else
  947. olc_hWnd = CreateWindowEx(dwExStyle, "OLC_PIXEL_GAME_ENGINE", "", dwStyle,
  948. nCosmeticOffset, nCosmeticOffset, width, height, NULL, NULL, GetModuleHandle(nullptr), this);
  949. #endif
  950. // Create Keyboard Mapping
  951. mapKeys[0x00] = Key::NONE;
  952. mapKeys[0x41] = Key::A; mapKeys[0x42] = Key::B; mapKeys[0x43] = Key::C; mapKeys[0x44] = Key::D; mapKeys[0x45] = Key::E;
  953. mapKeys[0x46] = Key::F; mapKeys[0x47] = Key::G; mapKeys[0x48] = Key::H; mapKeys[0x49] = Key::I; mapKeys[0x4A] = Key::J;
  954. mapKeys[0x4B] = Key::K; mapKeys[0x4C] = Key::L; mapKeys[0x4D] = Key::M; mapKeys[0x4E] = Key::N; mapKeys[0x4F] = Key::O;
  955. mapKeys[0x50] = Key::P; mapKeys[0x51] = Key::Q; mapKeys[0x52] = Key::R; mapKeys[0x53] = Key::S; mapKeys[0x54] = Key::T;
  956. mapKeys[0x55] = Key::U; mapKeys[0x56] = Key::V; mapKeys[0x57] = Key::W; mapKeys[0x58] = Key::X; mapKeys[0x59] = Key::Y;
  957. mapKeys[0x5A] = Key::Z;
  958. mapKeys[VK_F1] = Key::F1; mapKeys[VK_F2] = Key::F2; mapKeys[VK_F3] = Key::F3; mapKeys[VK_F4] = Key::F4;
  959. mapKeys[VK_F5] = Key::F5; mapKeys[VK_F6] = Key::F6; mapKeys[VK_F7] = Key::F7; mapKeys[VK_F8] = Key::F8;
  960. mapKeys[VK_F9] = Key::F9; mapKeys[VK_F10] = Key::F10; mapKeys[VK_F11] = Key::F11; mapKeys[VK_F12] = Key::F12;
  961. mapKeys[VK_DOWN] = Key::DOWN; mapKeys[VK_LEFT] = Key::LEFT; mapKeys[VK_RIGHT] = Key::RIGHT; mapKeys[VK_UP] = Key::UP;
  962. mapKeys[VK_RETURN] = Key::ENTER; //mapKeys[VK_RETURN] = Key::RETURN;
  963. mapKeys[VK_BACK] = Key::BACK; mapKeys[VK_ESCAPE] = Key::ESCAPE; mapKeys[VK_RETURN] = Key::ENTER; mapKeys[VK_PAUSE] = Key::PAUSE;
  964. mapKeys[VK_SCROLL] = Key::SCROLL; mapKeys[VK_TAB] = Key::TAB; mapKeys[VK_DELETE] = Key::DEL; mapKeys[VK_HOME] = Key::HOME;
  965. mapKeys[VK_END] = Key::END; mapKeys[VK_PRIOR] = Key::PGUP; mapKeys[VK_NEXT] = Key::PGDN; mapKeys[VK_INSERT] = Key::INS;
  966. mapKeys[VK_SHIFT] = Key::SHIFT; mapKeys[VK_CONTROL] = Key::CTRL;
  967. mapKeys[VK_SPACE] = Key::SPACE;
  968. mapKeys[0x30] = Key::K0; mapKeys[0x31] = Key::K1; mapKeys[0x32] = Key::K2; mapKeys[0x33] = Key::K3; mapKeys[0x34] = Key::K4;
  969. mapKeys[0x35] = Key::K5; mapKeys[0x36] = Key::K6; mapKeys[0x37] = Key::K7; mapKeys[0x38] = Key::K8; mapKeys[0x39] = Key::K9;
  970. mapKeys[VK_NUMPAD0] = Key::NP0; mapKeys[VK_NUMPAD1] = Key::NP1; mapKeys[VK_NUMPAD2] = Key::NP2; mapKeys[VK_NUMPAD3] = Key::NP3; mapKeys[VK_NUMPAD4] = Key::NP4;
  971. mapKeys[VK_NUMPAD5] = Key::NP5; mapKeys[VK_NUMPAD6] = Key::NP6; mapKeys[VK_NUMPAD7] = Key::NP7; mapKeys[VK_NUMPAD8] = Key::NP8; mapKeys[VK_NUMPAD9] = Key::NP9;
  972. mapKeys[VK_MULTIPLY] = Key::NP_MUL; mapKeys[VK_ADD] = Key::NP_ADD; mapKeys[VK_DIVIDE] = Key::NP_DIV; mapKeys[VK_SUBTRACT] = Key::NP_SUB; mapKeys[VK_DECIMAL] = Key::NP_DECIMAL;
  973. return olc_hWnd;
  974. }
  975. bool PixelGameEngine::olc_OpenGLCreate()
  976. {
  977. // Create Device Context
  978. glDeviceContext = GetDC(olc_hWnd);
  979. PIXELFORMATDESCRIPTOR pfd =
  980. {
  981. sizeof(PIXELFORMATDESCRIPTOR), 1,
  982. PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
  983. PFD_TYPE_RGBA, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  984. PFD_MAIN_PLANE, 0, 0, 0, 0
  985. };
  986. int pf = 0;
  987. if (!(pf = ChoosePixelFormat(glDeviceContext, &pfd))) return false;
  988. SetPixelFormat(glDeviceContext, pf, &pfd);
  989. if (!(glRenderContext = wglCreateContext(glDeviceContext))) return false;
  990. wglMakeCurrent(glDeviceContext, glRenderContext);
  991. glViewport(nViewX, nViewY, nViewW, nViewH);
  992. // Remove Frame cap
  993. wglSwapInterval = (wglSwapInterval_t*)wglGetProcAddress("wglSwapIntervalEXT");
  994. if (wglSwapInterval) wglSwapInterval(0);
  995. return true;
  996. }
  997. // Windows Event Handler
  998. LRESULT CALLBACK PixelGameEngine::olc_WindowEvent(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  999. {
  1000. static PixelGameEngine *sge;
  1001. switch (uMsg)
  1002. {
  1003. case WM_CREATE: sge = (PixelGameEngine*)((LPCREATESTRUCT)lParam)->lpCreateParams; return 0;
  1004. case WM_MOUSEMOVE:
  1005. {
  1006. uint16_t x = lParam & 0xFFFF; // Thanks @ForAbby (Discord)
  1007. uint16_t y = (lParam >> 16) & 0xFFFF;
  1008. int16_t ix = *(int16_t*)&x;
  1009. int16_t iy = *(int16_t*)&y;
  1010. sge->olc_UpdateMouse(ix, iy);
  1011. return 0;
  1012. }
  1013. case WM_SIZE:
  1014. {
  1015. sge->olc_UpdateWindowSize(lParam & 0xFFFF, (lParam >> 16) & 0xFFFF);
  1016. return 0;
  1017. }
  1018. case WM_MOUSEWHEEL:
  1019. {
  1020. sge->olc_UpdateMouseWheel(GET_WHEEL_DELTA_WPARAM(wParam));
  1021. return 0;
  1022. }
  1023. case WM_MOUSELEAVE: sge->bHasMouseFocus = false; return 0;
  1024. case WM_SETFOCUS: sge->bHasInputFocus = true; return 0;
  1025. case WM_KILLFOCUS: sge->bHasInputFocus = false; return 0;
  1026. case WM_KEYDOWN: sge->pKeyNewState[mapKeys[wParam]] = true; return 0;
  1027. case WM_KEYUP: sge->pKeyNewState[mapKeys[wParam]] = false; return 0;
  1028. case WM_LBUTTONDOWN:sge->pMouseNewState[0] = true; return 0;
  1029. case WM_LBUTTONUP: sge->pMouseNewState[0] = false; return 0;
  1030. case WM_RBUTTONDOWN:sge->pMouseNewState[1] = true; return 0;
  1031. case WM_RBUTTONUP: sge->pMouseNewState[1] = false; return 0;
  1032. case WM_MBUTTONDOWN:sge->pMouseNewState[2] = true; return 0;
  1033. case WM_MBUTTONUP: sge->pMouseNewState[2] = false; return 0;
  1034. case WM_CLOSE: bAtomActive = false; return 0;
  1035. case WM_DESTROY: PostQuitMessage(0); return 0;
  1036. }
  1037. return DefWindowProc(hWnd, uMsg, wParam, lParam);
  1038. }
  1039. #else
  1040. // Do the Linux stuff!
  1041. Display* PixelGameEngine::olc_WindowCreate()
  1042. {
  1043. XInitThreads();
  1044. // Grab the deafult display and window
  1045. olc_Display = XOpenDisplay(NULL);
  1046. olc_WindowRoot = DefaultRootWindow(olc_Display);
  1047. // Based on the display capabilities, configure the appearance of the window
  1048. GLint olc_GLAttribs[] = { GLX_RGBA, GLX_DEPTH_SIZE, 24, GLX_DOUBLEBUFFER, None };
  1049. olc_VisualInfo = glXChooseVisual(olc_Display, 0, olc_GLAttribs);
  1050. olc_ColourMap = XCreateColormap(olc_Display, olc_WindowRoot, olc_VisualInfo->visual, AllocNone);
  1051. olc_SetWindowAttribs.colormap = olc_ColourMap;
  1052. // Register which events we are interested in receiving
  1053. olc_SetWindowAttribs.event_mask = ExposureMask | KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | FocusChangeMask | StructureNotifyMask;
  1054. // Create the window
  1055. olc_Window = XCreateWindow(olc_Display, olc_WindowRoot, 30, 30, nScreenWidth * nPixelWidth, nScreenHeight * nPixelHeight, 0, olc_VisualInfo->depth, InputOutput, olc_VisualInfo->visual, CWColormap | CWEventMask, &olc_SetWindowAttribs);
  1056. Atom wmDelete = XInternAtom(olc_Display, "WM_DELETE_WINDOW", true);
  1057. XSetWMProtocols(olc_Display, olc_Window, &wmDelete, 1);
  1058. XMapWindow(olc_Display, olc_Window);
  1059. XStoreName(olc_Display, olc_Window, "OneLoneCoder.com - Pixel Game Engine");
  1060. if (bFullScreen) // Thanks DragonEye, again :D
  1061. {
  1062. Atom wm_state;
  1063. Atom fullscreen;
  1064. wm_state = XInternAtom(olc_Display, "_NET_WM_STATE", False);
  1065. fullscreen = XInternAtom(olc_Display, "_NET_WM_STATE_FULLSCREEN", False);
  1066. XEvent xev{ 0 };
  1067. xev.type = ClientMessage;
  1068. xev.xclient.window = olc_Window;
  1069. xev.xclient.message_type = wm_state;
  1070. xev.xclient.format = 32;
  1071. xev.xclient.data.l[0] = (bFullScreen ? 1 : 0); // the action (0: off, 1: on, 2: toggle)
  1072. xev.xclient.data.l[1] = fullscreen; // first property to alter
  1073. xev.xclient.data.l[2] = 0; // second property to alter
  1074. xev.xclient.data.l[3] = 0; // source indication
  1075. XMapWindow(olc_Display, olc_Window);
  1076. XSendEvent(olc_Display, DefaultRootWindow(olc_Display), False,
  1077. SubstructureRedirectMask | SubstructureNotifyMask, &xev);
  1078. XFlush(olc_Display);
  1079. XWindowAttributes gwa;
  1080. XGetWindowAttributes(olc_Display, olc_Window, &gwa);
  1081. nWindowWidth = gwa.width;
  1082. nWindowHeight = gwa.height;
  1083. olc_UpdateViewport();
  1084. } else {
  1085. olc_SizeHints.flags = PMaxSize | PMinSize;
  1086. olc_SizeHints.max_width = nScreenWidth * nPixelWidth;
  1087. olc_SizeHints.max_height = nScreenHeight * nPixelHeight;
  1088. olc_SizeHints.min_width = nScreenWidth * nPixelWidth;
  1089. olc_SizeHints.min_height = nScreenHeight * nPixelHeight;
  1090. XSetWMNormalHints(olc_Display, olc_Window, &olc_SizeHints);
  1091. }
  1092. // Create Keyboard Mapping
  1093. mapKeys[0x00] = Key::NONE;
  1094. mapKeys[0x61] = Key::A; mapKeys[0x62] = Key::B; mapKeys[0x63] = Key::C; mapKeys[0x64] = Key::D; mapKeys[0x65] = Key::E;
  1095. mapKeys[0x66] = Key::F; mapKeys[0x67] = Key::G; mapKeys[0x68] = Key::H; mapKeys[0x69] = Key::I; mapKeys[0x6A] = Key::J;
  1096. mapKeys[0x6B] = Key::K; mapKeys[0x6C] = Key::L; mapKeys[0x6D] = Key::M; mapKeys[0x6E] = Key::N; mapKeys[0x6F] = Key::O;
  1097. mapKeys[0x70] = Key::P; mapKeys[0x71] = Key::Q; mapKeys[0x72] = Key::R; mapKeys[0x73] = Key::S; mapKeys[0x74] = Key::T;
  1098. mapKeys[0x75] = Key::U; mapKeys[0x76] = Key::V; mapKeys[0x77] = Key::W; mapKeys[0x78] = Key::X; mapKeys[0x79] = Key::Y;
  1099. mapKeys[0x7A] = Key::Z;
  1100. mapKeys[XK_F1] = Key::F1; mapKeys[XK_F2] = Key::F2; mapKeys[XK_F3] = Key::F3; mapKeys[XK_F4] = Key::F4;
  1101. mapKeys[XK_F5] = Key::F5; mapKeys[XK_F6] = Key::F6; mapKeys[XK_F7] = Key::F7; mapKeys[XK_F8] = Key::F8;
  1102. mapKeys[XK_F9] = Key::F9; mapKeys[XK_F10] = Key::F10; mapKeys[XK_F11] = Key::F11; mapKeys[XK_F12] = Key::F12;
  1103. mapKeys[XK_Down] = Key::DOWN; mapKeys[XK_Left] = Key::LEFT; mapKeys[XK_Right] = Key::RIGHT; mapKeys[XK_Up] = Key::UP;
  1104. mapKeys[XK_KP_Enter] = Key::ENTER; mapKeys[XK_Return] = Key::ENTER;
  1105. mapKeys[XK_BackSpace] = Key::BACK; mapKeys[XK_Escape] = Key::ESCAPE; mapKeys[XK_Linefeed] = Key::ENTER; mapKeys[XK_Pause] = Key::PAUSE;
  1106. mapKeys[XK_Scroll_Lock] = Key::SCROLL; mapKeys[XK_Tab] = Key::TAB; mapKeys[XK_Delete] = Key::DEL; mapKeys[XK_Home] = Key::HOME;
  1107. mapKeys[XK_End] = Key::END; mapKeys[XK_Page_Up] = Key::PGUP; mapKeys[XK_Page_Down] = Key::PGDN; mapKeys[XK_Insert] = Key::INS;
  1108. mapKeys[XK_Shift_L] = Key::SHIFT; mapKeys[XK_Shift_R] = Key::SHIFT; mapKeys[XK_Control_L] = Key::CTRL; mapKeys[XK_Control_R] = Key::CTRL;
  1109. mapKeys[XK_space] = Key::SPACE;
  1110. mapKeys[XK_0] = Key::K0; mapKeys[XK_1] = Key::K1; mapKeys[XK_2] = Key::K2; mapKeys[XK_3] = Key::K3; mapKeys[XK_4] = Key::K4;
  1111. mapKeys[XK_5] = Key::K5; mapKeys[XK_6] = Key::K6; mapKeys[XK_7] = Key::K7; mapKeys[XK_8] = Key::K8; mapKeys[XK_9] = Key::K9;
  1112. mapKeys[XK_KP_0] = Key::NP0; mapKeys[XK_KP_1] = Key::NP1; mapKeys[XK_KP_2] = Key::NP2; mapKeys[XK_KP_3] = Key::NP3; mapKeys[XK_KP_4] = Key::NP4;
  1113. mapKeys[XK_KP_5] = Key::NP5; mapKeys[XK_KP_6] = Key::NP6; mapKeys[XK_KP_7] = Key::NP7; mapKeys[XK_KP_8] = Key::NP8; mapKeys[XK_KP_9] = Key::NP9;
  1114. mapKeys[XK_KP_Multiply] = Key::NP_MUL; mapKeys[XK_KP_Add] = Key::NP_ADD; mapKeys[XK_KP_Divide] = Key::NP_DIV; mapKeys[XK_KP_Subtract] = Key::NP_SUB; mapKeys[XK_KP_Decimal] = Key::NP_DECIMAL;
  1115. return olc_Display;
  1116. }
  1117. bool PixelGameEngine::olc_OpenGLCreate()
  1118. {
  1119. glDeviceContext = glXCreateContext(olc_Display, olc_VisualInfo, nullptr, GL_TRUE);
  1120. glXMakeCurrent(olc_Display, olc_Window, glDeviceContext);
  1121. XWindowAttributes gwa;
  1122. XGetWindowAttributes(olc_Display, olc_Window, &gwa);
  1123. glViewport(0, 0, gwa.width, gwa.height);
  1124. glSwapIntervalEXT = nullptr;
  1125. glSwapIntervalEXT = (glSwapInterval_t*)glXGetProcAddress((unsigned char*)"glXSwapIntervalEXT");
  1126. if (glSwapIntervalEXT)
  1127. glSwapIntervalEXT(olc_Display, olc_Window, 0);
  1128. else
  1129. {
  1130. printf("NOTE: Could not disable VSYNC, glXSwapIntervalEXT() was not found!\n");
  1131. printf(" Don't worry though, things will still work, it's just the\n");
  1132. printf(" frame rate will be capped to your monitors refresh rate - javidx9\n");
  1133. }
  1134. return true;
  1135. }
  1136. #endif
  1137. // Need a couple of statics as these are singleton instances
  1138. // read from multiple locations
  1139. std::atomic<bool> PixelGameEngine::bAtomActive{ false };
  1140. std::map<uint16_t, uint8_t> PixelGameEngine::mapKeys;
  1141. olc::PixelGameEngine* olc::PGEX::pge = nullptr;
  1142. #ifdef OLC_DBG_OVERDRAW
  1143. int olc::Sprite::nOverdrawCount = 0;
  1144. #endif
  1145. //=============================================================
  1146. }