You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

преди 13 години
преди 13 години
преди 12 години
преди 12 години
преди 12 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841
  1. //
  2. // Lol Engine - Fractal tutorial
  3. //
  4. // Copyright: (c) 2011 Sam Hocevar <sam@hocevar.net>
  5. // This program is free software; you can redistribute it and/or
  6. // modify it under the terms of the Do What The Fuck You Want To
  7. // Public License, Version 2, as published by Sam Hocevar. See
  8. // http://sam.zoy.org/projects/COPYING.WTFPL for more details.
  9. //
  10. #if defined HAVE_CONFIG_H
  11. # include "config.h"
  12. #endif
  13. #include <cstring>
  14. #include <cmath>
  15. #include "core.h"
  16. #include "lolgl.h"
  17. #include "loldebug.h"
  18. using namespace std;
  19. using namespace lol;
  20. #if defined _WIN32 && defined USE_D3D9
  21. # define FAR
  22. # define NEAR
  23. # include <d3d9.h>
  24. #endif
  25. #if USE_SDL && defined __APPLE__
  26. # include <SDL_main.h>
  27. #endif
  28. #if defined _WIN32
  29. # undef main /* FIXME: still needed? */
  30. # include <direct.h>
  31. #endif
  32. #if defined USE_D3D9
  33. extern IDirect3DDevice9 *g_d3ddevice;
  34. #elif defined _XBOX
  35. extern D3DDevice *g_d3ddevice;
  36. #elif __CELLOS_LV2__
  37. static GLint const INTERNAL_FORMAT = GL_ARGB_SCE;
  38. static GLenum const TEXTURE_FORMAT = GL_BGRA;
  39. static GLenum const TEXTURE_TYPE = GL_UNSIGNED_INT_8_8_8_8_REV;
  40. #elif defined __native_client__
  41. static GLint const INTERNAL_FORMAT = GL_RGBA;
  42. static GLenum const TEXTURE_FORMAT = GL_RGBA;
  43. static GLenum const TEXTURE_TYPE = GL_UNSIGNED_BYTE;
  44. #else
  45. /* Seems efficient for little endian textures */
  46. static GLint const INTERNAL_FORMAT = GL_RGBA;
  47. static GLenum const TEXTURE_FORMAT = GL_BGRA;
  48. static GLenum const TEXTURE_TYPE = GL_UNSIGNED_INT_8_8_8_8_REV;
  49. #endif
  50. class Fractal : public WorldEntity
  51. {
  52. public:
  53. Fractal(ivec2 const &size)
  54. {
  55. /* Ensure texture size is a multiple of 16 for better aligned
  56. * data access. Store the dimensions of a texel for our shader,
  57. * as well as the half-size of the screen. */
  58. m_size = size;
  59. m_size.x = (m_size.x + 15) & ~15;
  60. m_size.y = (m_size.y + 15) & ~15;
  61. m_texel_settings = vec4(1.0, 1.0, 2.0, 2.0) / m_size.xyxy;
  62. m_screen_settings = vec4(1.0, 1.0, 0.5, 0.5) * m_size.xyxy;
  63. /* Window size decides the world aspect ratio. For instance, 640×480
  64. * will be mapped to (-0.66,-0.5) - (0.66,0.5). */
  65. #if !defined __native_client__
  66. m_window_size = Video::GetSize();
  67. #else
  68. /* FIXME: it's illegal to call this on the game thread! */
  69. m_window_size = ivec2(640, 480);
  70. #endif
  71. if (m_window_size.y < m_window_size.x)
  72. m_window2world = 0.5 / m_window_size.y;
  73. else
  74. m_window2world = 0.5 / m_window_size.x;
  75. m_texel2world = (vec2)m_window_size / m_size * m_window2world;
  76. m_oldmouse = ivec2(0, 0);
  77. m_pixels = new u8vec4[m_size.x * m_size.y];
  78. m_tmppixels = new u8vec4[m_size.x / 2 * m_size.y / 2];
  79. m_frame = -1;
  80. m_slices = 4;
  81. for (int i = 0; i < 4; i++)
  82. {
  83. m_deltashift[i] = 0.0;
  84. m_deltascale[i] = 1.0;
  85. m_dirty[i] = 2;
  86. }
  87. #if defined __CELLOS_LV2__ || defined _XBOX
  88. //m_center = f64cmplx(-.22815528839841, -1.11514249704382);
  89. //m_center = f64cmplx(0.001643721971153, 0.822467633298876);
  90. m_center = f64cmplx(-0.65823419062254, 0.50221777363480);
  91. m_zoom_speed = -0.000025;
  92. #else
  93. m_center = -0.75;
  94. m_zoom_speed = 0.0;
  95. #endif
  96. m_translate = 0;
  97. m_radius = 5.0;
  98. m_ready = false;
  99. m_drag = false;
  100. m_palette = new u8vec4[(MAX_ITERATIONS + 1) * PALETTE_STEP];
  101. for (int i = 0; i < (MAX_ITERATIONS + 1) * PALETTE_STEP; i++)
  102. {
  103. double f = (double)i / PALETTE_STEP;
  104. double r = 0.5 * sin(f * 0.27 + 2.0) + 0.5;
  105. double g = 0.5 * sin(f * 0.17 - 1.8) + 0.5;
  106. double b = 0.5 * sin(f * 0.21 - 2.6) + 0.5;
  107. if (f < 7.0)
  108. {
  109. f = f < 1.0 ? 0.0 : (f - 1.0) / 6.0;
  110. r *= f;
  111. g *= f;
  112. b *= f;
  113. }
  114. uint8_t red = r * 255.99f;
  115. uint8_t green = g * 255.99f;
  116. uint8_t blue = b * 255.99f;
  117. #if defined __CELLOS_LV2__ || defined _XBOX
  118. m_palette[i] = u8vec4(255, red, green, blue);
  119. #elif defined __native_client__
  120. m_palette[i] = u8vec4(red, green, blue, 255);
  121. #else
  122. m_palette[i] = u8vec4(blue, green, red, 255);
  123. #endif
  124. }
  125. #if !defined __native_client__
  126. m_centertext = new Text(NULL, "gfx/font/ascii.png");
  127. m_centertext->SetPos(ivec3(5, m_window_size.y - 15, 1));
  128. Ticker::Ref(m_centertext);
  129. m_mousetext = new Text(NULL, "gfx/font/ascii.png");
  130. m_mousetext->SetPos(ivec3(5, m_window_size.y - 29, 1));
  131. Ticker::Ref(m_mousetext);
  132. m_zoomtext = new Text(NULL, "gfx/font/ascii.png");
  133. m_zoomtext->SetPos(ivec3(5, m_window_size.y - 43, 1));
  134. Ticker::Ref(m_zoomtext);
  135. #endif
  136. position = ivec3(0, 0, 0);
  137. bbox[0] = position;
  138. bbox[1] = ivec3(m_window_size, 0);
  139. Input::TrackMouse(this);
  140. /* Spawn worker threads and wait for their readiness. */
  141. for (int i = 0; i < MAX_THREADS; i++)
  142. m_threads[i] = new Thread(DoWorkHelper, this);
  143. for (int i = 0; i < MAX_THREADS; i++)
  144. m_spawnqueue.Pop();
  145. }
  146. ~Fractal()
  147. {
  148. /* Signal worker threads for completion and wait for
  149. * them to quit. */
  150. for (int i = 0; i < MAX_THREADS; i++)
  151. m_jobqueue.Push(-1);
  152. for (int i = 0; i < MAX_THREADS; i++)
  153. m_donequeue.Pop();
  154. Input::UntrackMouse(this);
  155. #if !defined __native_client__
  156. Ticker::Unref(m_centertext);
  157. Ticker::Unref(m_mousetext);
  158. Ticker::Unref(m_zoomtext);
  159. #endif
  160. delete m_pixels;
  161. delete m_tmppixels;
  162. delete m_palette;
  163. }
  164. inline f64cmplx TexelToWorldOffset(vec2 texel)
  165. {
  166. double dx = (0.5 + texel.x - m_size.x / 2) * m_texel2world.x;
  167. double dy = (0.5 + m_size.y / 2 - texel.y) * m_texel2world.y;
  168. return m_radius * f64cmplx(dx, dy);
  169. }
  170. inline f64cmplx ScreenToWorldOffset(vec2 pixel)
  171. {
  172. /* No 0.5 offset here, because we want to be able to position the
  173. * mouse at (0,0) exactly. */
  174. double dx = pixel.x - m_window_size.x / 2;
  175. double dy = m_window_size.y / 2 - pixel.y;
  176. return m_radius * m_window2world * f64cmplx(dx, dy);
  177. }
  178. virtual void TickGame(float deltams)
  179. {
  180. WorldEntity::TickGame(deltams);
  181. int prev_frame = m_frame;
  182. m_frame = (m_frame + 1) % 4;
  183. f64cmplx worldmouse = m_center + ScreenToWorldOffset(mousepos);
  184. ivec3 buttons = Input::GetMouseButtons();
  185. #if !defined __CELLOS_LV2__ && !defined _XBOX
  186. if (buttons[1])
  187. {
  188. if (!m_drag)
  189. {
  190. m_oldmouse = mousepos;
  191. m_drag = true;
  192. }
  193. m_translate = ScreenToWorldOffset(m_oldmouse)
  194. - ScreenToWorldOffset(mousepos);
  195. /* XXX: the purpose of this hack is to avoid translating by
  196. * an exact number of pixels. If this were to happen, the step()
  197. * optimisation for i915 cards in our shader would behave
  198. * incorrectly because a quarter of the pixels in the image
  199. * would have tie rankings in the distance calculation. */
  200. m_translate *= 1023.0 / 1024.0;
  201. m_oldmouse = mousepos;
  202. }
  203. else
  204. {
  205. m_drag = false;
  206. if (m_translate != 0.0)
  207. {
  208. m_translate *= pow(2.0, -deltams * 0.005);
  209. if (m_translate.norm() / m_radius < 1e-4)
  210. m_translate = 0.0;
  211. }
  212. }
  213. if ((buttons[0] || buttons[2]) && mousepos.x != -1)
  214. {
  215. double zoom = buttons[0] ? -0.0005 : 0.0005;
  216. m_zoom_speed += deltams * zoom;
  217. if (m_zoom_speed / zoom > 5)
  218. m_zoom_speed = 5 * zoom;
  219. }
  220. else if (m_zoom_speed)
  221. {
  222. m_zoom_speed *= pow(2.0, -deltams * 0.005);
  223. if (abs(m_zoom_speed) < 1e-5 || m_drag)
  224. m_zoom_speed = 0.0;
  225. }
  226. #endif
  227. if (m_zoom_speed || m_translate != 0.0)
  228. {
  229. f64cmplx oldcenter = m_center;
  230. double oldradius = m_radius;
  231. double zoom = pow(2.0, deltams * m_zoom_speed);
  232. if (m_radius * zoom > 8.0)
  233. {
  234. m_zoom_speed *= -1.0;
  235. zoom = 8.0 / m_radius;
  236. }
  237. else if (m_radius * zoom < 1e-14)
  238. {
  239. m_zoom_speed *= -1.0;
  240. zoom = 1e-14 / m_radius;
  241. }
  242. m_radius *= zoom;
  243. #if !defined __CELLOS_LV2__ && !defined _XBOX
  244. m_center += m_translate;
  245. m_center = (m_center - worldmouse) * zoom + worldmouse;
  246. worldmouse = m_center + ScreenToWorldOffset(mousepos);
  247. #endif
  248. /* Store the transformation properties to go from m_frame - 1
  249. * to m_frame. */
  250. m_deltashift[prev_frame] = (m_center - oldcenter) / oldradius;
  251. m_deltashift[prev_frame].x /= m_size.x * m_texel2world.x;
  252. m_deltashift[prev_frame].y /= m_size.y * m_texel2world.y;
  253. m_deltascale[prev_frame] = m_radius / oldradius;
  254. m_dirty[0] = m_dirty[1] = m_dirty[2] = m_dirty[3] = 2;
  255. }
  256. else
  257. {
  258. /* If settings didn't change, set transformation from previous
  259. * frame to identity. */
  260. m_deltashift[prev_frame] = 0.0;
  261. m_deltascale[prev_frame] = 1.0;
  262. }
  263. /* Transformation from current frame to current frame is always
  264. * identity. */
  265. m_zoom_settings[m_frame][0] = 0.0f;
  266. m_zoom_settings[m_frame][1] = 0.0f;
  267. m_zoom_settings[m_frame][2] = 1.0f;
  268. /* Compute transformation from other frames to current frame */
  269. for (int i = 0; i < 3; i++)
  270. {
  271. int prev_index = (m_frame + 4 - i) % 4;
  272. int cur_index = (m_frame + 3 - i) % 4;
  273. m_zoom_settings[cur_index][0] = m_zoom_settings[prev_index][0] * m_deltascale[cur_index] + m_deltashift[cur_index].x;
  274. m_zoom_settings[cur_index][1] = m_zoom_settings[prev_index][1] * m_deltascale[cur_index] + m_deltashift[cur_index].y;
  275. m_zoom_settings[cur_index][2] = m_zoom_settings[prev_index][2] * m_deltascale[cur_index];
  276. }
  277. /* Precompute texture offset change instead of doing it in GLSL */
  278. for (int i = 0; i < 4; i++)
  279. {
  280. m_zoom_settings[i][0] += 0.5 * (1.0 - m_zoom_settings[i][2]);
  281. m_zoom_settings[i][1] -= 0.5 * (1.0 - m_zoom_settings[i][2]);
  282. }
  283. #if !defined __native_client__
  284. char buf[128];
  285. sprintf(buf, "center: %+16.14f%+16.14fi", m_center.x, m_center.y);
  286. m_centertext->SetText(buf);
  287. sprintf(buf, " mouse: %+16.14f%+16.14fi", worldmouse.x, worldmouse.y);
  288. m_mousetext->SetText(buf);
  289. sprintf(buf, " zoom: %g", 1.0 / m_radius);
  290. m_zoomtext->SetText(buf);
  291. #endif
  292. if (m_dirty[m_frame])
  293. {
  294. m_dirty[m_frame]--;
  295. for (int i = 0; i < m_size.y; i += MAX_LINES * 2)
  296. m_jobqueue.Push(i);
  297. }
  298. }
  299. static void *DoWorkHelper(void *data)
  300. {
  301. Fractal *that = (Fractal *)data;
  302. that->m_spawnqueue.Push(0);
  303. for ( ; ; )
  304. {
  305. int line = that->m_jobqueue.Pop();
  306. if (line == -1)
  307. break;
  308. that->DoWork(line);
  309. that->m_donequeue.Push(0);
  310. }
  311. that->m_donequeue.Push(0);
  312. return NULL;
  313. };
  314. void DoWork(int line)
  315. {
  316. double const maxsqlen = 1024;
  317. double const k1 = 1.0 / (1 << 10) / (std::log(maxsqlen) / std::log(2.0));
  318. int jmin = ((m_frame + 1) % 4) / 2 + line;
  319. int jmax = jmin + MAX_LINES * 2;
  320. if (jmax > m_size.y)
  321. jmax = m_size.y;
  322. u8vec4 *m_pixelstart = m_pixels
  323. + m_size.x * (m_size.y / 4 * m_frame + line / 4);
  324. for (int j = jmin; j < jmax; j += 2)
  325. for (int i = m_frame % 2; i < m_size.x; i += 2)
  326. {
  327. f64cmplx z0 = m_center + TexelToWorldOffset(ivec2(i, j));
  328. f64cmplx z1, z2, z3, r0 = z0;
  329. //f64cmplx r0(0.28693186889504513, 0.014286693904085048);
  330. //f64cmplx r0(0.001643721971153, 0.822467633298876);
  331. //f64cmplx r0(-1.207205434596, 0.315432814901);
  332. //f64cmplx r0(-0.79192956889854, -0.14632423080102);
  333. //f64cmplx r0(0.3245046418497685, 0.04855101129280834);
  334. int iter = MAX_ITERATIONS - 4;
  335. for (;;)
  336. {
  337. /* Unroll the loop: tests are more expensive to do at each
  338. * iteration than the few extra multiplications. */
  339. z1 = z0 * z0 + r0;
  340. z2 = z1 * z1 + r0;
  341. z3 = z2 * z2 + r0;
  342. z0 = z3 * z3 + r0;
  343. if (sqlen(z0) >= maxsqlen)
  344. break;
  345. iter -= 4;
  346. if (iter < 4)
  347. break;
  348. }
  349. if (iter)
  350. {
  351. double n = sqlen(z0);
  352. if (sqlen(z1) >= maxsqlen) { iter += 3; n = sqlen(z1); }
  353. else if (sqlen(z2) >= maxsqlen) { iter += 2; n = sqlen(z2); }
  354. else if (sqlen(z3) >= maxsqlen) { iter += 1; n = sqlen(z3); }
  355. if (n > maxsqlen * maxsqlen)
  356. n = maxsqlen * maxsqlen;
  357. /* Approximate log(sqrt(n))/log(sqrt(maxsqlen)) */
  358. double f = iter;
  359. union { double n; uint64_t x; } u = { n };
  360. double k = (u.x >> 42) - (((1 << 10) - 1) << 10);
  361. k *= k1;
  362. /* Approximate log2(k) in [1,2]. */
  363. f += (- 0.344847817623168308695977510213252644185 * k
  364. + 2.024664188044341212602376988171727038739) * k
  365. - 1.674876738008591047163498125918330313237;
  366. *m_pixelstart++ = m_palette[(int)(f * PALETTE_STEP)];
  367. }
  368. else
  369. {
  370. #if defined __CELLOS_LV2__ || defined _XBOX
  371. *m_pixelstart++ = u8vec4(255, 0, 0, 0);
  372. #else
  373. *m_pixelstart++ = u8vec4(0, 0, 0, 255);
  374. #endif
  375. }
  376. }
  377. }
  378. virtual void TickDraw(float deltams)
  379. {
  380. WorldEntity::TickDraw(deltams);
  381. static float const vertices[] =
  382. {
  383. 1.0f, 1.0f,
  384. -1.0f, 1.0f,
  385. -1.0f, -1.0f,
  386. -1.0f, -1.0f,
  387. 1.0f, -1.0f,
  388. 1.0f, 1.0f,
  389. };
  390. static float const texcoords[] =
  391. {
  392. 1.0f, 1.0f,
  393. 0.0f, 1.0f,
  394. 0.0f, 0.0f,
  395. 0.0f, 0.0f,
  396. 1.0f, 0.0f,
  397. 1.0f, 1.0f,
  398. };
  399. if (!m_ready)
  400. {
  401. #if !defined _XBOX && !defined USE_D3D9
  402. /* Create a texture of half the width and twice the height
  403. * so that we can upload four different subimages each frame. */
  404. glGenTextures(1, &m_texid);
  405. glBindTexture(GL_TEXTURE_2D, m_texid);
  406. glTexImage2D(GL_TEXTURE_2D, 0, INTERNAL_FORMAT,
  407. m_size.x / 2, m_size.y * 2, 0,
  408. TEXTURE_FORMAT, TEXTURE_TYPE, m_pixels);
  409. # if defined __CELLOS_LV2__
  410. /* We need this hint because by default the storage type is
  411. * GL_TEXTURE_SWIZZLED_GPU_SCE. */
  412. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_ALLOCATION_HINT_SCE,
  413. GL_TEXTURE_TILED_GPU_SCE);
  414. # endif
  415. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  416. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  417. #elif defined _XBOX
  418. /* By default the X360 will swizzle the texture. Ask for linear. */
  419. g_d3ddevice->CreateTexture(m_size.x / 2, m_size.y * 2, 1,
  420. D3DUSAGE_WRITEONLY, D3DFMT_LIN_A8R8G8B8,
  421. D3DPOOL_DEFAULT, &m_tex, NULL);
  422. #else
  423. g_d3ddevice->CreateTexture(m_size.x / 2, m_size.y * 2, 1,
  424. D3DUSAGE_DYNAMIC, D3DFMT_A8R8G8B8,
  425. D3DPOOL_SYSTEMMEM, &m_tex, NULL);
  426. #endif
  427. m_shader = Shader::Create(
  428. #if !defined __CELLOS_LV2__ && !defined _XBOX && !defined USE_D3D9
  429. # if !defined HAVE_GLES_2X
  430. "#version 120\n"
  431. # else
  432. "precision highp float;"
  433. # endif
  434. ""
  435. "uniform mat4 u_ZoomSettings;"
  436. "uniform vec4 u_TexelSize;"
  437. "uniform vec4 u_ScreenSize;"
  438. ""
  439. "attribute vec2 a_TexCoord;"
  440. "attribute vec2 a_Vertex;"
  441. ""
  442. "varying vec4 v_CenterX, v_CenterY, v_IndexX, v_IndexY;"
  443. ""
  444. "void main(void)"
  445. "{"
  446. " gl_Position = vec4(a_Vertex, 0.0, 1.0);"
  447. /* Center point in [-.5,.5], apply zoom and translation
  448. * transformation, and go back to texture coordinates
  449. * in [0,1]. That's the ideal point we would like to
  450. * compute the value for. Then add or remove half the
  451. * size of a texel: the distance from this new point to
  452. * the final point will be our error. */
  453. " vec4 offsets = vec4(0.5, -0.5, 0.015625, -0.015625);"
  454. " vec4 zoomscale = vec4(u_ZoomSettings[0][2],"
  455. " u_ZoomSettings[1][2],"
  456. " u_ZoomSettings[2][2],"
  457. " u_ZoomSettings[3][2]);"
  458. " vec4 zoomtx = vec4(u_ZoomSettings[0][0],"
  459. " u_ZoomSettings[1][0],"
  460. " u_ZoomSettings[2][0],"
  461. " u_ZoomSettings[3][0]);"
  462. " vec4 zoomty = vec4(u_ZoomSettings[0][1],"
  463. " u_ZoomSettings[1][1],"
  464. " u_ZoomSettings[2][1],"
  465. " u_ZoomSettings[3][1]);"
  466. " v_CenterX = zoomscale * a_TexCoord.x + zoomtx"
  467. " + offsets.xyxy * u_TexelSize.x;"
  468. " v_CenterY = zoomscale * a_TexCoord.y - zoomty"
  469. " + offsets.xyyx * u_TexelSize.y;"
  470. /* Precompute the multiple of one texel where our ideal
  471. * point lies. The fragment shader will call floor() on
  472. * this value. We add or remove a slight offset to avoid
  473. * rounding issues at the image's edges. */
  474. " v_IndexX = v_CenterX * u_ScreenSize.z - offsets.zwzw;"
  475. " v_IndexY = v_CenterY * u_ScreenSize.w - offsets.zwwz;"
  476. "}",
  477. # if !defined HAVE_GLES_2X
  478. "#version 120\n"
  479. # else
  480. "precision highp float;"
  481. # endif
  482. ""
  483. "uniform vec4 u_TexelSize;"
  484. "uniform sampler2D u_Texture;"
  485. ""
  486. "varying vec4 v_CenterX, v_CenterY, v_IndexX, v_IndexY;"
  487. ""
  488. "void main(void)"
  489. "{"
  490. " vec4 v05 = vec4(0.5, 0.5, 0.5, 0.5);"
  491. " vec4 rx, ry, t0, dx, dy, dd;"
  492. /* Get a pixel coordinate from each slice into rx & ry */
  493. " rx = u_TexelSize.x + u_TexelSize.z * floor(v_IndexX);"
  494. " ry = u_TexelSize.y + u_TexelSize.w * floor(v_IndexY);"
  495. /* Compute inverse distance to expected pixel in dd,
  496. * and put zero if we fall outside the texture. */
  497. " t0 = step(abs(rx - v05), v05) * step(abs(ry - v05), v05);"
  498. " dx = rx - v_CenterX;"
  499. " dy = ry - v_CenterY;"
  500. //" vec4 dd = t0 * (abs(dx) + abs(dy));"
  501. //" vec4 dd = t0 / (0.001 + sqrt((dx * dx) + (dy * dy)));"
  502. " dd = t0 / (0.000001 + (dx * dx) + (dy * dy));"
  503. /* Modify Y coordinate to select proper quarter. */
  504. " ry = ry * 0.25 + vec4(0.0, 0.25, 0.5, 0.75);"
  505. ""
  506. # if 1
  507. "\n#if 0\n" /* XXX: disabled until we can autodetect i915 */
  508. /* t1.x <-- dd.x > dd.y */
  509. /* t1.y <-- dd.z > dd.w */
  510. " vec2 t1 = step(dd.xz, dd.yw);"
  511. /* ret.x <-- max(rx.x, rx.y) wrt. t1.x */
  512. /* ret.y <-- max(rx.z, rx.w) wrt. t1.y */
  513. /* ret.z <-- max(ry.x, ry.y) wrt. t1.x */
  514. /* ret.w <-- max(ry.z, ry.w) wrt. t1.y */
  515. " vec4 ret = mix(vec4(rx.xz, ry.xz),"
  516. " vec4(rx.yw, ry.yw), t1.xyxy);"
  517. /* dd.x <-- max(dd.x, dd.y) */
  518. /* dd.z <-- max(dd.z, dd.w) */
  519. " dd.xy = mix(dd.xz, dd.yw, t1);"
  520. /* t2 <-- dd.x > dd.z */
  521. " float t2 = step(dd.x, dd.y);"
  522. /* ret.x <-- max(ret.x, ret.y); */
  523. /* ret.y <-- max(ret.z, ret.w); */
  524. " ret.xy = mix(ret.xz, ret.yw, t2);"
  525. "\n#else\n"
  526. /* Fallback for i915 cards -- the trick to reduce the
  527. * number of operations is to compute both step(a,b)
  528. * and step(b,a) and hope that their sum is 1. This is
  529. * almost always the case, and when it isn't we can
  530. * afford to have a few wrong pixels. However, a real
  531. * problem is when panning the image, because half the
  532. * screen is likely to flicker. To avoid this problem,
  533. * we cheat a little (see m_translate comment above). */
  534. " vec4 t1 = step(dd.xzyw, dd.ywxz);"
  535. " vec4 ret = vec4(rx.xz, ry.xz) * t1.zwzw"
  536. " + vec4(rx.yw, ry.yw) * t1.xyxy;"
  537. " dd.xy = dd.xz * t1.zw + dd.yw * t1.xy;"
  538. " vec2 t2 = step(dd.xy, dd.yx);"
  539. " ret.xy = ret.xz * t2.yy + ret.yw * t2.xx;"
  540. "\n#endif\n"
  541. /* Nearest neighbour */
  542. " gl_FragColor = texture2D(u_Texture, ret.xy);"
  543. # else
  544. /* Alternate version: some kind of linear interpolation */
  545. " vec4 p0 = texture2D(u_Texture, vec2(rx.x, ry.x));"
  546. " vec4 p1 = texture2D(u_Texture, vec2(rx.y, ry.y));"
  547. " vec4 p2 = texture2D(u_Texture, vec2(rx.z, ry.z));"
  548. " vec4 p3 = texture2D(u_Texture, vec2(rx.w, ry.w));"
  549. " gl_FragColor = 1.0 / (dd.x + dd.y + dd.z + dd.w)"
  550. " * (dd.x * p0 + dd.y * p1 + dd.z * p2 + dd.w * p3);"
  551. # endif
  552. "}"
  553. #else
  554. "void main(float2 a_Vertex : POSITION,"
  555. " float2 a_TexCoord : TEXCOORD0,"
  556. " uniform float4x4 u_ZoomSettings,"
  557. " uniform float4 u_TexelSize,"
  558. " uniform float4 u_ScreenSize,"
  559. " out float4 out_Position : POSITION0,"
  560. " out float4 v_CenterX : TEXCOORD0,"
  561. " out float4 v_CenterY : TEXCOORD1,"
  562. " out float4 v_IndexX : TEXCOORD2,"
  563. " out float4 v_IndexY : TEXCOORD3)"
  564. "{"
  565. " out_Position = float4(a_Vertex, 0.0, 1.0);"
  566. " float4 offsets = float4(0.5, -0.5, 0.015625, -0.015625);"
  567. " float4 zoomscale = float4(u_ZoomSettings[2][0],"
  568. " u_ZoomSettings[2][1],"
  569. " u_ZoomSettings[2][2],"
  570. " u_ZoomSettings[2][3]);"
  571. " float4 zoomtx = float4(u_ZoomSettings[0][0],"
  572. " u_ZoomSettings[0][1],"
  573. " u_ZoomSettings[0][2],"
  574. " u_ZoomSettings[0][3]);"
  575. " float4 zoomty = float4(u_ZoomSettings[1][0],"
  576. " u_ZoomSettings[1][1],"
  577. " u_ZoomSettings[1][2],"
  578. " u_ZoomSettings[1][3]);"
  579. " v_CenterX = zoomscale * a_TexCoord.x + zoomtx"
  580. " + offsets.xyxy * u_TexelSize.x;"
  581. " v_CenterY = zoomscale * a_TexCoord.y - zoomty"
  582. " + offsets.xyyx * u_TexelSize.y;"
  583. " v_IndexX = v_CenterX * u_ScreenSize.z - offsets.zwzw;"
  584. " v_IndexY = v_CenterY * u_ScreenSize.w - offsets.zwwz;"
  585. "}",
  586. "void main(in float4 v_CenterX : TEXCOORD0,"
  587. " in float4 v_CenterY : TEXCOORD1,"
  588. " in float4 v_IndexX : TEXCOORD2,"
  589. " in float4 v_IndexY : TEXCOORD3,"
  590. " uniform float4 u_TexelSize,"
  591. " uniform sampler2D u_Texture,"
  592. " out float4 out_FragColor : COLOR)"
  593. "{"
  594. " float4 v05 = float4(0.5, 0.5, 0.5, 0.5);"
  595. " float4 rx, ry, t0, dx, dy, dd;"
  596. " rx = u_TexelSize.x + u_TexelSize.z * floor(v_IndexX);"
  597. " ry = u_TexelSize.y + u_TexelSize.w * floor(v_IndexY);"
  598. " t0 = step(abs(rx - v05), v05) * step(abs(ry - v05), v05);"
  599. " dx = rx - v_CenterX;"
  600. " dy = ry - v_CenterY;"
  601. " dd = t0 / (0.000001 + (dx * dx) + (dy * dy));"
  602. " ry = ry * 0.25 + float4(0.0, 0.25, 0.5, 0.75);"
  603. " float2 t1 = step(dd.xz, dd.yw);"
  604. " float4 ret = lerp(float4(rx.xz, ry.xz),"
  605. " float4(rx.yw, ry.yw), t1.xyxy);"
  606. " dd.xy = lerp(dd.xz, dd.yw, t1);"
  607. " float t2 = step(dd.x, dd.y);"
  608. " ret.xy = lerp(ret.xz, ret.yw, t2);"
  609. " out_FragColor = tex2D(u_Texture, ret.xy);"
  610. "}"
  611. #endif
  612. );
  613. m_vertexattrib = m_shader->GetAttribLocation("a_Vertex", VertexUsage::Position, 0);
  614. m_texattrib = m_shader->GetAttribLocation("a_TexCoord", VertexUsage::TexCoord, 0);
  615. m_texeluni = m_shader->GetUniformLocation("u_TexelSize");
  616. m_screenuni = m_shader->GetUniformLocation("u_ScreenSize");
  617. m_zoomuni = m_shader->GetUniformLocation("u_ZoomSettings");
  618. m_vdecl =
  619. new VertexDeclaration(VertexStream<vec2>(VertexUsage::Position),
  620. VertexStream<vec2>(VertexUsage::TexCoord));
  621. m_vbo = new VertexBuffer(sizeof(vertices));
  622. m_tbo = new VertexBuffer(sizeof(texcoords));
  623. void *tmp = m_vbo->Lock(0, 0);
  624. memcpy(tmp, vertices, sizeof(vertices));
  625. m_vbo->Unlock();
  626. tmp = m_tbo->Lock(0, 0);
  627. memcpy(tmp, texcoords, sizeof(texcoords));
  628. m_tbo->Unlock();
  629. /* FIXME: this object never cleans up */
  630. m_ready = true;
  631. }
  632. #if defined _XBOX || defined USE_D3D9
  633. #else
  634. # if !defined HAVE_GLES_2X
  635. glEnable(GL_TEXTURE_2D);
  636. # endif
  637. glBindTexture(GL_TEXTURE_2D, m_texid);
  638. #endif
  639. if (m_dirty[m_frame])
  640. {
  641. for (int i = 0; i < m_size.y; i += MAX_LINES * 2)
  642. m_donequeue.Pop();
  643. m_dirty[m_frame]--;
  644. #if defined _XBOX || defined USE_D3D9
  645. D3DLOCKED_RECT rect;
  646. # if defined _XBOX
  647. m_tex->LockRect(0, &rect, NULL, D3DLOCK_NOOVERWRITE);
  648. # else
  649. m_tex->LockRect(0, &rect, NULL,
  650. D3DLOCK_DISCARD | D3DLOCK_NOOVERWRITE);
  651. # endif
  652. for (int j = 0; j < m_size.y * 2; j++)
  653. {
  654. u8vec4 *line = (u8vec4 *)rect.pBits + j * rect.Pitch / 4;
  655. for (int i = 0; i < m_size.x / 2; i++)
  656. line[i] = m_pixels[m_size.x / 2 * j + i];
  657. }
  658. m_tex->UnlockRect(0);
  659. #elif defined __CELLOS_LV2__
  660. /* glTexSubImage2D is extremely slow on the PS3, to the point
  661. * that uploading the whole texture is 40 times faster. */
  662. glTexImage2D(GL_TEXTURE_2D, 0, INTERNAL_FORMAT,
  663. m_size.x / 2, m_size.y * 2, 0,
  664. TEXTURE_FORMAT, TEXTURE_TYPE, m_pixels);
  665. #else
  666. glTexSubImage2D(GL_TEXTURE_2D, 0, 0, m_frame * m_size.y / 2,
  667. m_size.x / 2, m_size.y / 2,
  668. TEXTURE_FORMAT, TEXTURE_TYPE,
  669. m_pixels + m_size.x * m_size.y / 4 * m_frame);
  670. #endif
  671. }
  672. m_shader->Bind();
  673. m_shader->SetUniform(m_texeluni, m_texel_settings);
  674. m_shader->SetUniform(m_screenuni, m_screen_settings);
  675. m_shader->SetUniform(m_zoomuni, m_zoom_settings);
  676. m_vdecl->Bind();
  677. m_vdecl->SetStream(m_vbo, m_vertexattrib);
  678. m_vdecl->SetStream(m_tbo, m_texattrib);
  679. #if defined _XBOX || defined USE_D3D9
  680. g_d3ddevice->SetTexture(0, m_tex);
  681. g_d3ddevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CW);
  682. #elif !defined __CELLOS_LV2__ && !defined __ANDROID__
  683. #else
  684. //glEnableClientState(GL_VERTEX_ARRAY);
  685. //glVertexPointer(2, GL_FLOAT, 0, vertices);
  686. //glEnableClientState(GL_TEXTURE_COORD_ARRAY);
  687. //glTexCoordPointer(2, GL_FLOAT, 0, texcoords);
  688. #endif
  689. #if defined _XBOX || defined USE_D3D9
  690. g_d3ddevice->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 2);
  691. #else
  692. glDrawArrays(GL_TRIANGLES, 0, 6);
  693. #endif
  694. #if defined _XBOX || defined USE_D3D9
  695. m_vdecl->Unbind();
  696. #elif !defined __CELLOS_LV2__ && !defined __ANDROID__
  697. //glDisableVertexAttribArray(m_vertexattrib);
  698. //glDisableVertexAttribArray(m_texattrib);
  699. //glBindBuffer(GL_ARRAY_BUFFER, 0);
  700. #elif !defined __CELLOS_LV2__ && !defined __ANDROID__
  701. /* Never used for now */
  702. //glDisableVertexAttribArray(m_vertexattrib);
  703. //glDisableVertexAttribArray(m_texattrib);
  704. #else
  705. //glDisableClientState(GL_VERTEX_ARRAY);
  706. //glDisableClientState(GL_TEXTURE_COORD_ARRAY);
  707. #endif
  708. }
  709. private:
  710. static int const MAX_ITERATIONS = 340;
  711. static int const PALETTE_STEP = 32;
  712. static int const MAX_THREADS = 8;
  713. static int const MAX_LINES = 8;
  714. ivec2 m_size, m_window_size, m_oldmouse;
  715. double m_window2world;
  716. f64vec2 m_texel2world;
  717. u8vec4 *m_pixels, *m_tmppixels, *m_palette;
  718. Shader *m_shader;
  719. ShaderAttrib m_vertexattrib, m_texattrib;
  720. ShaderUniform m_texeluni, m_screenuni, m_zoomuni;
  721. VertexDeclaration *m_vdecl;
  722. VertexBuffer *m_vbo, *m_tbo;
  723. #if defined USE_D3D9
  724. IDirect3DTexture9 *m_tex;
  725. #elif defined _XBOX
  726. D3DTexture *m_tex;
  727. #else
  728. GLuint m_texid;
  729. #endif
  730. int m_frame, m_slices, m_dirty[4];
  731. bool m_ready, m_drag;
  732. f64cmplx m_center, m_translate;
  733. double m_zoom_speed, m_radius;
  734. vec4 m_texel_settings, m_screen_settings;
  735. mat4 m_zoom_settings;
  736. f64cmplx m_deltashift[4];
  737. double m_deltascale[4];
  738. /* Worker threads */
  739. Thread *m_threads[MAX_THREADS];
  740. Queue<int> m_spawnqueue, m_jobqueue, m_donequeue;
  741. /* Debug information */
  742. #if !defined __native_client__
  743. Text *m_centertext, *m_mousetext, *m_zoomtext;
  744. #endif
  745. };
  746. int main(int argc, char **argv)
  747. {
  748. Application app("Tutorial 3: Fractal", ivec2(640, 480), 60.0f);
  749. #if defined _MSC_VER && !defined _XBOX
  750. _chdir("..");
  751. #elif defined _WIN32 && !defined _XBOX
  752. _chdir("../..");
  753. #endif
  754. new DebugFps(5, 5);
  755. new Fractal(ivec2(640, 480));
  756. //new DebugRecord("fractalol.ogm", 60.0f);
  757. app.Run();
  758. return EXIT_SUCCESS;
  759. }