Advertisement
Guest User

Untitled

a guest
May 27th, 2018
70
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 12.46 KB | None | 0 0
  1.  
  2. <html>
  3. <head>
  4. <meta charset="utf-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <title>Flat Warped World</title>
  7. </head>
  8. <style>
  9. html {
  10. background: #333;
  11. color: #eee;
  12. text-align: center;
  13. font-family: sans-serif;
  14. margin: 0;
  15. }
  16. ::-webkit-scrollbar {
  17. width: 0.6em;
  18. }
  19. ::-webkit-scrollbar-thumb {
  20. background: #888;
  21. background: linear-gradient(90deg, #888 0%, #aaa 80%, #888 100%);
  22. border-radius: 1em;
  23. }
  24. canvas {
  25. outline: #5af solid 1px;
  26. display: block;
  27. margin: 1em auto;
  28. width: 720px;
  29. max-width: 95%;
  30. }
  31. p {
  32. width: 80%;
  33. margin: 0.1em auto;
  34. }
  35. </style>
  36. <body>
  37. <h1>Flat Warped World Test</h1>
  38. <p>Ходим по 2D-поверхности в 3D-пространстве. Пока что в роли поверхности выставлена сфера. Разные цвета - это 6 точек на сфере. Возможность переключить поверхность и раскраску добавлю потом, наверное. Управление: WASD + QE.</p>
  39. <script>
  40. // Developed during:
  41. // 20.05.2018
  42. // 21.05.2018
  43. // 22.05.2018
  44. // 25.05.2018
  45.  
  46. const DIM = 3;
  47. const DIMLAST = DIM - 1;
  48.  
  49. // vector methods
  50.  
  51. function vecAll(fillWith, size) {
  52. return Array(size).fill(fillWith);
  53. }
  54.  
  55. function vecMix(arr, brr, f) {
  56. return Array.isArray(brr) ? Array.from(arr, (a, i) => f(a, brr[i])) : arr.map((a) => f(a, brr));
  57. }
  58.  
  59. function vecMul(arr, brr) {
  60. return vecMix(arr, brr, (a, b) => a * b);
  61. }
  62.  
  63. function vecAdd(arr, brr) {
  64. return vecMix(arr, brr, (a, b) => a + b);
  65. }
  66.  
  67. function vecSub(arr, brr) {
  68. return vecMix(arr, brr, (a, b) => a - b);
  69. }
  70.  
  71. function vecSum(arr) {
  72. return arr.reduce((x, y) => x + y);
  73. }
  74.  
  75. function vecDot(arr, brr) {
  76. return vecSum(vecMul(arr, brr));
  77. }
  78.  
  79. function vecSqr(arr) {
  80. return arr.reduce((x, y) => x + (y ** 2), 0);
  81. }
  82.  
  83. function vecDir(arr) {
  84. const dist = Math.sqrt(vecSqr(arr));
  85. return arr.map((x) => x / dist);
  86. }
  87.  
  88. // only for 3D
  89. function vecCross(arr, brr) {
  90. const result = Array(DIM);
  91. for (let i = 0; i < DIM; i++) {
  92. const j = (i + 1) % DIM;
  93. const k = (i + 2) % DIM;
  94. result[i] = arr[j] * brr[k] - brr[j] * arr[k];
  95. }
  96. return result;
  97. }
  98.  
  99.  
  100. /////////
  101.  
  102. const canv = document.createElement('canvas');
  103. const mini = document.createElement('canvas');
  104.  
  105. let scale = 4;
  106.  
  107. let w = 200;
  108. let h = 120;
  109. let w2 = w / 2;
  110. let h2 = h / 2;
  111. mini.width = w;
  112. mini.height = h;
  113. canv.width = w * scale;
  114. canv.height = h * scale;
  115. const bm = canv.getContext('2d');
  116. const bmm = mini.getContext('2d');
  117.  
  118. // params
  119.  
  120. /*
  121. AAA__BBB name means AAA-to-BBB ratio.
  122.  
  123. view: from center to corner of screen
  124. M: meter (1.0) in dimension
  125. R: angle in radians
  126. R360: angle in turns (360 degrees)
  127. rs: ray step
  128. rt: ray tick
  129. ms: move step
  130. mt: move tick
  131. ts: rotate step
  132. dot: pixel of bmm
  133. */
  134.  
  135. // screen params
  136. let ray__R360 = 32; // number of rays
  137. let rs__view = 32; // number of steps
  138.  
  139. // world params
  140. let M__view = 5.0;
  141.  
  142. // ray params
  143. let M__rt = 0.1; // ray travel quality
  144. let M__rs = M__view / rs__view;
  145. let rt__rs = Math.floor(M__rs / M__rt) + 1;
  146.  
  147. // move params
  148. let ms__view = rs__view;
  149. let M__mt = M__rt;
  150. let M__ms = M__view / ms__view;
  151. let mt__ms = Math.floor(M__ms / M__mt) + 1;
  152.  
  153. let attr__rt = 3;
  154.  
  155. // rotate params
  156. let R__ts = 0.15;
  157.  
  158. // screen params
  159. let dot__view = Math.hypot(w2, h2);
  160. let dot__rs = Math.floor(dot__view / rs__view) + 1;
  161.  
  162.  
  163.  
  164.  
  165. // surface data
  166.  
  167. let surfaces = {
  168. plane: {
  169. start: {
  170. x: [0, 0, 0],
  171. d: [0, 1, 0],
  172. },
  173. f: (x) => x[DIMLAST],
  174. df: (x) => [0, 0, 1],
  175. },
  176.  
  177. sphere: {
  178. start: {
  179. x: [0, 0, 1],
  180. d: [0, 1, 0],
  181. },
  182. f: (x) => vecSqr(x) - 1,
  183. df: (x) => vecMul(x, 2),
  184. },
  185.  
  186. innersphere: {
  187. start: {
  188. x: [0, 0, 1],
  189. d: [0, 1, 0],
  190. },
  191. f: (x) => 1 - vecSqr(x),
  192. df: (x) => vecMul(x, -2),
  193. },
  194.  
  195. paraboloid: {
  196. start: {
  197. x: [0, 0, 0],
  198. d: [0, 1, 0],
  199. },
  200. f: (x) => vecSum(x.map((a, i) => (i == DIMLAST ? a : -(a ** 2)))),
  201. df: (x) => x.map((a, i) => (i == DIMLAST ? 1 : -2 * a)),
  202. },
  203.  
  204. saddle: {
  205. start: {
  206. x: [0, 0, 0],
  207. d: [0, 1, 0],
  208. },
  209. f: (x) => (x[0] ** 2) - (x[1] ** 2) + x[2], // z = y^2 - x^2
  210. df: (x) => [2 * x[0], -2 * x[1], 1],
  211. },
  212. };
  213.  
  214. // colorings
  215.  
  216. function test01(x) {
  217. if (x < 0) return 0;
  218. if (x < 1) return x;
  219. return 1;
  220. }
  221.  
  222. function nearExp(x, y, k=1) {
  223. return Math.exp(-k * vecSqr(vecSub(x, y)));
  224. }
  225.  
  226. function nearDiv(x, y, k=1) {
  227. return 1 / (1 + k * vecSqr(vecSub(x, y)));
  228. }
  229.  
  230. function exp1(x) {
  231. if (x < 0) {
  232. return 0;
  233. }
  234. return Math.exp(-1 / ((x / Math.LN2) ** 2));
  235. }
  236.  
  237. let colorings = {
  238. border01: (x) => x.map(test01),
  239. near0: (x) => Array(DIM).fill(nearDiv(x, vecAll(0, DIM))),
  240. near110: (x) => [nearDiv(x, [1, 0, 0]), nearDiv(x, [0, 1, 0]), nearDiv(x, [0, 0, 0])],
  241. near111: (x) => [nearDiv(x, [1, 0, 0]), nearDiv(x, [0, 1, 0]), nearDiv(x, [0, 0, 1])],
  242. neare111: (x) => [nearExp(x, [1, 0, 0]), nearExp(x, [0, 1, 0]), nearExp(x, [0, 0, 1])],
  243. nan: (x) => x.map((t) => t != t),
  244. exp6: (x) => vecAdd(x.map((t) => 0.5 * exp1(t)), x.map((t) => 0.5 * exp1(-t))),
  245. tan3: (x) => x.map((t) => (Math.atan(t) / Math.PI + 0.5)),
  246. };
  247.  
  248. let dotmaps = {
  249. compass: [
  250. // 6 dots in 6 directions
  251. [[1, 0, 0], [1, 0, 0]],
  252. [[0, 1, 0], [0, 1, 0]],
  253. [[0, 0, 1], [0, 0, 1]],
  254.  
  255. [[-1, 0, 0], [0, 1, 1]],
  256. [[0, -1, 0], [1, 0, 1]],
  257. [[0, 0, -1], [1, 1, 0]],
  258.  
  259. // white center
  260. [[0, 0, 0], [1, 1, 1]],
  261. ],
  262. compassParaboloid: [
  263. [[1, 0, 1], [1, 0, 0]],
  264. [[0, 1, 1], [0, 1, 0]],
  265. [[-1, 0, 1], [0, 1, 1]],
  266. [[0, -1, 1], [1, 0, 1]],
  267.  
  268. [[2, 0, 4], [1, 0.8, 0]],
  269. [[0, 2, 4], [0.8, 1, 0]],
  270. [[-2, 0, 4], [0.8, 1, 1]],
  271. [[0, -2, 4], [1, 0.8, 1]],
  272.  
  273. [[0, 0, 0], [1, 1, 1]],
  274. ],
  275. compassSaddle: [
  276. [[1, 0, -1], [1, 0, 0]],
  277. [[0, 1, 1], [0, 1, 0]],
  278. [[-1, 0, -1], [0, 1, 1]],
  279. [[0, -1, 1], [1, 0, 1]],
  280.  
  281. [[2, 0, -4], [1, 0.8, 0]],
  282. [[0, 2, 4], [0.8, 1, 0]],
  283. [[-2, 0, -4], [0.8, 1, 1]],
  284. [[0, -2, 4], [1, 0.8, 1]],
  285.  
  286. [[0, 0, 0], [1, 1, 1]],
  287. ],
  288. }
  289.  
  290. function neardots(x) {
  291. const weighted = dotmap.map(([y, color]) => [nearExp(x, y, 2), color]);
  292. const summed = weighted.reduce((acc, [w, color]) => vecAdd(acc, vecMul(color, w)), vecAll(0, 3));
  293. const light = weighted.reduce((acc, [w, color]) => acc + w, 0);
  294. const k = 1 / Math.sqrt(1 + (light ** 2));
  295. return vecMul(summed, k);
  296. }
  297.  
  298. // user current position
  299. /*let view = {
  300. x: [0, 0, 1], // position vector
  301. // all dir vectors are normed
  302. d: [0, 1, 0], // forward direction vector
  303. r: [1, 0, 0], // right direction vector
  304. n: [0, 0, 1], // surface normal vector
  305. };*/
  306.  
  307. // utils
  308.  
  309. function ease(x) {
  310. return x// * x * (3 - 2 * x);
  311. }
  312.  
  313. // surface operations
  314.  
  315. function getAirAt(x) {
  316. return surface.f(x);
  317. }
  318.  
  319. function getDeltaAt(x) {
  320. return surface.df(x);
  321. }
  322.  
  323. // ray pos operations
  324.  
  325. // create a ray with specified angle
  326. function initRay(angle) {
  327. return {
  328. x: view.x,
  329. d: vecSub(vecMul(view.d, Math.cos(angle)), vecMul(view.r, Math.sin(angle))),
  330. }
  331. }
  332.  
  333. // move ray forward along the surface
  334. function updateRay(ray, move) {
  335. for (let i = 0; i < (move ? mt__ms : rt__rs); i++) {
  336. ray.x = vecAdd(ray.x, vecMul(ray.d, (move ? M__mt : M__rt)));
  337.  
  338. // attracting x to surface
  339. for (let j = 0; j < attr__rt; j++) {
  340. const air = getAirAt(ray.x);
  341. const delta = getDeltaAt(ray.x);
  342. const attr = vecSqr(delta);
  343.  
  344. ray.x = vecSub(ray.x, vecMul(delta, air / attr));
  345. }
  346.  
  347. // updating direction
  348. const norm = getDeltaAt(ray.x);
  349. const prod = vecSum(vecMul(norm, ray.d)); // dot prod
  350. const dist = vecSqr(norm);
  351. const proj = vecMul(norm, prod / dist); // ray.d projected on norm direction
  352. const flat = vecSub(ray.d, proj); // ray.d projected on surface environ
  353. ray.d = vecDir(flat);
  354.  
  355. if (move) {
  356. ray.n = vecDir(norm);
  357. }
  358. }
  359. }
  360.  
  361. // get color
  362. function getRayData(ray) {
  363. return coloring(ray.x, ray.d);
  364. }
  365.  
  366. // render logic
  367.  
  368. function computeSectorColors() {
  369. const sectors = [];
  370.  
  371. // compute each ray
  372. // (can be parallelized)
  373. for (let i = 0; i < ray__R360; i++) {
  374. const raydata = [];
  375. const angle = i * 2 * Math.PI / ray__R360;
  376. const ray = initRay(angle);
  377.  
  378. while (true) {
  379. raydata.push(getRayData(ray));
  380. if (raydata.length > rs__view) {
  381. break;
  382. }
  383. updateRay(ray);
  384. }
  385.  
  386. sectors.push(raydata);
  387. }
  388. return sectors;
  389. }
  390.  
  391. function drawSectors(sectors) {
  392. const dat = bmm.getImageData(0, 0, w, h);
  393. const d = dat.data;
  394.  
  395. const m = Array(4);
  396. const p = Array(4);
  397. let offset = 0;
  398.  
  399. // find a sector for each bmm's dot
  400. // and compute its color by linear radial interpolation
  401. // (can be parallelized)
  402. for (let j = 0; j < h; j++) {
  403. for (let i = 0; i < w; i++) {
  404. const x = i - w2;
  405. const y = j - h2;
  406.  
  407. // determining radius
  408. const rval = Math.hypot(x, y) / dot__rs;
  409. p[0] = Math.floor(rval);
  410. p[1] = p[0] + 1;
  411. m[0] = 1 - (rval - p[0]);
  412. m[1] = 1 - m[0];
  413.  
  414. // determining angle
  415. let aval = ray__R360 * (Math.atan2(x, y) / (2 * Math.PI) + 0.5);
  416. if (aval >= ray__R360) {
  417. aval = 0;
  418. }
  419. p[2] = Math.floor(aval);
  420. p[3] = (p[2] + 1) % ray__R360;
  421. m[2] = 1 - (aval - p[2]);
  422. m[3] = 1 - m[2];
  423.  
  424. // sum sector corner colors multipled by vicinity
  425. const sdat = [0, 0, 0, 1];
  426. for (let k = 0; k < 4; k++) {
  427. const ri = k & 1;
  428. const ai = (k >> 1) | 2;
  429. const color = sectors[p[ai]][p[ri]];
  430. const mul = m[ri] * m[ai];
  431.  
  432. for (let c = 0; c < 3; c++) {
  433. sdat[c] += color[c] * mul;
  434. }
  435. }
  436.  
  437. // set data
  438. for (let c = 0; c < 4; c++) {
  439. d[offset + c] = sdat[c] * 255;
  440. }
  441.  
  442. offset += 4;
  443. }
  444. }
  445.  
  446. bmm.putImageData(dat, 0, 0);
  447. bm.drawImage(mini, 0, 0, w * scale, h * scale);
  448. }
  449.  
  450. function drawAll() {
  451. let sectors = debugTime('sectors', computeSectorColors);
  452. debugTime('draw', drawSectors, sectors);
  453. }
  454.  
  455. // moving and rotating
  456.  
  457. function initView(surface) {
  458. const view = {};
  459. view.x = surface.start.x;
  460. view.d = surface.start.d;
  461. view.n = vecDir(getDeltaAt(view.x));
  462. view.r = vecCross(view.d, view.n);
  463. return view;
  464. }
  465.  
  466. function updateView(view, surface, inp) {
  467. // moving
  468. let angle = 0;
  469. if (inp.d || inp.r) {
  470. angle = Math.atan2(inp.r, inp.d);
  471. const ray = initRay(angle);
  472. updateRay(ray, surface, true);
  473. view.x = ray.x;
  474. view.d = ray.d;
  475. view.n = ray.n;
  476. view.r = vecCross(view.d, view.n);
  477. }
  478.  
  479. // rotating
  480. angle += inp.t * R__ts;
  481. const x = view.r;
  482. const y = view.d;
  483. const c = Math.cos(angle);
  484. const s = Math.sin(angle);
  485.  
  486. view.r = vecSub(vecMul(x, c), vecMul(y, s));
  487. view.d = vecAdd(vecMul(x, s), vecMul(y, c));
  488. }
  489.  
  490. // button controls
  491.  
  492. let keylist = {
  493. '65': false, // A
  494. '87': false, // W
  495. '68': false, // D
  496. '83': false, // S
  497.  
  498. '37': false, // left
  499. '38': false, // up
  500. '39': false, // right
  501. '40': false, // down
  502.  
  503. '69': false, // E: rotate clockwise
  504. '81': false, // Q: rotate counter-clockwise
  505.  
  506. //16: false, // Shift * smod
  507. //18: false, // Alt / smod
  508.  
  509. //187: false, // +
  510. //189: false, // -
  511.  
  512. //112: false, // F1
  513. //113: false, // F2
  514. //114: false, // F3
  515. }
  516.  
  517. function setKey(key, state) {
  518. key = String(key);
  519. if (keylist[key] == undefined) {
  520. return false;
  521. }
  522. keylist[key] = state;
  523. return true;
  524. }
  525.  
  526. function getInputs() {
  527. return {
  528. // along y
  529. d: (keylist['38'] || keylist['87']) - (keylist['40'] || keylist['83']),
  530. // along x
  531. r: (keylist['37'] || keylist['65']) - (keylist['39'] || keylist['68']),
  532. // rotation
  533. t: keylist['69'] - keylist['81'],
  534. };
  535. }
  536.  
  537. window.onkeydown = function (e) {
  538. if (setKey(e.keyCode, true)) {
  539. e.preventDefault();
  540. }
  541. }
  542. window.onkeyup = function (e) {
  543. if (setKey(e.keyCode, false)) {
  544. e.preventDefault();
  545. }
  546. }
  547. window.onblur = function(e) {
  548. for (let k in keylist) {
  549. keylist[k] = false;
  550. }
  551. }
  552.  
  553. // tick
  554.  
  555. function updateAll(view) {
  556. const inp = getInputs();
  557. if (inp.d || inp.r || inp.t) {
  558. debugTime('change', updateView, view, surface, inp);
  559. console.log(view);
  560. debugTime('redraw', drawAll);
  561. }
  562.  
  563. setTimeout(updateAll, tickDelay, view);
  564. }
  565.  
  566. // debug time
  567.  
  568. function debugTime(name, f, ...args) {
  569. console.time(name);
  570. const ret = f(...args);
  571. console.timeEnd(name);
  572. return ret;
  573. }
  574.  
  575. // params
  576.  
  577. //let params
  578. const tickDelay = 20;
  579. let surface = surfaces.sphere;
  580. //let coloring = colorings.tan3;
  581. let coloring = neardots;
  582. let dotmap = dotmaps.compass;
  583.  
  584. // init all
  585.  
  586. function initAll() {
  587. let view = initView(surface);
  588. window.view = view; // to be changed
  589. drawAll(view);
  590. updateAll(view);
  591. document.body.appendChild(canv);
  592. }
  593.  
  594. debugTime('initAll', initAll);
  595. </script>
  596. </body>
  597. </html>
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement