Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

demo.js 22KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964
  1. /**
  2. * @file MARTINS.js WebAR demo with WebGL
  3. * @author Alexandre Martins (https://github.com/alemart)
  4. * @license LGPL-3.0-or-later
  5. */
  6. /*
  7. WebGL low-level code:
  8. */
  9. class Entity
  10. {
  11. constructor(gl)
  12. {
  13. this._gl = gl;
  14. this._program = this._compile(gl, this.vertexShader, this.fragmentShader);
  15. this._uniformLocation = {
  16. 'time': gl.getUniformLocation(this._program, 'time'),
  17. 'resolution': gl.getUniformLocation(this._program, 'resolution'),
  18. 'projectionMatrix': gl.getUniformLocation(this._program, 'projectionMatrix'),
  19. 'modelViewMatrix': gl.getUniformLocation(this._program, 'modelViewMatrix'),
  20. };
  21. this.projectionMatrix = new Float32Array([1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]);
  22. this.modelViewMatrix = new Float32Array([1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]);
  23. }
  24. render(time = 0)
  25. {
  26. const gl = this._gl, u = this._uniformLocation;
  27. gl.useProgram(this._program);
  28. gl.uniform1f(u.time, time);
  29. gl.uniform2f(u.resolution, gl.canvas.width, gl.canvas.height);
  30. gl.uniformMatrix4fv(u.projectionMatrix, false, this.projectionMatrix);
  31. gl.uniformMatrix4fv(u.modelViewMatrix, false, this.modelViewMatrix);
  32. gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
  33. this._render(time);
  34. }
  35. _render(time)
  36. {
  37. const gl = this._gl;
  38. gl.drawArrays(gl.TRIANGLES, 0, 3 * this.numberOfTriangles);
  39. }
  40. get numberOfTriangles()
  41. {
  42. throw new Error();
  43. }
  44. get vertexShader()
  45. {
  46. throw new Error();
  47. }
  48. get fragmentShader()
  49. {
  50. throw new Error();
  51. }
  52. get _includeShaderUtils()
  53. {
  54. return `
  55. mat4 translate(vec3 offset)
  56. {
  57. float x = offset.x, y = offset.y, z = offset.z;
  58. return mat4(
  59. 1, 0, 0, 0,
  60. 0, 1, 0, 0,
  61. 0, 0, 1, 0,
  62. x, y, z, 1
  63. );
  64. }
  65. mat4 scale(vec3 factor)
  66. {
  67. float x = factor.x, y = factor.y, z = factor.z;
  68. return mat4(
  69. x, 0, 0, 0,
  70. 0, y, 0, 0,
  71. 0, 0, z, 0,
  72. 0, 0, 0, 1
  73. );
  74. }
  75. mat4 rotateX(float rad)
  76. {
  77. float s = sin(rad), c = cos(rad);
  78. return mat4(
  79. 1, 0, 0, 0,
  80. 0, c, s, 0,
  81. 0,-s, c, 0,
  82. 0, 0, 0, 1
  83. );
  84. }
  85. mat4 rotateY(float rad)
  86. {
  87. float s = sin(rad), c = cos(rad);
  88. return mat4(
  89. c, 0,-s, 0,
  90. 0, 1, 0, 0,
  91. s, 0, c, 0,
  92. 0, 0, 0, 1
  93. );
  94. }
  95. mat4 rotateZ(float rad)
  96. {
  97. float s = sin(rad), c = cos(rad);
  98. return mat4(
  99. c, s, 0, 0,
  100. -s, c, 0, 0,
  101. 0, 0, 1, 0,
  102. 0, 0, 0, 1
  103. );
  104. }
  105. mat4 rotate(vec3 euler)
  106. {
  107. return rotateZ(euler.z) * rotateY(euler.y) * rotateX(euler.x);
  108. }
  109. `;
  110. }
  111. get _includeVertexShaderUtils()
  112. {
  113. return this._includeShaderUtils + this._transform;
  114. }
  115. get _includeFragmentShaderUtils()
  116. {
  117. return this._includeShaderUtils + this._colorize;
  118. }
  119. get _transform()
  120. {
  121. return `
  122. vec4 transform(vec4 v)
  123. {
  124. return v;
  125. }
  126. `;
  127. }
  128. get _colorize()
  129. {
  130. return `
  131. vec4 colorize(vec4 p)
  132. {
  133. return p;
  134. }
  135. `;
  136. }
  137. _createShader(gl, shaderType, shaderSource)
  138. {
  139. const shader = gl.createShader(shaderType);
  140. gl.shaderSource(shader, shaderSource);
  141. gl.compileShader(shader);
  142. if(!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
  143. const message = gl.getShaderInfoLog(shader);
  144. gl.deleteShader(shader);
  145. throw new Error(message);
  146. }
  147. return shader;
  148. }
  149. _createProgram(gl, vs, fs)
  150. {
  151. const program = gl.createProgram();
  152. gl.attachShader(program, vs);
  153. gl.attachShader(program, fs);
  154. gl.linkProgram(program);
  155. if(!gl.getProgramParameter(program, gl.LINK_STATUS)) {
  156. const message = gl.getProgramInfoLog(program);
  157. gl.deleteProgram(program);
  158. throw new Error(message);
  159. }
  160. return program;
  161. }
  162. _compile(gl, vsSource, fsSource)
  163. {
  164. const vs = this._createShader(gl, gl.VERTEX_SHADER, vsSource);
  165. const fs = this._createShader(gl, gl.FRAGMENT_SHADER, fsSource);
  166. const program = this._createProgram(gl, vs, fs);
  167. return program;
  168. }
  169. }
  170. class Pyramid extends Entity
  171. {
  172. get numberOfTriangles()
  173. {
  174. return 4;
  175. }
  176. get vertexShader()
  177. {
  178. return `#version 300 es
  179. uniform mat4 projectionMatrix;
  180. uniform mat4 modelViewMatrix;
  181. uniform float time;
  182. uniform vec2 resolution;
  183. flat out int triangleID;
  184. out vec2 control;
  185. const float VERTEX[] = float[](
  186. 0.5, -0.25, 0.25,
  187. 0.0, 0.25, 0.0,
  188. -0.5, -0.25, 0.25,
  189. -0.5, -0.25, 0.25,
  190. 0.0, 0.25, 0.0,
  191. 0.0, -0.25, -0.5,
  192. 0.0, -0.25, -0.5,
  193. 0.0, 0.25, 0.0,
  194. 0.5, -0.25, 0.25,
  195. 0.0, -0.25, -0.5,
  196. 0.5, -0.25, 0.25,
  197. -0.5, -0.25, 0.25
  198. );
  199. const vec3 CENTROID = vec3(
  200. 0.0, -0.125, 0.0
  201. );
  202. const vec2 UV_CONTROL_POINT[] = vec2[](
  203. vec2(0.0, 1.0),
  204. vec2(1.0, -1.0),
  205. vec2(-1.0, -1.0)
  206. );
  207. ${this._includeVertexShaderUtils}
  208. void main()
  209. {
  210. int base = gl_VertexID * 3;
  211. vec4 vertex = vec4(VERTEX[base], VERTEX[base+1], VERTEX[base+2], 1.0);
  212. gl_Position = projectionMatrix * modelViewMatrix * transform(vertex);
  213. triangleID = gl_VertexID / 3;
  214. control = UV_CONTROL_POINT[gl_VertexID % 3];
  215. }
  216. `;
  217. }
  218. get fragmentShader()
  219. {
  220. return `#version 300 es
  221. precision mediump float;
  222. flat in int triangleID;
  223. out vec4 color;
  224. const vec3 COLOR[] = vec3[](
  225. vec3(0,1,1),
  226. vec3(1,1,0),
  227. vec3(1,0,1),
  228. vec3(0,1,0)
  229. );
  230. ${this._includeFragmentShaderUtils}
  231. void main()
  232. {
  233. vec4 pixel = vec4(COLOR[triangleID], 1.0);
  234. color = colorize(pixel);
  235. }
  236. `;
  237. }
  238. get _colorize()
  239. {
  240. // create black borders
  241. return `
  242. const float THICKNESS = 0.05;
  243. in vec2 control;
  244. vec4 colorize(vec4 p)
  245. {
  246. const float SQRT5 = float(${Math.sqrt(5.0)});
  247. float x = control.x, y = control.y;
  248. float d = abs(y + 1.0);
  249. d = min(d, abs(2.0 * x - y + 1.0) / SQRT5);
  250. d = min(d, abs(-2.0 * x - y + 1.0) / SQRT5);
  251. p.rgb *= step(THICKNESS, d);
  252. return p;
  253. }
  254. `;
  255. }
  256. }
  257. class AnimatedPyramid extends Pyramid
  258. {
  259. get _transform()
  260. {
  261. // rotate around the centroid
  262. return `
  263. const float CYCLE_DURATION = 3.0;
  264. const vec3 POSITION = vec3(0.0, 0.0, 0.5);
  265. const vec3 SCALE = vec3(1.5);
  266. vec4 transform(vec4 v)
  267. {
  268. const float PI = float(${Math.PI});
  269. float rad = -(2.0 * PI / CYCLE_DURATION) * time;
  270. v = translate(-CENTROID) * v;
  271. v = rotate(vec3(rad, rad, 0.0)) * v;
  272. v = translate(CENTROID) * v;
  273. v = scale(SCALE) * v;
  274. v = translate(POSITION) * v;
  275. return v;
  276. }
  277. `;
  278. }
  279. }
  280. class Cube extends Entity
  281. {
  282. get numberOfTriangles()
  283. {
  284. return 12; // 6 faces * 2 triangles per face
  285. }
  286. get vertexShader()
  287. {
  288. return `#version 300 es
  289. uniform mat4 projectionMatrix;
  290. uniform mat4 modelViewMatrix;
  291. uniform float time;
  292. uniform vec2 resolution;
  293. flat out int faceID;
  294. out vec2 control;
  295. const float VERTEX[] = float[](
  296. 1.0, 1.0, 1.0,
  297. -1.0, 1.0, 1.0,
  298. -1.0, -1.0, 1.0,
  299. -1.0, -1.0, 1.0,
  300. 1.0, -1.0, 1.0,
  301. 1.0, 1.0, 1.0,
  302. 1.0, 1.0, -1.0,
  303. -1.0, 1.0, -1.0,
  304. -1.0, -1.0, -1.0,
  305. -1.0, -1.0, -1.0,
  306. 1.0, -1.0, -1.0,
  307. 1.0, 1.0, -1.0,
  308. 1.0, 1.0, 1.0,
  309. -1.0, 1.0, 1.0,
  310. -1.0, 1.0, -1.0,
  311. -1.0, 1.0, -1.0,
  312. 1.0, 1.0, -1.0,
  313. 1.0, 1.0, 1.0,
  314. 1.0, -1.0, 1.0,
  315. -1.0, -1.0, 1.0,
  316. -1.0, -1.0, -1.0,
  317. -1.0, -1.0, -1.0,
  318. 1.0, -1.0, -1.0,
  319. 1.0, -1.0, 1.0,
  320. 1.0, 1.0, 1.0,
  321. 1.0, -1.0, 1.0,
  322. 1.0, -1.0, -1.0,
  323. 1.0, -1.0, -1.0,
  324. 1.0, 1.0, -1.0,
  325. 1.0, 1.0, 1.0,
  326. -1.0, 1.0, 1.0,
  327. -1.0, -1.0, 1.0,
  328. -1.0, -1.0, -1.0,
  329. -1.0, -1.0, -1.0,
  330. -1.0, 1.0, -1.0,
  331. -1.0, 1.0, 1.0
  332. );
  333. const vec3 CENTROID = vec3(
  334. 0.0, 0.0, 0.0
  335. );
  336. const vec2 UV_CONTROL_POINT[] = vec2[](
  337. vec2(1.0, 1.0),
  338. vec2(0.0, 1.0),
  339. vec2(0.0, 0.0),
  340. vec2(0.0, 0.0),
  341. vec2(1.0, 0.0),
  342. vec2(1.0, 1.0)
  343. );
  344. ${this._includeVertexShaderUtils}
  345. void main()
  346. {
  347. int base = gl_VertexID * 3;
  348. vec4 vertex = vec4(VERTEX[base], VERTEX[base+1], VERTEX[base+2], 1.0);
  349. gl_Position = projectionMatrix * modelViewMatrix * transform(vertex);
  350. faceID = gl_VertexID / 6;
  351. control = UV_CONTROL_POINT[gl_VertexID % 6];
  352. }
  353. `;
  354. }
  355. get fragmentShader()
  356. {
  357. return `#version 300 es
  358. precision mediump float;
  359. flat in int faceID;
  360. out vec4 color;
  361. const vec3 COLOR[] = vec3[](
  362. vec3(1,0,1),
  363. vec3(1,1,0),
  364. vec3(0,1,1),
  365. vec3(1,0,0),
  366. vec3(0,0,1),
  367. vec3(0,1,0)
  368. );
  369. ${this._includeFragmentShaderUtils}
  370. void main()
  371. {
  372. vec4 pixel = vec4(COLOR[faceID], 1.0);
  373. color = colorize(pixel);
  374. }
  375. `;
  376. }
  377. get _colorize()
  378. {
  379. // create black borders
  380. return `
  381. const float THICKNESS = 0.03;
  382. in vec2 control;
  383. vec4 colorize(vec4 p)
  384. {
  385. float x = control.x, y = control.y;
  386. vec4 d4 = abs(vec4(x, y, x - 1.0, y - 1.0));
  387. vec2 d2 = min(d4.xy, d4.zw);
  388. float d = min(d2.x, d2.y);
  389. p.rgb *= step(THICKNESS, d);
  390. return p;
  391. }
  392. `;
  393. }
  394. }
  395. class AnimatedCube extends Cube
  396. {
  397. get _transform()
  398. {
  399. // rotate around the centroid
  400. return `
  401. const float CYCLE_DURATION = 3.0;
  402. const vec3 POSITION = vec3(-0.5, -1.5, 2.0);
  403. const vec3 SCALE = vec3(0.5);
  404. vec4 transform(vec4 v)
  405. {
  406. const float PI = float(${Math.PI});
  407. float rad = -(2.0 * PI / CYCLE_DURATION) * time;
  408. v = translate(-CENTROID) * v;
  409. v = rotateY(rad) * v;
  410. v = translate(CENTROID) * v;
  411. v = scale(SCALE) * v;
  412. v = translate(POSITION) * v;
  413. return v;
  414. }
  415. `;
  416. }
  417. }
  418. class Quad extends Entity
  419. {
  420. get numberOfTriangles()
  421. {
  422. return 2;
  423. }
  424. get vertexShader()
  425. {
  426. return `#version 300 es
  427. uniform mat4 projectionMatrix;
  428. uniform mat4 modelViewMatrix;
  429. uniform float time;
  430. uniform vec2 resolution;
  431. out vec2 texCoord;
  432. const float UV[] = float[](
  433. 0.0, 0.0,
  434. 1.0, 0.0,
  435. 0.0, 1.0,
  436. 0.0, 1.0,
  437. 1.0, 0.0,
  438. 1.0, 1.0
  439. );
  440. const float QUAD[] = float[](
  441. -1.0,-1.0,
  442. 1.0,-1.0,
  443. -1.0, 1.0,
  444. -1.0, 1.0,
  445. 1.0,-1.0,
  446. 1.0, 1.0
  447. );
  448. ${this._includeVertexShaderUtils}
  449. ${this._rectify}
  450. ${this._rectifyUV}
  451. void main()
  452. {
  453. int base = gl_VertexID * 2;
  454. vec4 vertex = vec4(QUAD[base], QUAD[base+1], 0.0, 1.0);
  455. vec4 v = rectify(vertex);
  456. gl_Position = projectionMatrix * modelViewMatrix * transform(v);
  457. vec2 uv = vec2(UV[base], UV[base+1]) * vec2(1,-1);
  458. texCoord = rectifyUV(uv);
  459. }
  460. `;
  461. }
  462. get _rectify()
  463. {
  464. return `
  465. vec4 rectify(vec4 v)
  466. {
  467. return v;
  468. }
  469. `;
  470. }
  471. get _rectifyUV()
  472. {
  473. return `
  474. vec2 rectifyUV(vec2 v)
  475. {
  476. return v;
  477. }
  478. `;
  479. }
  480. }
  481. class ImageQuad extends Quad
  482. {
  483. constructor(gl)
  484. {
  485. super(gl);
  486. this._uniformLocation['image'] = gl.getUniformLocation(this._program, 'image');
  487. this._texture = gl.createTexture();
  488. this._uploaded = false;
  489. this._image = null;
  490. if(this._imageURL != '') {
  491. this._image = new Image();
  492. this._image.onload = () => this.upload(this._image);
  493. this._image.src = this._imageURL;
  494. }
  495. }
  496. upload(data)
  497. {
  498. const gl = this._gl;
  499. gl.bindTexture(gl.TEXTURE_2D, this._texture);
  500. gl.texImage2D(gl.TEXTURE_2D,
  501. 0, // mipmap level
  502. gl.RGBA, // internal format
  503. gl.RGBA, // data format
  504. gl.UNSIGNED_BYTE, // data type
  505. data // data
  506. );
  507. gl.generateMipmap(gl.TEXTURE_2D);
  508. gl.bindTexture(gl.TEXTURE_2D, null);
  509. this._uploaded = true;
  510. }
  511. _render(time)
  512. {
  513. const gl = this._gl;
  514. if(!this._uploaded)
  515. return;
  516. gl.bindTexture(gl.TEXTURE_2D, this._texture);
  517. gl.activeTexture(gl.TEXTURE0 + 0);
  518. gl.uniform1i(this._uniformLocation['image'], 0);
  519. super._render(time);
  520. gl.bindTexture(gl.TEXTURE_2D, null);
  521. }
  522. get _imageURL()
  523. {
  524. return '';
  525. }
  526. get _rectify()
  527. {
  528. return `
  529. uniform sampler2D image;
  530. vec4 rectify(vec4 v)
  531. {
  532. ivec2 size = textureSize(image, 0);
  533. float imageAspect = size.y > 0 ? float(size.x) / float(size.y) : 1.0;
  534. vec4 u = imageAspect > 1.0 ?
  535. vec4(1.0, 1.0 / imageAspect, 1.0, 1.0) :
  536. vec4(imageAspect, 1.0, 1.0, 1.0);
  537. return u * v;
  538. }
  539. `;
  540. }
  541. get fragmentShader()
  542. {
  543. return `#version 300 es
  544. precision mediump float;
  545. uniform sampler2D image;
  546. in vec2 texCoord;
  547. out vec4 color;
  548. ${this._includeFragmentShaderUtils}
  549. void main()
  550. {
  551. vec4 pixel = texture(image, texCoord);
  552. color = colorize(pixel);
  553. }
  554. `;
  555. }
  556. }
  557. class Sprite extends ImageQuad
  558. {
  559. get _numberOfFrames()
  560. {
  561. return 1;
  562. }
  563. get _framesPerSecond()
  564. {
  565. return 8;
  566. }
  567. get _initialFrame()
  568. {
  569. return 0;
  570. }
  571. get _isHorizontalSpritesheet()
  572. {
  573. return true;
  574. }
  575. get _rectify()
  576. {
  577. return `
  578. uniform sampler2D image;
  579. vec4 rectify(vec4 v)
  580. {
  581. const int NUMBER_OF_FRAMES = int(${this._numberOfFrames});
  582. const bool HORIZONTAL = bool(${this._isHorizontalSpritesheet});
  583. ivec2 size = textureSize(image, 0);
  584. size /= HORIZONTAL ? ivec2(NUMBER_OF_FRAMES, 1) : ivec2(1, NUMBER_OF_FRAMES);
  585. float imageAspect = size.y > 0 ? float(size.x) / float(size.y) : 1.0;
  586. vec4 u = imageAspect > 1.0 ?
  587. vec4(1.0, 1.0 / imageAspect, 1.0, 1.0) :
  588. vec4(imageAspect, 1.0, 1.0, 1.0);
  589. return u * v;
  590. }
  591. `;
  592. }
  593. get _rectifyUV()
  594. {
  595. return `
  596. vec2 rectifyUV(vec2 v)
  597. {
  598. const bool HORIZONTAL = bool(${this._isHorizontalSpritesheet});
  599. const float NUMBER_OF_FRAMES = float(${this._numberOfFrames});
  600. const float INITIAL_FRAME = float(${this._initialFrame});
  601. const float FPS = float(${this._framesPerSecond});
  602. float frame = mod(time * FPS + INITIAL_FRAME, NUMBER_OF_FRAMES);
  603. float base = floor(frame) / NUMBER_OF_FRAMES;
  604. vec2 offset = v / NUMBER_OF_FRAMES;
  605. return HORIZONTAL ?
  606. vec2(base + offset.x, v.y) :
  607. vec2(v.x, base + offset.y);
  608. }
  609. `;
  610. }
  611. }
  612. class ItWorks extends ImageQuad
  613. {
  614. constructor(gl)
  615. {
  616. super(gl);
  617. this._image = document.getElementById('it-works');
  618. this.upload(this._image);
  619. }
  620. get _transform()
  621. {
  622. return `
  623. const vec3 POSITION = vec3(0.0, 1.0, 0.0);
  624. const vec3 SCALE = vec3(2.25);
  625. vec4 transform(vec4 v)
  626. {
  627. return translate(POSITION) * scale(SCALE) * v;
  628. }
  629. `;
  630. }
  631. get _colorize()
  632. {
  633. return `
  634. const vec4 COLOR = vec4(173, 255, 47, 255) / 255.0;
  635. vec4 colorize(vec4 p)
  636. {
  637. return p * COLOR;
  638. }
  639. `;
  640. }
  641. }
  642. class WingMan extends Sprite
  643. {
  644. constructor(gl)
  645. {
  646. WingMan._count = WingMan._count || 0;
  647. ++WingMan._count;
  648. super(gl);
  649. this._image = document.getElementById('wing-man');
  650. this.upload(this._image);
  651. }
  652. get _numberOfFrames()
  653. {
  654. return 8;
  655. }
  656. get _framesPerSecond()
  657. {
  658. return 20;
  659. }
  660. get _initialFrame()
  661. {
  662. const n = 2;
  663. return (WingMan._count % n) * Math.floor(this._numberOfFrames / n);
  664. }
  665. get _transform()
  666. {
  667. return `
  668. const float PI = float(${Math.PI});
  669. const float INITIAL_PHASE = PI * float(${WingMan._count});
  670. const vec3 INITIAL_POSITION = vec3(1.25 * cos(INITIAL_PHASE), -0.25, 2.0);
  671. const vec3 SCALE = vec3(0.7);
  672. vec4 transform(vec4 v)
  673. {
  674. float z = 0.6 * cos(INITIAL_PHASE);
  675. float y = 0.1 * cos(2.0 * PI * time + INITIAL_PHASE);
  676. vec3 position = vec3(0.0, y, z) + INITIAL_POSITION;
  677. return translate(position) * scale(SCALE) * v;
  678. }
  679. `;
  680. }
  681. }
  682. /*
  683. MARTINS.js + WebGL code:
  684. */
  685. window.addEventListener('load', async function() {
  686. try {
  687. const session = await startARSession();
  688. const gl = initGL(session.viewport.canvas);
  689. const scene = [
  690. new AnimatedPyramid(gl),
  691. new AnimatedCube(gl),
  692. new ItWorks(gl),
  693. new WingMan(gl),
  694. new WingMan(gl),
  695. ];
  696. function initGL(canvas)
  697. {
  698. const gl = canvas.getContext('webgl2', {
  699. alpha: true
  700. });
  701. if(!gl)
  702. throw new Error(`Can't create WebGL2 context`);
  703. gl.enable(gl.DEPTH_TEST);
  704. gl.enable(gl.BLEND);
  705. gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
  706. return gl;
  707. }
  708. function clear()
  709. {
  710. gl.clearColor(0, 0, 0, 0);
  711. gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
  712. }
  713. function render(elapsedTimeInSeconds, projectionMatrix, modelViewMatrix)
  714. {
  715. for(const entity of scene) {
  716. entity.projectionMatrix.set(projectionMatrix);
  717. entity.modelViewMatrix.set(modelViewMatrix);
  718. entity.render(elapsedTimeInSeconds);
  719. }
  720. }
  721. function animate(time, frame)
  722. {
  723. clear();
  724. for(const result of frame.results) {
  725. if('image-tracker' == result.tracker.type) {
  726. if(result.trackables.length > 0) {
  727. const trackable = result.trackables[0];
  728. const projectionMatrix = result.viewer.view.projectionMatrix;
  729. const modelViewMatrix = result.viewer.convertToViewerSpace(trackable.pose).transform.matrix;
  730. render(frame.session.time.elapsed, projectionMatrix.read(), modelViewMatrix.read());
  731. }
  732. }
  733. }
  734. session.requestAnimationFrame(animate);
  735. }
  736. session.requestAnimationFrame(animate);
  737. }
  738. catch(error) {
  739. alert(error.message);
  740. }
  741. async function startARSession()
  742. {
  743. if(!Martins.isSupported()) {
  744. throw new Error(
  745. 'This device is not compatible with AR.\n\n' +
  746. 'User agent: ' + navigator.userAgent
  747. );
  748. }
  749. //Martins.Settings.powerPreference = 'low-power';
  750. const tracker = Martins.Tracker.ImageTracker();
  751. await tracker.database.add([{
  752. name: 'my-reference-image',
  753. image: document.getElementById('my-reference-image')
  754. }]);
  755. const viewport = Martins.Viewport({
  756. container: document.getElementById('ar-viewport'),
  757. hudContainer: document.getElementById('ar-hud')
  758. });
  759. const video = document.getElementById('my-video');
  760. const useWebcam = (video === null);
  761. const source = useWebcam ?
  762. Martins.Source.Camera({ resolution: 'md' }) :
  763. Martins.Source.Video(video);
  764. const session = await Martins.startSession({
  765. mode: 'immersive',
  766. viewport: viewport,
  767. trackers: [ tracker ],
  768. sources: [ source ],
  769. stats: true,
  770. gizmos: true,
  771. });
  772. const scan = document.getElementById('scan');
  773. tracker.addEventListener('targetfound', event => {
  774. session.gizmos.visible = false;
  775. if(scan)
  776. scan.hidden = true;
  777. });
  778. tracker.addEventListener('targetlost', event => {
  779. session.gizmos.visible = true;
  780. if(scan)
  781. scan.hidden = false;
  782. });
  783. return session;
  784. }
  785. });