PageRenderTime 49ms CodeModel.GetById 7ms RepoModel.GetById 0ms app.codeStats 1ms

/sdk/tests/conformance/resources/webgl-test-utils.js

https://github.com/JunJiang/WebGL
JavaScript | 2429 lines | 1498 code | 164 blank | 767 comment | 246 complexity | 4916ccfcca581dd97e9a73b2f6bab1a5 MD5 | raw file
  1. /*
  2. ** Copyright (c) 2012 The Khronos Group Inc.
  3. **
  4. ** Permission is hereby granted, free of charge, to any person obtaining a
  5. ** copy of this software and/or associated documentation files (the
  6. ** "Materials"), to deal in the Materials without restriction, including
  7. ** without limitation the rights to use, copy, modify, merge, publish,
  8. ** distribute, sublicense, and/or sell copies of the Materials, and to
  9. ** permit persons to whom the Materials are furnished to do so, subject to
  10. ** the following conditions:
  11. **
  12. ** The above copyright notice and this permission notice shall be included
  13. ** in all copies or substantial portions of the Materials.
  14. **
  15. ** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  16. ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  17. ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
  18. ** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
  19. ** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
  20. ** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
  21. ** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
  22. */
  23. var WebGLTestUtils = (function() {
  24. "use strict";
  25. /**
  26. * Wrapped logging function.
  27. * @param {string} msg The message to log.
  28. */
  29. var log = function(msg) {
  30. if (window.console && window.console.log) {
  31. window.console.log(msg);
  32. }
  33. };
  34. /**
  35. * Wrapped logging function.
  36. * @param {string} msg The message to log.
  37. */
  38. var error = function(msg) {
  39. if (window.console) {
  40. if (window.console.error) {
  41. window.console.error(msg);
  42. }
  43. else if (window.console.log) {
  44. window.console.log(msg);
  45. }
  46. }
  47. };
  48. /**
  49. * Turn off all logging.
  50. */
  51. var loggingOff = function() {
  52. log = function() {};
  53. error = function() {};
  54. };
  55. /**
  56. * Converts a WebGL enum to a string.
  57. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  58. * @param {number} value The enum value.
  59. * @return {string} The enum as a string.
  60. */
  61. var glEnumToString = function(gl, value) {
  62. // Optimization for the most common enum:
  63. if (value === gl.NO_ERROR) {
  64. return "NO_ERROR";
  65. }
  66. for (var p in gl) {
  67. if (gl[p] == value) {
  68. return p;
  69. }
  70. }
  71. return "0x" + value.toString(16);
  72. };
  73. var lastError = "";
  74. /**
  75. * Returns the last compiler/linker error.
  76. * @return {string} The last compiler/linker error.
  77. */
  78. var getLastError = function() {
  79. return lastError;
  80. };
  81. /**
  82. * Whether a haystack ends with a needle.
  83. * @param {string} haystack String to search
  84. * @param {string} needle String to search for.
  85. * @param {boolean} True if haystack ends with needle.
  86. */
  87. var endsWith = function(haystack, needle) {
  88. return haystack.substr(haystack.length - needle.length) === needle;
  89. };
  90. /**
  91. * Whether a haystack starts with a needle.
  92. * @param {string} haystack String to search
  93. * @param {string} needle String to search for.
  94. * @param {boolean} True if haystack starts with needle.
  95. */
  96. var startsWith = function(haystack, needle) {
  97. return haystack.substr(0, needle.length) === needle;
  98. };
  99. /**
  100. * A vertex shader for a single texture.
  101. * @type {string}
  102. */
  103. var simpleTextureVertexShader = [
  104. 'attribute vec4 vPosition;',
  105. 'attribute vec2 texCoord0;',
  106. 'varying vec2 texCoord;',
  107. 'void main() {',
  108. ' gl_Position = vPosition;',
  109. ' texCoord = texCoord0;',
  110. '}'].join('\n');
  111. /**
  112. * A fragment shader for a single texture.
  113. * @type {string}
  114. */
  115. var simpleTextureFragmentShader = [
  116. 'precision mediump float;',
  117. 'uniform sampler2D tex;',
  118. 'varying vec2 texCoord;',
  119. 'void main() {',
  120. ' gl_FragData[0] = texture2D(tex, texCoord);',
  121. '}'].join('\n');
  122. /**
  123. * A vertex shader for a single texture.
  124. * @type {string}
  125. */
  126. var noTexCoordTextureVertexShader = [
  127. 'attribute vec4 vPosition;',
  128. 'varying vec2 texCoord;',
  129. 'void main() {',
  130. ' gl_Position = vPosition;',
  131. ' texCoord = vPosition.xy * 0.5 + 0.5;',
  132. '}'].join('\n');
  133. /**
  134. * A vertex shader for a uniform color.
  135. * @type {string}
  136. */
  137. var simpleColorVertexShader = [
  138. 'attribute vec4 vPosition;',
  139. 'void main() {',
  140. ' gl_Position = vPosition;',
  141. '}'].join('\n');
  142. /**
  143. * A fragment shader for a uniform color.
  144. * @type {string}
  145. */
  146. var simpleColorFragmentShader = [
  147. 'precision mediump float;',
  148. 'uniform vec4 u_color;',
  149. 'void main() {',
  150. ' gl_FragData[0] = u_color;',
  151. '}'].join('\n');
  152. /**
  153. * A vertex shader for vertex colors.
  154. * @type {string}
  155. */
  156. var simpleVertexColorVertexShader = [
  157. 'attribute vec4 vPosition;',
  158. 'attribute vec4 a_color;',
  159. 'varying vec4 v_color;',
  160. 'void main() {',
  161. ' gl_Position = vPosition;',
  162. ' v_color = a_color;',
  163. '}'].join('\n');
  164. /**
  165. * A fragment shader for vertex colors.
  166. * @type {string}
  167. */
  168. var simpleVertexColorFragmentShader = [
  169. 'precision mediump float;',
  170. 'varying vec4 v_color;',
  171. 'void main() {',
  172. ' gl_FragData[0] = v_color;',
  173. '}'].join('\n');
  174. /**
  175. * Creates a simple texture vertex shader.
  176. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  177. * @return {!WebGLShader}
  178. */
  179. var setupSimpleTextureVertexShader = function(gl) {
  180. return loadShader(gl, simpleTextureVertexShader, gl.VERTEX_SHADER);
  181. };
  182. /**
  183. * Creates a simple texture fragment shader.
  184. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  185. * @return {!WebGLShader}
  186. */
  187. var setupSimpleTextureFragmentShader = function(gl) {
  188. return loadShader(
  189. gl, simpleTextureFragmentShader, gl.FRAGMENT_SHADER);
  190. };
  191. /**
  192. * Creates a texture vertex shader that doesn't need texcoords.
  193. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  194. * @return {!WebGLShader}
  195. */
  196. var setupNoTexCoordTextureVertexShader = function(gl) {
  197. return loadShader(gl, noTexCoordTextureVertexShader, gl.VERTEX_SHADER);
  198. };
  199. /**
  200. * Creates a simple vertex color vertex shader.
  201. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  202. * @return {!WebGLShader}
  203. */
  204. var setupSimpleVertexColorVertexShader = function(gl) {
  205. return loadShader(gl, simpleVertexColorVertexShader, gl.VERTEX_SHADER);
  206. };
  207. /**
  208. * Creates a simple vertex color fragment shader.
  209. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  210. * @return {!WebGLShader}
  211. */
  212. var setupSimpleVertexColorFragmentShader = function(gl) {
  213. return loadShader(
  214. gl, simpleVertexColorFragmentShader, gl.FRAGMENT_SHADER);
  215. };
  216. /**
  217. * Creates a simple color vertex shader.
  218. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  219. * @return {!WebGLShader}
  220. */
  221. var setupSimpleColorVertexShader = function(gl) {
  222. return loadShader(gl, simpleColorVertexShader, gl.VERTEX_SHADER);
  223. };
  224. /**
  225. * Creates a simple color fragment shader.
  226. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  227. * @return {!WebGLShader}
  228. */
  229. var setupSimpleColorFragmentShader = function(gl) {
  230. return loadShader(
  231. gl, simpleColorFragmentShader, gl.FRAGMENT_SHADER);
  232. };
  233. /**
  234. * Creates a program, attaches shaders, binds attrib locations, links the
  235. * program and calls useProgram.
  236. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  237. * @param {!Array.<!WebGLShader|string>} shaders The shaders to
  238. * attach, or the source, or the id of a script to get
  239. * the source from.
  240. * @param {!Array.<string>} opt_attribs The attribs names.
  241. * @param {!Array.<number>} opt_locations The locations for the attribs.
  242. */
  243. var setupProgram = function(gl, shaders, opt_attribs, opt_locations) {
  244. var realShaders = [];
  245. var program = gl.createProgram();
  246. var shaderCount = 0;
  247. for (var ii = 0; ii < shaders.length; ++ii) {
  248. var shader = shaders[ii];
  249. var shaderType = undefined;
  250. if (typeof shader == 'string') {
  251. var element = document.getElementById(shader);
  252. if (element) {
  253. if (element.type != "x-shader/x-vertex" && element.type != "x-shader/x-fragment")
  254. shaderType = ii ? gl.FRAGMENT_SHADER : gl.VERTEX_SHADER;
  255. shader = loadShaderFromScript(gl, shader, shaderType);
  256. } else if (endsWith(shader, ".vert")) {
  257. shader = loadShaderFromFile(gl, shader, gl.VERTEX_SHADER);
  258. } else if (endsWith(shader, ".frag")) {
  259. shader = loadShaderFromFile(gl, shader, gl.FRAGMENT_SHADER);
  260. } else {
  261. shader = loadShader(gl, shader, ii ? gl.FRAGMENT_SHADER : gl.VERTEX_SHADER);
  262. }
  263. }
  264. if (shader) {
  265. ++shaderCount;
  266. gl.attachShader(program, shader);
  267. }
  268. }
  269. if (shaderCount != 2) {
  270. error("Error in compiling shader");
  271. return null;
  272. }
  273. if (opt_attribs) {
  274. for (var ii = 0; ii < opt_attribs.length; ++ii) {
  275. gl.bindAttribLocation(
  276. program,
  277. opt_locations ? opt_locations[ii] : ii,
  278. opt_attribs[ii]);
  279. }
  280. }
  281. gl.linkProgram(program);
  282. // Check the link status
  283. var linked = gl.getProgramParameter(program, gl.LINK_STATUS);
  284. if (!linked) {
  285. // something went wrong with the link
  286. lastError = gl.getProgramInfoLog (program);
  287. error("Error in program linking:" + lastError);
  288. gl.deleteProgram(program);
  289. return null;
  290. }
  291. gl.useProgram(program);
  292. return program;
  293. };
  294. /**
  295. * Creates a simple texture program.
  296. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  297. * @return {WebGLProgram}
  298. */
  299. var setupNoTexCoordTextureProgram = function(gl) {
  300. var vs = setupNoTexCoordTextureVertexShader(gl);
  301. var fs = setupSimpleTextureFragmentShader(gl);
  302. if (!vs || !fs) {
  303. return null;
  304. }
  305. var program = setupProgram(
  306. gl,
  307. [vs, fs],
  308. ['vPosition'],
  309. [0]);
  310. if (!program) {
  311. gl.deleteShader(fs);
  312. gl.deleteShader(vs);
  313. }
  314. gl.useProgram(program);
  315. return program;
  316. };
  317. /**
  318. * Creates a simple texture program.
  319. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  320. * @param {number} opt_positionLocation The attrib location for position.
  321. * @param {number} opt_texcoordLocation The attrib location for texture coords.
  322. * @return {WebGLProgram}
  323. */
  324. var setupSimpleTextureProgram = function(
  325. gl, opt_positionLocation, opt_texcoordLocation) {
  326. opt_positionLocation = opt_positionLocation || 0;
  327. opt_texcoordLocation = opt_texcoordLocation || 1;
  328. var vs = setupSimpleTextureVertexShader(gl);
  329. var fs = setupSimpleTextureFragmentShader(gl);
  330. if (!vs || !fs) {
  331. return null;
  332. }
  333. var program = setupProgram(
  334. gl,
  335. [vs, fs],
  336. ['vPosition', 'texCoord0'],
  337. [opt_positionLocation, opt_texcoordLocation]);
  338. if (!program) {
  339. gl.deleteShader(fs);
  340. gl.deleteShader(vs);
  341. }
  342. gl.useProgram(program);
  343. return program;
  344. };
  345. /**
  346. * Creates a simple vertex color program.
  347. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  348. * @param {number} opt_positionLocation The attrib location for position.
  349. * @param {number} opt_vertexColorLocation The attrib location
  350. * for vertex colors.
  351. * @return {WebGLProgram}
  352. */
  353. var setupSimpleVertexColorProgram = function(
  354. gl, opt_positionLocation, opt_vertexColorLocation) {
  355. opt_positionLocation = opt_positionLocation || 0;
  356. opt_vertexColorLocation = opt_vertexColorLocation || 1;
  357. var vs = setupSimpleVertexColorVertexShader(gl);
  358. var fs = setupSimpleVertexColorFragmentShader(gl);
  359. if (!vs || !fs) {
  360. return null;
  361. }
  362. var program = setupProgram(
  363. gl,
  364. [vs, fs],
  365. ['vPosition', 'a_color'],
  366. [opt_positionLocation, opt_vertexColorLocation]);
  367. if (!program) {
  368. gl.deleteShader(fs);
  369. gl.deleteShader(vs);
  370. }
  371. gl.useProgram(program);
  372. return program;
  373. };
  374. /**
  375. * Creates a simple color program.
  376. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  377. * @param {number} opt_positionLocation The attrib location for position.
  378. * @return {WebGLProgram}
  379. */
  380. var setupSimpleColorProgram = function(gl, opt_positionLocation) {
  381. opt_positionLocation = opt_positionLocation || 0;
  382. var vs = setupSimpleColorVertexShader(gl);
  383. var fs = setupSimpleColorFragmentShader(gl);
  384. if (!vs || !fs) {
  385. return null;
  386. }
  387. var program = setupProgram(
  388. gl,
  389. [vs, fs],
  390. ['vPosition'],
  391. [opt_positionLocation]);
  392. if (!program) {
  393. gl.deleteShader(fs);
  394. gl.deleteShader(vs);
  395. }
  396. gl.useProgram(program);
  397. return program;
  398. };
  399. /**
  400. * Creates buffers for a textured unit quad and attaches them to vertex attribs.
  401. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  402. * @param {number} opt_positionLocation The attrib location for position.
  403. * @param {number} opt_texcoordLocation The attrib location for texture coords.
  404. * @return {!Array.<WebGLBuffer>} The buffer objects that were
  405. * created.
  406. */
  407. var setupUnitQuad = function(gl, opt_positionLocation, opt_texcoordLocation) {
  408. return setupUnitQuadWithTexCoords(gl, [ 0.0, 0.0 ], [ 1.0, 1.0 ],
  409. opt_positionLocation, opt_texcoordLocation);
  410. };
  411. /**
  412. * Creates buffers for a textured unit quad with specified lower left
  413. * and upper right texture coordinates, and attaches them to vertex
  414. * attribs.
  415. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  416. * @param {!Array.<number>} lowerLeftTexCoords The texture coordinates for the lower left corner.
  417. * @param {!Array.<number>} upperRightTexCoords The texture coordinates for the upper right corner.
  418. * @param {number} opt_positionLocation The attrib location for position.
  419. * @param {number} opt_texcoordLocation The attrib location for texture coords.
  420. * @return {!Array.<WebGLBuffer>} The buffer objects that were
  421. * created.
  422. */
  423. var setupUnitQuadWithTexCoords = function(
  424. gl, lowerLeftTexCoords, upperRightTexCoords,
  425. opt_positionLocation, opt_texcoordLocation) {
  426. return setupQuad(gl, {
  427. positionLocation: opt_positionLocation || 0,
  428. texcoordLocation: opt_texcoordLocation || 1,
  429. lowerLeftTexCoords: lowerLeftTexCoords,
  430. upperRightTexCoords: upperRightTexCoords,
  431. });
  432. };
  433. /**
  434. * Makes a quad with various options.
  435. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  436. * @param {!Object} options.
  437. *
  438. * scale: scale to multiple unit quad values by. default 1.0.
  439. * positionLocation: attribute location for position.
  440. * texcoordLocation: attribute location for texcoords.
  441. * If this does not exist no texture coords are created.
  442. * lowerLeftTexCoords: an array of 2 values for the
  443. * lowerLeftTexCoords.
  444. * upperRightTexCoords: an array of 2 values for the
  445. * upperRightTexCoords.
  446. */
  447. var setupQuad = function(gl, options) {
  448. var positionLocation = options.positionLocation || 0;
  449. var scale = options.scale || 1;
  450. var objects = [];
  451. var vertexObject = gl.createBuffer();
  452. gl.bindBuffer(gl.ARRAY_BUFFER, vertexObject);
  453. gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
  454. 1.0 * scale , 1.0 * scale,
  455. -1.0 * scale , 1.0 * scale,
  456. -1.0 * scale , -1.0 * scale,
  457. 1.0 * scale , 1.0 * scale,
  458. -1.0 * scale , -1.0 * scale,
  459. 1.0 * scale , -1.0 * scale,]), gl.STATIC_DRAW);
  460. gl.enableVertexAttribArray(positionLocation);
  461. gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
  462. objects.push(vertexObject);
  463. if (options.texcoordLocation !== undefined) {
  464. var llx = options.lowerLeftTexCoords[0];
  465. var lly = options.lowerLeftTexCoords[1];
  466. var urx = options.upperRightTexCoords[0];
  467. var ury = options.upperRightTexCoords[1];
  468. var vertexObject = gl.createBuffer();
  469. gl.bindBuffer(gl.ARRAY_BUFFER, vertexObject);
  470. gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
  471. urx, ury,
  472. llx, ury,
  473. llx, lly,
  474. urx, ury,
  475. llx, lly,
  476. urx, lly]), gl.STATIC_DRAW);
  477. gl.enableVertexAttribArray(options.texcoordLocation);
  478. gl.vertexAttribPointer(options.texcoordLocation, 2, gl.FLOAT, false, 0, 0);
  479. objects.push(vertexObject);
  480. }
  481. return objects;
  482. };
  483. /**
  484. * Creates a program and buffers for rendering a textured quad.
  485. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  486. * @param {number} opt_positionLocation The attrib location for
  487. * position. Default = 0.
  488. * @param {number} opt_texcoordLocation The attrib location for
  489. * texture coords. Default = 1.
  490. * @return {!WebGLProgram}
  491. */
  492. var setupTexturedQuad = function(
  493. gl, opt_positionLocation, opt_texcoordLocation) {
  494. var program = setupSimpleTextureProgram(
  495. gl, opt_positionLocation, opt_texcoordLocation);
  496. setupUnitQuad(gl, opt_positionLocation, opt_texcoordLocation);
  497. return program;
  498. };
  499. /**
  500. * Creates a program and buffers for rendering a color quad.
  501. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  502. * @param {number} opt_positionLocation The attrib location for position.
  503. * @return {!WebGLProgram}
  504. */
  505. var setupColorQuad = function(gl, opt_positionLocation) {
  506. opt_positionLocation = opt_positionLocation || 0;
  507. var program = setupSimpleColorProgram(gl);
  508. setupUnitQuad(gl, opt_positionLocation);
  509. return program;
  510. };
  511. /**
  512. * Creates a program and buffers for rendering a textured quad with
  513. * specified lower left and upper right texture coordinates.
  514. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  515. * @param {!Array.<number>} lowerLeftTexCoords The texture coordinates for the lower left corner.
  516. * @param {!Array.<number>} upperRightTexCoords The texture coordinates for the upper right corner.
  517. * @param {number} opt_positionLocation The attrib location for position.
  518. * @param {number} opt_texcoordLocation The attrib location for texture coords.
  519. * @return {!WebGLProgram}
  520. */
  521. var setupTexturedQuadWithTexCoords = function(
  522. gl, lowerLeftTexCoords, upperRightTexCoords,
  523. opt_positionLocation, opt_texcoordLocation) {
  524. var program = setupSimpleTextureProgram(
  525. gl, opt_positionLocation, opt_texcoordLocation);
  526. setupUnitQuadWithTexCoords(gl, lowerLeftTexCoords, upperRightTexCoords,
  527. opt_positionLocation, opt_texcoordLocation);
  528. return program;
  529. };
  530. /**
  531. * Creates a unit quad with only positions of a given resolution.
  532. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  533. * @param {number} gridRes The resolution of the mesh grid,
  534. * expressed in the number of quads across and down.
  535. * @param {number} opt_positionLocation The attrib location for position.
  536. */
  537. var setupIndexedQuad = function (
  538. gl, gridRes, opt_positionLocation, opt_flipOddTriangles) {
  539. return setupIndexedQuadWithOptions(gl,
  540. { gridRes: gridRes,
  541. positionLocation: opt_positionLocation,
  542. flipOddTriangles: opt_flipOddTriangles
  543. });
  544. };
  545. /**
  546. * Creates a quad with various options.
  547. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  548. * @param {!Object) options The options. See below.
  549. * @return {!Array.<WebGLBuffer>} The created buffers.
  550. * [positions, <colors>, indices]
  551. *
  552. * Options:
  553. * gridRes: number of quads across and down grid.
  554. * positionLocation: attrib location for position
  555. * flipOddTriangles: reverse order of vertices of every other
  556. * triangle
  557. * positionOffset: offset added to each vertex
  558. * positionMult: multipier for each vertex
  559. * colorLocation: attrib location for vertex colors. If
  560. * undefined no vertex colors will be created.
  561. */
  562. var setupIndexedQuadWithOptions = function (gl, options) {
  563. var positionLocation = options.positionLocation || 0;
  564. var objects = [];
  565. var gridRes = options.gridRes || 1;
  566. var positionOffset = options.positionOffset || 0;
  567. var positionMult = options.positionMult || 1;
  568. var vertsAcross = gridRes + 1;
  569. var numVerts = vertsAcross * vertsAcross;
  570. var positions = new Float32Array(numVerts * 3);
  571. var indices = new Uint16Array(6 * gridRes * gridRes);
  572. var poffset = 0;
  573. for (var yy = 0; yy <= gridRes; ++yy) {
  574. for (var xx = 0; xx <= gridRes; ++xx) {
  575. positions[poffset + 0] = (-1 + 2 * xx / gridRes) * positionMult + positionOffset;
  576. positions[poffset + 1] = (-1 + 2 * yy / gridRes) * positionMult + positionOffset;
  577. positions[poffset + 2] = 0;
  578. poffset += 3;
  579. }
  580. }
  581. var buf = gl.createBuffer();
  582. gl.bindBuffer(gl.ARRAY_BUFFER, buf);
  583. gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW);
  584. gl.enableVertexAttribArray(positionLocation);
  585. gl.vertexAttribPointer(positionLocation, 3, gl.FLOAT, false, 0, 0);
  586. objects.push(buf);
  587. if (options.colorLocation !== undefined) {
  588. var colors = new Float32Array(numVerts * 4);
  589. for (var yy = 0; yy <= gridRes; ++yy) {
  590. for (var xx = 0; xx <= gridRes; ++xx) {
  591. if (options.color !== undefined) {
  592. colors[poffset + 0] = options.color[0];
  593. colors[poffset + 1] = options.color[1];
  594. colors[poffset + 2] = options.color[2];
  595. colors[poffset + 3] = options.color[3];
  596. } else {
  597. colors[poffset + 0] = xx / gridRes;
  598. colors[poffset + 1] = yy / gridRes;
  599. colors[poffset + 2] = (xx / gridRes) * (yy / gridRes);
  600. colors[poffset + 3] = (yy % 2) * 0.5 + 0.5;
  601. }
  602. poffset += 4;
  603. }
  604. }
  605. var buf = gl.createBuffer();
  606. gl.bindBuffer(gl.ARRAY_BUFFER, buf);
  607. gl.bufferData(gl.ARRAY_BUFFER, colors, gl.STATIC_DRAW);
  608. gl.enableVertexAttribArray(options.colorLocation);
  609. gl.vertexAttribPointer(options.colorLocation, 4, gl.FLOAT, false, 0, 0);
  610. objects.push(buf);
  611. }
  612. var tbase = 0;
  613. for (var yy = 0; yy < gridRes; ++yy) {
  614. var index = yy * vertsAcross;
  615. for (var xx = 0; xx < gridRes; ++xx) {
  616. indices[tbase + 0] = index + 0;
  617. indices[tbase + 1] = index + 1;
  618. indices[tbase + 2] = index + vertsAcross;
  619. indices[tbase + 3] = index + vertsAcross;
  620. indices[tbase + 4] = index + 1;
  621. indices[tbase + 5] = index + vertsAcross + 1;
  622. if (options.flipOddTriangles) {
  623. indices[tbase + 4] = index + vertsAcross + 1;
  624. indices[tbase + 5] = index + 1;
  625. }
  626. index += 1;
  627. tbase += 6;
  628. }
  629. }
  630. var buf = gl.createBuffer();
  631. gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buf);
  632. gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);
  633. objects.push(buf);
  634. return objects;
  635. };
  636. /**
  637. * Returns the constructor for an ArrayBuffer that
  638. * corresponds to the given WebGL type.
  639. * @param {!WebGLRenderingContext} gl A WebGLRenderingContext.
  640. * @param {number} type The WebGL type (eg, gl.UNSIGNED_BYTE)
  641. * @return {!Constructor} The ArrayBuffer constructor that
  642. * corresponds to the given type.
  643. */
  644. var glTypeToArrayBufferType = function(gl, type) {
  645. switch (type) {
  646. case gl.BYTE:
  647. return window.Int8Array;
  648. case gl.UNSIGNED_BYTE:
  649. return window.Uint8Array;
  650. case gl.SHORT:
  651. return window.Int16Array;
  652. case gl.UNSIGNED_SHORT:
  653. case gl.UNSIGNED_SHORT_5_6_5:
  654. case gl.UNSIGNED_SHORT_4_4_4_4:
  655. case gl.UNSIGNED_SHORT_5_5_5_1:
  656. return window.Uint16Array;
  657. case gl.INT:
  658. return window.Int32Array;
  659. case gl.UNSIGNED_INT:
  660. return window.Uint32Array;
  661. default:
  662. throw 'unknown gl type ' + glEnumToString(gl, type);
  663. }
  664. };
  665. /**
  666. * Returns the number of bytes per component for a given WebGL
  667. * type.
  668. * @param {!WebGLRenderingContext} gl A WebGLRenderingContext.
  669. * @param {number} type The WebGL type (eg, gl.UNSIGNED_BYTE)
  670. * @return {!Constructor} The ArrayBuffer constructor that
  671. * corresponds to the given type.
  672. */
  673. var getBytesPerComponent = function(gl, type) {
  674. switch (type) {
  675. case gl.BYTE:
  676. case gl.UNSIGNED_BYTE:
  677. return 1;
  678. case gl.SHORT:
  679. case gl.UNSIGNED_SHORT:
  680. case gl.UNSIGNED_SHORT_5_6_5:
  681. case gl.UNSIGNED_SHORT_4_4_4_4:
  682. case gl.UNSIGNED_SHORT_5_5_5_1:
  683. return 2;
  684. case gl.INT:
  685. case gl.UNSIGNED_INT:
  686. return 4;
  687. default:
  688. throw 'unknown gl type ' + glEnumToString(gl, type);
  689. }
  690. };
  691. /**
  692. * Fills the given texture with a solid color.
  693. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  694. * @param {!WebGLTexture} tex The texture to fill.
  695. * @param {number} width The width of the texture to create.
  696. * @param {number} height The height of the texture to create.
  697. * @param {!Array.<number>} color The color to fill with.
  698. * where each element is in the range 0 to 255.
  699. * @param {number} opt_level The level of the texture to fill. Default = 0.
  700. * @param {number} opt_format The format for the texture.
  701. */
  702. var fillTexture = function(gl, tex, width, height, color, opt_level, opt_format, opt_type) {
  703. opt_level = opt_level || 0;
  704. opt_format = opt_format || gl.RGBA;
  705. opt_type = opt_type || gl.UNSIGNED_BYTE;
  706. var pack = gl.getParameter(gl.UNPACK_ALIGNMENT);
  707. var numComponents = color.length;
  708. var bytesPerComponent = getBytesPerComponent(gl, opt_type);
  709. var rowSize = numComponents * width * bytesPerComponent;
  710. var paddedRowSize = Math.floor((rowSize + pack - 1) / pack) * pack;
  711. var size = rowSize + (height - 1) * paddedRowSize;
  712. size = Math.floor((size + bytesPerComponent - 1) / bytesPerComponent) * bytesPerComponent;
  713. var buf = new (glTypeToArrayBufferType(gl, opt_type))(size);
  714. for (var yy = 0; yy < height; ++yy) {
  715. var off = yy * paddedRowSize;
  716. for (var xx = 0; xx < width; ++xx) {
  717. for (var jj = 0; jj < numComponents; ++jj) {
  718. buf[off++] = color[jj];
  719. }
  720. }
  721. }
  722. gl.bindTexture(gl.TEXTURE_2D, tex);
  723. gl.texImage2D(
  724. gl.TEXTURE_2D, opt_level, opt_format, width, height, 0,
  725. opt_format, opt_type, buf);
  726. };
  727. /**
  728. * Creates a texture and fills it with a solid color.
  729. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  730. * @param {number} width The width of the texture to create.
  731. * @param {number} height The height of the texture to create.
  732. * @param {!Array.<number>} color The color to fill with. A 4 element array
  733. * where each element is in the range 0 to 255.
  734. * @return {!WebGLTexture}
  735. */
  736. var createColoredTexture = function(gl, width, height, color) {
  737. var tex = gl.createTexture();
  738. fillTexture(gl, tex, width, height, color);
  739. return tex;
  740. };
  741. var ubyteToFloat = function(c) {
  742. return c / 255;
  743. };
  744. var ubyteColorToFloatColor = function(color) {
  745. var floatColor = [];
  746. for (var ii = 0; ii < color.length; ++ii) {
  747. floatColor[ii] = ubyteToFloat(color[ii]);
  748. }
  749. return floatColor;
  750. };
  751. /**
  752. * Sets the "u_color" uniform of the current program to color.
  753. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  754. * @param {!Array.<number> color 4 element array of 0-1 color
  755. * components.
  756. */
  757. var setFloatDrawColor = function(gl, color) {
  758. var program = gl.getParameter(gl.CURRENT_PROGRAM);
  759. var colorLocation = gl.getUniformLocation(program, "u_color");
  760. gl.uniform4fv(colorLocation, color);
  761. };
  762. /**
  763. * Sets the "u_color" uniform of the current program to color.
  764. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  765. * @param {!Array.<number> color 4 element array of 0-255 color
  766. * components.
  767. */
  768. var setUByteDrawColor = function(gl, color) {
  769. setFloatDrawColor(gl, ubyteColorToFloatColor(color));
  770. };
  771. /**
  772. * Draws a previously setup quad in the given color.
  773. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  774. * @param {!Array.<number>} color The color to draw with. A 4
  775. * element array where each element is in the range 0 to
  776. * 1.
  777. */
  778. var drawFloatColorQuad = function(gl, color) {
  779. var program = gl.getParameter(gl.CURRENT_PROGRAM);
  780. var colorLocation = gl.getUniformLocation(program, "u_color");
  781. gl.uniform4fv(colorLocation, color);
  782. gl.drawArrays(gl.TRIANGLES, 0, 6);
  783. };
  784. /**
  785. * Draws a previously setup quad in the given color.
  786. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  787. * @param {!Array.<number>} color The color to draw with. A 4
  788. * element array where each element is in the range 0 to
  789. * 255.
  790. */
  791. var drawUByteColorQuad = function(gl, color) {
  792. drawFloatColorQuad(gl, ubyteColorToFloatColor(color));
  793. };
  794. /**
  795. * Draws a previously setupUnitQuad.
  796. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  797. */
  798. var drawUnitQuad = function(gl) {
  799. gl.drawArrays(gl.TRIANGLES, 0, 6);
  800. };
  801. /**
  802. * Clears then Draws a previously setupUnitQuad.
  803. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  804. * @param {!Array.<number>} opt_color The color to fill clear with before
  805. * drawing. A 4 element array where each element is in the range 0 to
  806. * 255. Default [255, 255, 255, 255]
  807. */
  808. var clearAndDrawUnitQuad = function(gl, opt_color) {
  809. opt_color = opt_color || [255, 255, 255, 255];
  810. gl.clearColor(
  811. opt_color[0] / 255,
  812. opt_color[1] / 255,
  813. opt_color[2] / 255,
  814. opt_color[3] / 255);
  815. gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
  816. drawUnitQuad(gl);
  817. };
  818. /**
  819. * Draws a quad previously setup with setupIndexedQuad.
  820. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  821. * @param {number} gridRes Resolution of grid.
  822. */
  823. var drawIndexedQuad = function(gl, gridRes) {
  824. gl.drawElements(gl.TRIANGLES, gridRes * gridRes * 6, gl.UNSIGNED_SHORT, 0);
  825. };
  826. /**
  827. * Draws a previously setupIndexedQuad
  828. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  829. * @param {number} gridRes Resolution of grid.
  830. * @param {!Array.<number>} opt_color The color to fill clear with before
  831. * drawing. A 4 element array where each element is in the range 0 to
  832. * 255. Default [255, 255, 255, 255]
  833. */
  834. var clearAndDrawIndexedQuad = function(gl, gridRes, opt_color) {
  835. opt_color = opt_color || [255, 255, 255, 255];
  836. gl.clearColor(
  837. opt_color[0] / 255,
  838. opt_color[1] / 255,
  839. opt_color[2] / 255,
  840. opt_color[3] / 255);
  841. gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
  842. drawIndexedQuad(gl, gridRes);
  843. };
  844. /**
  845. * Clips a range to min, max
  846. * (Eg. clipToRange(-5,7,0,20) would return {value:0,extent:2}
  847. * @param {number} value start of range
  848. * @param {number} extent extent of range
  849. * @param {number} min min.
  850. * @param {number} max max.
  851. * @return {!{value:number,extent:number} The clipped value.
  852. */
  853. var clipToRange = function(value, extent, min, max) {
  854. if (value < min) {
  855. extent -= min - value;
  856. value = min;
  857. }
  858. var end = value + extent;
  859. if (end > max) {
  860. extent -= end - max;
  861. }
  862. if (extent < 0) {
  863. value = max;
  864. extent = 0;
  865. }
  866. return {value:value, extent: extent};
  867. };
  868. /**
  869. * Determines if the passed context is an instance of a WebGLRenderingContext
  870. * or later variant (like WebGL2RenderingContext)
  871. * @param {CanvasRenderingContext} ctx The context to check.
  872. */
  873. var isWebGLContext = function(ctx) {
  874. if (ctx instanceof WebGLRenderingContext)
  875. return true;
  876. if ('WebGL2RenderingContext' in window && ctx instanceof WebGL2RenderingContext)
  877. return true;
  878. return false;
  879. };
  880. /**
  881. * Checks that a portion of a canvas is 1 color.
  882. * @param {!WebGLRenderingContext|CanvasRenderingContext2D} gl The
  883. * WebGLRenderingContext or 2D context to use.
  884. * @param {number} x left corner of region to check.
  885. * @param {number} y bottom corner of region to check in case of checking from
  886. * a GL context or top corner in case of checking from a 2D context.
  887. * @param {number} width width of region to check.
  888. * @param {number} height width of region to check.
  889. * @param {!Array.<number>} color The color expected. A 4 element array where
  890. * each element is in the range 0 to 255.
  891. * @param {number} opt_errorRange Optional. Acceptable error in
  892. * color checking. 0 by default.
  893. * @param {!function()} sameFn Function to call if all pixels
  894. * are the same as color.
  895. * @param {!function()} differentFn Function to call if a pixel
  896. * is different than color
  897. * @param {!function()} logFn Function to call for logging.
  898. */
  899. var checkCanvasRectColor = function(gl, x, y, width, height, color, opt_errorRange, sameFn, differentFn, logFn) {
  900. if (isWebGLContext(gl) && !gl.getParameter(gl.FRAMEBUFFER_BINDING)) {
  901. // We're reading the backbuffer so clip.
  902. var xr = clipToRange(x, width, 0, gl.canvas.width);
  903. var yr = clipToRange(y, height, 0, gl.canvas.height);
  904. if (!xr.extent || !yr.extent) {
  905. logFn("checking rect: effective width or heigh is zero");
  906. sameFn();
  907. return;
  908. }
  909. x = xr.value;
  910. y = yr.value;
  911. width = xr.extent;
  912. height = yr.extent;
  913. }
  914. var errorRange = opt_errorRange || 0;
  915. if (!errorRange.length) {
  916. errorRange = [errorRange, errorRange, errorRange, errorRange]
  917. }
  918. var buf;
  919. if (isWebGLContext(gl)) {
  920. buf = new Uint8Array(width * height * 4);
  921. gl.readPixels(x, y, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buf);
  922. } else {
  923. buf = gl.getImageData(x, y, width, height).data;
  924. }
  925. for (var i = 0; i < width * height; ++i) {
  926. var offset = i * 4;
  927. for (var j = 0; j < color.length; ++j) {
  928. if (Math.abs(buf[offset + j] - color[j]) > errorRange[j]) {
  929. differentFn();
  930. var was = buf[offset + 0].toString();
  931. for (j = 1; j < color.length; ++j) {
  932. was += "," + buf[offset + j];
  933. }
  934. logFn('at (' + (x + (i % width)) + ', ' + (y + Math.floor(i / width)) +
  935. ') expected: ' + color + ' was ' + was);
  936. return;
  937. }
  938. }
  939. }
  940. sameFn();
  941. };
  942. /**
  943. * Checks that a portion of a canvas is 1 color.
  944. * @param {!WebGLRenderingContext|CanvasRenderingContext2D} gl The
  945. * WebGLRenderingContext or 2D context to use.
  946. * @param {number} x left corner of region to check.
  947. * @param {number} y bottom corner of region to check in case of checking from
  948. * a GL context or top corner in case of checking from a 2D context.
  949. * @param {number} width width of region to check.
  950. * @param {number} height width of region to check.
  951. * @param {!Array.<number>} color The color expected. A 4 element array where
  952. * each element is in the range 0 to 255.
  953. * @param {string} opt_msg Message to associate with success. Eg
  954. * ("should be red").
  955. * @param {number} opt_errorRange Optional. Acceptable error in
  956. * color checking. 0 by default.
  957. */
  958. var checkCanvasRect = function(gl, x, y, width, height, color, opt_msg, opt_errorRange) {
  959. var msg = opt_msg;
  960. if (msg === undefined) {
  961. msg = "should be " + color.toString();
  962. }
  963. checkCanvasRectColor(
  964. gl, x, y, width, height, color, opt_errorRange,
  965. function() {
  966. testPassed(msg);
  967. },
  968. function() {
  969. testFailed(msg);
  970. },
  971. debug);
  972. };
  973. /**
  974. * Checks that an entire canvas is 1 color.
  975. * @param {!WebGLRenderingContext|CanvasRenderingContext2D} gl The
  976. * WebGLRenderingContext or 2D context to use.
  977. * @param {!Array.<number>} color The color expected. A 4 element array where
  978. * each element is in the range 0 to 255.
  979. * @param {string} msg Message to associate with success. Eg ("should be red").
  980. * @param {number} errorRange Optional. Acceptable error in
  981. * color checking. 0 by default.
  982. */
  983. var checkCanvas = function(gl, color, msg, errorRange) {
  984. checkCanvasRect(gl, 0, 0, gl.canvas.width, gl.canvas.height, color, msg, errorRange);
  985. };
  986. /**
  987. * Checks a rectangular area both inside the area and outside
  988. * the area.
  989. * @param {!WebGLRenderingContext|CanvasRenderingContext2D} gl The
  990. * WebGLRenderingContext or 2D context to use.
  991. * @param {number} x left corner of region to check.
  992. * @param {number} y bottom corner of region to check in case of checking from
  993. * a GL context or top corner in case of checking from a 2D context.
  994. * @param {number} width width of region to check.
  995. * @param {number} height width of region to check.
  996. * @param {!Array.<number>} innerColor The color expected inside
  997. * the area. A 4 element array where each element is in the
  998. * range 0 to 255.
  999. * @param {!Array.<number>} outerColor The color expected
  1000. * outside. A 4 element array where each element is in the
  1001. * range 0 to 255.
  1002. * @param {!number} opt_edgeSize: The number of pixels to skip
  1003. * around the edges of the area. Defaut 0.
  1004. * @param {!{width:number, height:number}} opt_outerDimensions
  1005. * The outer dimensions. Default the size of gl.canvas.
  1006. */
  1007. var checkAreaInAndOut = function(gl, x, y, width, height, innerColor, outerColor, opt_edgeSize, opt_outerDimensions) {
  1008. var outerDimensions = opt_outerDimensions || { width: gl.canvas.width, height: gl.canvas.height };
  1009. var edgeSize = opt_edgeSize || 0;
  1010. checkCanvasRect(gl, x + edgeSize, y + edgeSize, width - edgeSize * 2, height - edgeSize * 2, innerColor);
  1011. checkCanvasRect(gl, 0, 0, x - edgeSize, outerDimensions.height, outerColor);
  1012. checkCanvasRect(gl, x + width + edgeSize, 0, outerDimensions.width - x - width - edgeSize, outerDimensions.height, outerColor);
  1013. checkCanvasRect(gl, 0, 0, outerDimensions.width, y - edgeSize, outerColor);
  1014. checkCanvasRect(gl, 0, y + height + edgeSize, outerDimensions.width, outerDimensions.height - y - height - edgeSize, outerColor);
  1015. };
  1016. /**
  1017. * Loads a texture, calls callback when finished.
  1018. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  1019. * @param {string} url URL of image to load
  1020. * @param {function(!Image): void} callback Function that gets called after
  1021. * image has loaded
  1022. * @return {!WebGLTexture} The created texture.
  1023. */
  1024. var loadTexture = function(gl, url, callback) {
  1025. var texture = gl.createTexture();
  1026. gl.bindTexture(gl.TEXTURE_2D, texture);
  1027. gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
  1028. gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
  1029. var image = new Image();
  1030. image.onload = function() {
  1031. gl.bindTexture(gl.TEXTURE_2D, texture);
  1032. gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
  1033. gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
  1034. callback(image);
  1035. };
  1036. image.src = url;
  1037. return texture;
  1038. };
  1039. /**
  1040. * Makes a shallow copy of an object.
  1041. * @param {!Object) src Object to copy
  1042. * @return {!Object} The copy of src.
  1043. */
  1044. var shallowCopyObject = function(src) {
  1045. var dst = {};
  1046. for (var attr in src) {
  1047. if (src.hasOwnProperty(attr)) {
  1048. dst[attr] = src[attr];
  1049. }
  1050. }
  1051. return dst;
  1052. };
  1053. /**
  1054. * Checks if an attribute exists on an object case insensitive.
  1055. * @param {!Object) obj Object to check
  1056. * @param {string} attr Name of attribute to look for.
  1057. * @return {string?} The name of the attribute if it exists,
  1058. * undefined if not.
  1059. */
  1060. var hasAttributeCaseInsensitive = function(obj, attr) {
  1061. var lower = attr.toLowerCase();
  1062. for (var key in obj) {
  1063. if (obj.hasOwnProperty(key) && key.toLowerCase() == lower) {
  1064. return key;
  1065. }
  1066. }
  1067. };
  1068. /**
  1069. * Returns a map of URL querystring options
  1070. * @return {Object?} Object containing all the values in the URL querystring
  1071. */
  1072. var getUrlOptions = function() {
  1073. var options = {};
  1074. var s = window.location.href;
  1075. var q = s.indexOf("?");
  1076. var e = s.indexOf("#");
  1077. if (e < 0) {
  1078. e = s.length;
  1079. }
  1080. var query = s.substring(q + 1, e);
  1081. var pairs = query.split("&");
  1082. for (var ii = 0; ii < pairs.length; ++ii) {
  1083. var keyValue = pairs[ii].split("=");
  1084. var key = keyValue[0];
  1085. var value = decodeURIComponent(keyValue[1]);
  1086. options[key] = value;
  1087. }
  1088. return options;
  1089. };
  1090. /**
  1091. * Creates a webgl context.
  1092. * @param {!Canvas|string} opt_canvas The canvas tag to get
  1093. * context from. If one is not passed in one will be
  1094. * created. If it's a string it's assumed to be the id of a
  1095. * canvas.
  1096. * @param {Object} opt_attributes Context attributes.
  1097. * @param {!number} opt_version Version of WebGL context to create
  1098. * @return {!WebGLRenderingContext} The created context.
  1099. */
  1100. var create3DContext = function(opt_canvas, opt_attributes, opt_version) {
  1101. if (window.initTestingHarness) {
  1102. window.initTestingHarness();
  1103. }
  1104. var attributes = shallowCopyObject(opt_attributes || {});
  1105. if (!hasAttributeCaseInsensitive(attributes, "antialias")) {
  1106. attributes.antialias = false;
  1107. }
  1108. if (!opt_version) {
  1109. opt_version = parseInt(getUrlOptions().webglVersion, 10) || 1;
  1110. }
  1111. opt_canvas = opt_canvas || document.createElement("canvas");
  1112. if (typeof opt_canvas == 'string') {
  1113. opt_canvas = document.getElementById(opt_canvas);
  1114. }
  1115. var context = null;
  1116. var names;
  1117. switch (opt_version) {
  1118. case 2:
  1119. names = ["webgl2", "experimental-webgl2"]; break;
  1120. default:
  1121. names = ["webgl", "experimental-webgl"]; break;
  1122. }
  1123. for (var i = 0; i < names.length; ++i) {
  1124. try {
  1125. context = opt_canvas.getContext(names[i], attributes);
  1126. } catch (e) {
  1127. }
  1128. if (context) {
  1129. break;
  1130. }
  1131. }
  1132. if (!context) {
  1133. testFailed("Unable to fetch WebGL rendering context for Canvas");
  1134. }
  1135. return context;
  1136. }
  1137. /**
  1138. * Wraps a WebGL function with a function that throws an exception if there is
  1139. * an error.
  1140. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  1141. * @param {string} fname Name of function to wrap.
  1142. * @return {function} The wrapped function.
  1143. */
  1144. var createGLErrorWrapper = function(context, fname) {
  1145. return function() {
  1146. var rv = context[fname].apply(context, arguments);
  1147. var err = context.getError();
  1148. if (err != context.NO_ERROR)
  1149. throw "GL error " + glEnumToString(context, err) + " in " + fname;
  1150. return rv;
  1151. };
  1152. };
  1153. /**
  1154. * Creates a WebGL context where all functions are wrapped to throw an exception
  1155. * if there is an error.
  1156. * @param {!Canvas} canvas The HTML canvas to get a context from.
  1157. * @return {!Object} The wrapped context.
  1158. */
  1159. function create3DContextWithWrapperThatThrowsOnGLError(canvas) {
  1160. var context = create3DContext(canvas);
  1161. var wrap = {};
  1162. for (var i in context) {
  1163. try {
  1164. if (typeof context[i] == 'function') {
  1165. wrap[i] = createGLErrorWrapper(context, i);
  1166. } else {
  1167. wrap[i] = context[i];
  1168. }
  1169. } catch (e) {
  1170. error("createContextWrapperThatThrowsOnGLError: Error accessing " + i);
  1171. }
  1172. }
  1173. wrap.getError = function() {
  1174. return context.getError();
  1175. };
  1176. return wrap;
  1177. };
  1178. /**
  1179. * Tests that an evaluated expression generates a specific GL error.
  1180. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  1181. * @param {number|Array.<number>} glErrors The expected gl error or an array of expected errors.
  1182. * @param {string} evalStr The string to evaluate.
  1183. */
  1184. var shouldGenerateGLError = function(gl, glErrors, evalStr) {
  1185. var exception;
  1186. try {
  1187. eval(evalStr);
  1188. } catch (e) {
  1189. exception = e;
  1190. }
  1191. if (exception) {
  1192. testFailed(evalStr + " threw exception " + exception);
  1193. } else {
  1194. glErrorShouldBe(gl, glErrors, "after evaluating: " + evalStr);
  1195. }
  1196. };
  1197. /**
  1198. * Tests that the first error GL returns is the specified error.
  1199. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  1200. * @param {number|Array.<number>} glErrors The expected gl error or an array of expected errors.
  1201. * @param {string} opt_msg Optional additional message.
  1202. */
  1203. var glErrorShouldBe = function(gl, glErrors, opt_msg) {
  1204. if (!glErrors.length) {
  1205. glErrors = [glErrors];
  1206. }
  1207. opt_msg = opt_msg || "";
  1208. var err = gl.getError();
  1209. var ndx = glErrors.indexOf(err);
  1210. var errStrs = [];
  1211. for (var ii = 0; ii < glErrors.length; ++ii) {
  1212. errStrs.push(glEnumToString(gl, glErrors[ii]));
  1213. }
  1214. var expected = errStrs.join(" or ");
  1215. if (ndx < 0) {
  1216. var msg = "getError expected" + ((glErrors.length > 1) ? " one of: " : ": ");
  1217. testFailed(msg + expected + ". Was " + glEnumToString(gl, err) + " : " + opt_msg);
  1218. } else {
  1219. var msg = "getError was " + ((glErrors.length > 1) ? "one of: " : "expected value: ");
  1220. testPassed(msg + expected + " : " + opt_msg);
  1221. }
  1222. };
  1223. /**
  1224. * Links a WebGL program, throws if there are errors.
  1225. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  1226. * @param {!WebGLProgram} program The WebGLProgram to link.
  1227. * @param {function(string): void) opt_errorCallback callback for errors.
  1228. */
  1229. var linkProgram = function(gl, program, opt_errorCallback) {
  1230. var errFn = opt_errorCallback || testFailed;
  1231. // Link the program
  1232. gl.linkProgram(program);
  1233. // Check the link status
  1234. var linked = gl.getProgramParameter(program, gl.LINK_STATUS);
  1235. if (!linked) {
  1236. // something went wrong with the link
  1237. var error = gl.getProgramInfoLog (program);
  1238. errFn("Error in program linking:" + error);
  1239. gl.deleteProgram(program);
  1240. }
  1241. };
  1242. /**
  1243. * Loads text from an external file. This function is synchronous.
  1244. * @param {string} url The url of the external file.
  1245. * @param {!function(bool, string): void} callback that is sent a bool for
  1246. * success and the string.
  1247. */
  1248. var loadTextFileAsync = function(url, callback) {
  1249. log ("loading: " + url);
  1250. var error = 'loadTextFileSynchronous failed to load url "' + url + '"';
  1251. var request;
  1252. if (window.XMLHttpRequest) {
  1253. request = new XMLHttpRequest();
  1254. if (request.overrideMimeType) {
  1255. request.overrideMimeType('text/plain');
  1256. }
  1257. } else {
  1258. throw 'XMLHttpRequest is disabled';
  1259. }
  1260. try {
  1261. request.open('GET', url, true);
  1262. request.onreadystatechange = function() {
  1263. if (request.readyState == 4) {
  1264. var text = '';
  1265. // HTTP reports success with a 200 status. The file protocol reports
  1266. // success with zero. HTTP does not use zero as a status code (they
  1267. // start at 100).
  1268. // https://developer.mozilla.org/En/Using_XMLHttpRequest
  1269. var success = request.status == 200 || request.status == 0;
  1270. if (success) {
  1271. text = request.responseText;
  1272. }
  1273. log("loaded: " + url);
  1274. callback(success, text);
  1275. }
  1276. };
  1277. request.send(null);
  1278. } catch (e) {
  1279. log("failed to load: " + url);
  1280. callback(false, '');
  1281. }
  1282. };
  1283. /**
  1284. * Recursively loads a file as a list. Each line is parsed for a relative
  1285. * path. If the file ends in .txt the contents of that file is inserted in
  1286. * the list.
  1287. *
  1288. * @param {string} url The url of the external file.
  1289. * @param {!function(bool, Array<string>): void} callback that is sent a bool
  1290. * for success and the array of strings.
  1291. */
  1292. var getFileListAsync = function(url, callback) {
  1293. var files = [];
  1294. var getFileListImpl = function(url, callback) {
  1295. var files = [];
  1296. if (url.substr(url.length - 4) == '.txt') {
  1297. loadTextFileAsync(url, function() {
  1298. return function(success, text) {
  1299. if (!success) {
  1300. callback(false, '');
  1301. return;
  1302. }
  1303. var lines = text.split('\n');
  1304. var prefix = '';
  1305. var lastSlash = url.lastIndexOf('/');
  1306. if (lastSlash >= 0) {
  1307. prefix = url.substr(0, lastSlash + 1);
  1308. }
  1309. var fail = false;
  1310. var count = 1;
  1311. var index = 0;
  1312. for (var ii = 0; ii < lines.length; ++ii) {
  1313. var str = lines[ii].replace(/^\s\s*/, '').replace(/\s\s*$/, '');
  1314. if (str.length > 4 &&
  1315. str[0] != '#' &&
  1316. str[0] != ";" &&
  1317. str.substr(0, 2) != "//") {
  1318. var names = str.split(/ +/);
  1319. new_url = prefix + str;
  1320. if (names.length == 1) {
  1321. new_url = prefix + str;
  1322. ++count;
  1323. getFileListImpl(new_url, function(index) {
  1324. return function(success, new_files) {
  1325. log("got files: " + new_files.length);
  1326. if (success) {
  1327. files[index] = new_files;
  1328. }
  1329. finish(success);
  1330. };
  1331. }(index++));
  1332. } else {
  1333. var s = "";
  1334. var p = "";
  1335. for (var jj = 0; jj < names.length; ++jj) {
  1336. s += p + prefix + names[jj];
  1337. p = " ";
  1338. }
  1339. files[index++] = s;
  1340. }
  1341. }
  1342. }
  1343. finish(true);
  1344. function finish(success) {
  1345. if (!success) {
  1346. fail = true;
  1347. }
  1348. --count;
  1349. log("count: " + count);
  1350. if (!count) {
  1351. callback(!fail, files);
  1352. }
  1353. }
  1354. }
  1355. }());
  1356. } else {
  1357. files.push(url);
  1358. callback(true, files);
  1359. }
  1360. };
  1361. getFileListImpl(url, function(success, files) {
  1362. // flatten
  1363. var flat = [];
  1364. flatten(files);
  1365. function flatten(files) {
  1366. for (var ii = 0; ii < files.length; ++ii) {
  1367. var value = files[ii];
  1368. if (typeof(value) == "string") {
  1369. flat.push(value);
  1370. } else {
  1371. flatten(value);
  1372. }
  1373. }
  1374. }
  1375. callback(success, flat);
  1376. });
  1377. };
  1378. /**
  1379. * Gets a file from a file/URL.
  1380. * @param {string} file the URL of the file to get.
  1381. * @return {string} The contents of the file.
  1382. */
  1383. var readFile = function(file) {
  1384. var xhr = new XMLHttpRequest();
  1385. xhr.open("GET", file, false);
  1386. xhr.send();
  1387. return xhr.responseText.replace(/\r/g, "");
  1388. };
  1389. var readFileList = function(url) {
  1390. var files = [];
  1391. if (url.substr(url.length - 4) == '.txt') {
  1392. var lines = readFile(url).split('\n');
  1393. var prefix = '';
  1394. var lastSlash = url.lastIndexOf('/');
  1395. if (lastSlash >= 0) {
  1396. prefix = url.substr(0, lastSlash + 1);
  1397. }
  1398. for (var ii = 0; ii < lines.length; ++ii) {
  1399. var str = lines[ii].replace(/^\s\s*/, '').replace(/\s\s*$/, '');
  1400. if (str.length > 4 &&
  1401. str[0] != '#' &&
  1402. str[0] != ";" &&
  1403. str.substr(0, 2) != "//") {
  1404. var names = str.split(/ +/);
  1405. if (names.length == 1) {
  1406. new_url = prefix + str;
  1407. files = files.concat(readFileList(new_url));
  1408. } else {
  1409. var s = "";
  1410. var p = "";
  1411. for (var jj = 0; jj < names.length; ++jj) {
  1412. s += p + prefix + names[jj];
  1413. p = " ";
  1414. }
  1415. files.push(s);
  1416. }
  1417. }
  1418. }
  1419. } else {
  1420. files.push(url);
  1421. }
  1422. return files;
  1423. };
  1424. /**
  1425. * Loads a shader.
  1426. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  1427. * @param {string} shaderSource The shader source.
  1428. * @param {number} shaderType The type of shader.
  1429. * @param {function(string): void) opt_errorCallback callback for errors.
  1430. * @return {!WebGLShader} The created shader.
  1431. */
  1432. var loadShader = function(gl, shaderSource, shaderType, opt_errorCallback) {
  1433. var errFn = opt_errorCallback || error;
  1434. // Create the shader object
  1435. var shader = gl.createShader(shaderType);
  1436. if (shader == null) {
  1437. errFn("*** Error: unable to create shader '"+shaderSource+"'");
  1438. return null;
  1439. }
  1440. // Load the shader source
  1441. gl.shaderSource(shader, shaderSource);
  1442. var err = gl.getError();
  1443. if (err != gl.NO_ERROR) {
  1444. errFn("*** Error loading shader '" + shader + "':" + glEnumToString(gl, err));
  1445. return null;
  1446. }
  1447. // Compile the shader
  1448. gl.compileShader(shader);
  1449. // Check the compile status
  1450. var compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
  1451. if (!compiled) {
  1452. // Something went wrong during compilation; get the error
  1453. lastError = gl.getShaderInfoLog(shader);
  1454. errFn("*** Error compiling " + glEnumToString(gl, shaderType) + " '" + shader + "':" + lastError);
  1455. gl.deleteShader(shader);
  1456. return null;
  1457. }
  1458. return shader;
  1459. }
  1460. /**
  1461. * Loads a shader from a URL.
  1462. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  1463. * @param {file} file The URL of the shader source.
  1464. * @param {number} type The type of shader.
  1465. * @param {function(string): void) opt_errorCallback callback for errors.
  1466. * @return {!WebGLShader} The created shader.
  1467. */
  1468. var loadShaderFromFile = function(gl, file, type, opt_errorCallback) {
  1469. var shaderSource = readFile(file);
  1470. return loadShader(gl, shaderSource, type, opt_errorCallback);
  1471. };
  1472. /**
  1473. * Gets the content of script.
  1474. * @param {string} scriptId The id of the script tag.
  1475. * @return {string} The content of the script.
  1476. */
  1477. var getScript = function(scriptId) {
  1478. var shaderScript = document.getElementById(scriptId);
  1479. if (!shaderScript) {
  1480. throw("*** Error: unknown script element" + scriptId);
  1481. }
  1482. return shaderScript.text;
  1483. };
  1484. /**
  1485. * Loads a shader from a script tag.
  1486. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  1487. * @param {string} scriptId The id of the script tag.
  1488. * @param {number} opt_shaderType The type of shader. If not passed in it will
  1489. * be derived from the type of the script tag.
  1490. * @param {function(string): void) opt_errorCallback callback for errors.
  1491. * @return {!WebGLShader} The created shader.
  1492. */
  1493. var loadShaderFromScript = function(
  1494. gl, scriptId, opt_shaderType, opt_errorCallback) {
  1495. var shaderSource = "";
  1496. var shaderScript = document.getElementById(scriptId);
  1497. if (!shaderScript) {
  1498. throw("*** Error: unknown script element " + scriptId);
  1499. }
  1500. shaderSource = shaderScript.text;
  1501. if (!opt_shaderType) {
  1502. if (shaderScript.type == "x-shader/x-vertex") {
  1503. opt_shaderType = gl.VERTEX_SHADER;
  1504. } else if (shaderScript.type == "x-shader/x-fragment") {
  1505. opt_shaderType = gl.FRAGMENT_SHADER;
  1506. } else {
  1507. throw("*** Error: unknown shader type");
  1508. return null;
  1509. }
  1510. }
  1511. return loadShader(
  1512. gl, shaderSource, opt_shaderType, opt_errorCallback);
  1513. };
  1514. var loadStandardProgram = function(gl) {
  1515. var program = gl.createProgram();
  1516. gl.attachShader(program, loadStandardVertexShader(gl));
  1517. gl.attachShader(program, loadStandardFragmentShader(gl));
  1518. gl.bindAttribLocation(program, 0, "a_vertex");
  1519. gl.bindAttribLocation(program, 1, "a_normal");
  1520. linkProgram(gl, program);
  1521. return program;
  1522. };
  1523. /**
  1524. * Loads shaders from files, creates a program, attaches the shaders and links.
  1525. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  1526. * @param {string} vertexShaderPath The URL of the vertex shader.
  1527. * @param {string} fragmentShaderPath The URL of the fragment shader.
  1528. * @param {function(string): void) opt_errorCallback callback for errors.
  1529. * @return {!WebGLProgram} The created program.
  1530. */
  1531. var loadProgramFromFile = function(
  1532. gl, vertexShaderPath, fragmentShaderPath, opt_errorCallback) {
  1533. var program = gl.createProgram();
  1534. var vs = loadShaderFromFile(
  1535. gl, vertexShaderPath, gl.VERTEX_SHADER, opt_errorCallback);
  1536. var fs = loadShaderFromFile(
  1537. gl, fragmentShaderPath, gl.FRAGMENT_SHADER, opt_errorCallback);
  1538. if (vs && fs) {
  1539. gl.attachShader(program, vs);
  1540. gl.attachShader(program, fs);
  1541. linkProgram(gl, program, opt_errorCallback);
  1542. }
  1543. if (vs) {
  1544. gl.deleteShader(vs);
  1545. }
  1546. if (fs) {
  1547. gl.deleteShader(fs);
  1548. }
  1549. return program;
  1550. };
  1551. /**
  1552. * Loads shaders from script tags, creates a program, attaches the shaders and
  1553. * links.
  1554. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  1555. * @param {string} vertexScriptId The id of the script tag that contains the
  1556. * vertex shader.
  1557. * @param {string} fragmentScriptId The id of the script tag that contains the
  1558. * fragment shader.
  1559. * @param {function(string): void) opt_errorCallback callback for errors.
  1560. * @return {!WebGLProgram} The created program.
  1561. */
  1562. var loadProgramFromScript = function loadProgramFromScript(
  1563. gl, vertexScriptId, fragmentScriptId, opt_errorCallback) {
  1564. var program = gl.createProgram();
  1565. gl.attachShader(
  1566. program,
  1567. loadShaderFromScript(
  1568. gl, vertexScriptId, gl.VERTEX_SHADER, opt_errorCallback));
  1569. gl.attachShader(
  1570. program,
  1571. loadShaderFromScript(
  1572. gl, fragmentScriptId, gl.FRAGMENT_SHADER, opt_errorCallback));
  1573. linkProgram(gl, program, opt_errorCallback);
  1574. return program;
  1575. };
  1576. /**
  1577. * Loads shaders from source, creates a program, attaches the shaders and
  1578. * links.
  1579. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  1580. * @param {!WebGLShader} vertexShader The vertex shader.
  1581. * @param {!WebGLShader} fragmentShader The fragment shader.
  1582. * @param {function(string): void) opt_errorCallback callback for errors.
  1583. * @return {!WebGLProgram} The created program.
  1584. */
  1585. var createProgram = function(gl, vertexShader, fragmentShader, opt_errorCallback) {
  1586. var program = gl.createProgram();
  1587. gl.attachShader(program, vertexShader);
  1588. gl.attachShader(program, fragmentShader);
  1589. linkProgram(gl, program, opt_errorCallback);
  1590. return program;
  1591. };
  1592. /**
  1593. * Loads shaders from source, creates a program, attaches the shaders and
  1594. * links.
  1595. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  1596. * @param {string} vertexShader The vertex shader source.
  1597. * @param {string} fragmentShader The fragment shader source.
  1598. * @param {function(string): void) opt_errorCallback callback for errors.
  1599. * @return {!WebGLProgram} The created program.
  1600. */
  1601. var loadProgram = function(
  1602. gl, vertexShader, fragmentShader, opt_errorCallback) {
  1603. var program;
  1604. var vs = loadShader(
  1605. gl, vertexShader, gl.VERTEX_SHADER, opt_errorCallback);
  1606. var fs = loadShader(
  1607. gl, fragmentShader, gl.FRAGMENT_SHADER, opt_errorCallback);
  1608. if (vs && fs) {
  1609. program = createProgram(gl, vs, fs, opt_errorCallback)
  1610. }
  1611. if (vs) {
  1612. gl.deleteShader(vs);
  1613. }
  1614. if (fs) {
  1615. gl.deleteShader(fs);
  1616. }
  1617. return program;
  1618. };
  1619. /**
  1620. * Loads shaders from source, creates a program, attaches the shaders and
  1621. * links but expects error.
  1622. *
  1623. * GLSL 1.0.17 10.27 effectively says that compileShader can
  1624. * always succeed as long as linkProgram fails so we can't
  1625. * rely on compileShader failing. This function expects
  1626. * one of the shader to fail OR linking to fail.
  1627. *
  1628. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  1629. * @param {string} vertexShaderScriptId The vertex shader.
  1630. * @param {string} fragmentShaderScriptId The fragment shader.
  1631. * @return {WebGLProgram} The created program.
  1632. */
  1633. var loadProgramFromScriptExpectError = function(
  1634. gl, vertexShaderScriptId, fragmentShaderScriptId) {
  1635. var vertexShader = loadShaderFromScript(gl, vertexShaderScriptId);
  1636. if (!vertexShader) {
  1637. return null;
  1638. }
  1639. var fragmentShader = loadShaderFromScript(gl, fragmentShaderScriptId);
  1640. if (!fragmentShader) {
  1641. return null;
  1642. }
  1643. var linkSuccess = true;
  1644. var program = gl.createProgram();
  1645. gl.attachShader(program, vertexShader);
  1646. gl.attachShader(program, fragmentShader);
  1647. linkSuccess = true;
  1648. linkProgram(gl, program, function() {
  1649. linkSuccess = false;
  1650. });
  1651. return linkSuccess ? program : null;
  1652. };
  1653. var getActiveMap = function(gl, program, typeInfo) {
  1654. var numVariables = gl.getProgramParameter(program, gl[typeInfo.param]);
  1655. var variables = {};
  1656. for (var ii = 0; ii < numVariables; ++ii) {
  1657. var info = gl[typeInfo.activeFn](program, ii);
  1658. variables[info.name] = {
  1659. name: info.name,
  1660. size: info.size,
  1661. type: info.type,
  1662. location: gl[typeInfo.locFn](program, info.name)
  1663. };
  1664. }
  1665. return variables;
  1666. };
  1667. /**
  1668. * Returns a map of attrib names to info about those
  1669. * attribs.
  1670. *
  1671. * eg:
  1672. * { "attrib1Name":
  1673. * {
  1674. * name: "attrib1Name",
  1675. * size: 1,
  1676. * type: gl.FLOAT_MAT2,
  1677. * location: 0
  1678. * },
  1679. * "attrib2Name[0]":
  1680. * {
  1681. * name: "attrib2Name[0]",
  1682. * size: 4,
  1683. * type: gl.FLOAT,
  1684. * location: 1
  1685. * },
  1686. * }
  1687. *
  1688. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  1689. * @param {WebGLProgram} The program to query for attribs.
  1690. * @return the map.
  1691. */
  1692. var getAttribMap = function(gl, program) {
  1693. return getActiveMap(gl, program, {
  1694. param: "ACTIVE_ATTRIBUTES",
  1695. activeFn: "getActiveAttrib",
  1696. locFn: "getAttribLocation"
  1697. });
  1698. };
  1699. /**
  1700. * Returns a map of uniform names to info about those uniforms.
  1701. *
  1702. * eg:
  1703. * { "uniform1Name":
  1704. * {
  1705. * name: "uniform1Name",
  1706. * size: 1,
  1707. * type: gl.FLOAT_MAT2,
  1708. * location: WebGLUniformLocation
  1709. * },
  1710. * "uniform2Name[0]":
  1711. * {
  1712. * name: "uniform2Name[0]",
  1713. * size: 4,
  1714. * type: gl.FLOAT,
  1715. * location: WebGLUniformLocation
  1716. * },
  1717. * }
  1718. *
  1719. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  1720. * @param {WebGLProgram} The program to query for uniforms.
  1721. * @return the map.
  1722. */
  1723. var getUniformMap = function(gl, program) {
  1724. return getActiveMap(gl, program, {
  1725. param: "ACTIVE_UNIFORMS",
  1726. activeFn: "getActiveUniform",
  1727. locFn: "getUniformLocation"
  1728. });
  1729. };
  1730. var basePath;
  1731. var getBasePath = function() {
  1732. if (!basePath) {
  1733. var expectedBase = "webgl-test-utils.js";
  1734. var scripts = document.getElementsByTagName('script');
  1735. for (var script, i = 0; script = scripts[i]; i++) {
  1736. var src = script.src;
  1737. var l = src.length;
  1738. if (src.substr(l - expectedBase.length) == expectedBase) {
  1739. basePath = src.substr(0, l - expectedBase.length);
  1740. }
  1741. }
  1742. }
  1743. return basePath;
  1744. };
  1745. var loadStandardVertexShader = function(gl) {
  1746. return loadShaderFromFile(
  1747. gl, getBasePath() + "vertexShader.vert", gl.VERTEX_SHADER);
  1748. };
  1749. var loadStandardFragmentShader = function(gl) {
  1750. return loadShaderFromFile(
  1751. gl, getBasePath() + "fragmentShader.frag", gl.FRAGMENT_SHADER);
  1752. };
  1753. /**
  1754. * Loads an image asynchronously.
  1755. * @param {string} url URL of image to load.
  1756. * @param {!function(!Element): void} callback Function to call
  1757. * with loaded image.
  1758. */
  1759. var loadImageAsync = function(url, callback) {
  1760. var img = document.createElement('img');
  1761. img.onload = function() {
  1762. callback(img);
  1763. };
  1764. img.src = url;
  1765. };
  1766. /**
  1767. * Loads an array of images.
  1768. * @param {!Array.<string>} urls URLs of images to load.
  1769. * @param {!function(!{string, img}): void} callback. Callback
  1770. * that gets passed map of urls to img tags.
  1771. */
  1772. var loadImagesAsync = function(urls, callback) {
  1773. var count = 1;
  1774. var images = { };
  1775. function countDown() {
  1776. --count;
  1777. if (count == 0) {
  1778. callback(images);
  1779. }
  1780. }
  1781. function imageLoaded(url) {
  1782. return function(img) {
  1783. images[url] = img;
  1784. countDown();
  1785. }
  1786. }
  1787. for (var ii = 0; ii < urls.length; ++ii) {
  1788. ++count;
  1789. loadImageAsync(urls[ii], imageLoaded(urls[ii]));
  1790. }
  1791. countDown();
  1792. };
  1793. /**
  1794. * Returns a map of key=value values from url.
  1795. * @return {!Object.<string, number>} map of keys to values.
  1796. */
  1797. var getUrlArguments = function() {
  1798. var args = {};
  1799. try {
  1800. var s = window.location.href;
  1801. var q = s.indexOf("?");
  1802. var e = s.indexOf("#");
  1803. if (e < 0) {
  1804. e = s.length;
  1805. }
  1806. var query = s.substring(q + 1, e);
  1807. var pairs = query.split("&");
  1808. for (var ii = 0; ii < pairs.length; ++ii) {
  1809. var keyValue = pairs[ii].split("=");
  1810. var key = keyValue[0];
  1811. var value = decodeURIComponent(keyValue[1]);
  1812. args[key] = value;
  1813. }
  1814. } catch (e) {
  1815. throw "could not parse url";
  1816. }
  1817. return args;
  1818. };
  1819. /**
  1820. * Makes an image from a canvas.
  1821. * @param {!HTMLCanvas} canvas Canvas to make image from.
  1822. * @return {!Image} The created image.
  1823. */
  1824. var makeImage = function(canvas) {
  1825. var img = document.createElement('img');
  1826. img.src = canvas.toDataURL();
  1827. return img;
  1828. };
  1829. /**
  1830. * Inserts an image with a caption into 'element'.
  1831. * @param {!HTMLElement} element Element to append image to.
  1832. * @param {string} caption caption to associate with image.
  1833. * @param {!Image) img image to insert.
  1834. */
  1835. var insertImage = function(element, caption, img) {
  1836. var div = document.createElement("div");
  1837. div.appendChild(img);
  1838. var label = document.createElement("div");
  1839. label.appendChild(document.createTextNode(caption));
  1840. div.appendChild(label);
  1841. element.appendChild(div);
  1842. };
  1843. /**
  1844. * Inserts a 'label' that when clicked expands to the pre
  1845. * formatted text supplied by 'source'.
  1846. * @param {!HTMLElement} element element to append label to.
  1847. * @param {string} label label for anchor.
  1848. * @param {string} source preformatted text to expand to.
  1849. * @param {string} opt_url url of source. If provided a 2nd link
  1850. * will be added.
  1851. */
  1852. var addShaderSource = function(element, label, source, opt_url) {
  1853. var div = document.createElement("div");
  1854. var s = document.createElement("pre");
  1855. s.className = "shader-source";
  1856. s.style.display = "none";
  1857. var ol = document.createElement("ol");
  1858. //s.appendChild(document.createTextNode(source));
  1859. var lines = source.split("\n");
  1860. for (var ii = 0; ii < lines.length; ++ii) {
  1861. var line = lines[ii];
  1862. var li = document.createElement("li");
  1863. li.appendChild(document.createTextNode(line));
  1864. ol.appendChild(li);
  1865. }
  1866. s.appendChild(ol);
  1867. var l = document.createElement("a");
  1868. l.href = "show-shader-source";
  1869. l.appendChild(document.createTextNode(label));
  1870. l.addEventListener('click', function(event) {
  1871. if (event.preventDefault) {
  1872. event.preventDefault();
  1873. }
  1874. s.style.display = (s.style.display == 'none') ? 'block' : 'none';
  1875. return false;
  1876. }, false);
  1877. div.appendChild(l);
  1878. if (opt_url) {
  1879. var u = document.createElement("a");
  1880. u.href = opt_url;
  1881. div.appendChild(document.createTextNode(" "));
  1882. u.appendChild(document.createTextNode("(" + opt_url + ")"));
  1883. div.appendChild(u);
  1884. }
  1885. div.appendChild(s);
  1886. element.appendChild(div);
  1887. };
  1888. // Add your prefix here.
  1889. var browserPrefixes = [
  1890. "",
  1891. "MOZ_",
  1892. "OP_",
  1893. "WEBKIT_"
  1894. ];
  1895. /**
  1896. * Given an extension name like WEBGL_compressed_texture_s3tc
  1897. * returns the name of the supported version extension, like
  1898. * WEBKIT_WEBGL_compressed_teture_s3tc
  1899. * @param {string} name Name of extension to look for.
  1900. * @return {string} name of extension found or undefined if not
  1901. * found.
  1902. */
  1903. var getSupportedExtensionWithKnownPrefixes = function(gl, name) {
  1904. var supported = gl.getSupportedExtensions();
  1905. for (var ii = 0; ii < browserPrefixes.length; ++ii) {
  1906. var prefixedName = browserPrefixes[ii] + name;
  1907. if (supported.indexOf(prefixedName) >= 0) {
  1908. return prefixedName;
  1909. }
  1910. }
  1911. };
  1912. /**
  1913. * Given an extension name like WEBGL_compressed_texture_s3tc
  1914. * returns the supported version extension, like
  1915. * WEBKIT_WEBGL_compressed_teture_s3tc
  1916. * @param {string} name Name of extension to look for.
  1917. * @return {WebGLExtension} The extension or undefined if not
  1918. * found.
  1919. */
  1920. var getExtensionWithKnownPrefixes = function(gl, name) {
  1921. for (var ii = 0; ii < browserPrefixes.length; ++ii) {
  1922. var prefixedName = browserPrefixes[ii] + name;
  1923. var ext = gl.getExtension(prefixedName);
  1924. if (ext) {
  1925. return ext;
  1926. }
  1927. }
  1928. };
  1929. var replaceRE = /\$\((\w+)\)/g;
  1930. /**
  1931. * Replaces strings with property values.
  1932. * Given a string like "hello $(first) $(last)" and an object
  1933. * like {first:"John", last:"Smith"} will return
  1934. * "hello John Smith".
  1935. * @param {string} str String to do replacements in.
  1936. * @param {...} 1 or more objects containing properties.
  1937. */
  1938. var replaceParams = function(str) {
  1939. var args = arguments;
  1940. return str.replace(replaceRE, function(str, p1, offset, s) {
  1941. for (var ii = 1; ii < args.length; ++ii) {
  1942. if (args[ii][p1] !== undefined) {
  1943. return args[ii][p1];
  1944. }
  1945. }
  1946. throw "unknown string param '" + p1 + "'";
  1947. });
  1948. };
  1949. var upperCaseFirstLetter = function(str) {
  1950. return str.substring(0, 1).toUpperCase() + str.substring(1);
  1951. };
  1952. /**
  1953. * Gets a prefixed property. For example,
  1954. *
  1955. * var fn = getPrefixedProperty(
  1956. * window,
  1957. * "requestAnimationFrame");
  1958. *
  1959. * Will return either:
  1960. * "window.requestAnimationFrame",
  1961. * "window.oRequestAnimationFrame",
  1962. * "window.msRequestAnimationFrame",
  1963. * "window.mozRequestAnimationFrame",
  1964. * "window.webKitRequestAnimationFrame",
  1965. * undefined
  1966. *
  1967. * the non-prefixed function is tried first.
  1968. */
  1969. var propertyPrefixes = ["", "moz", "ms", "o", "webkit"];
  1970. var getPrefixedProperty = function(obj, propertyName) {
  1971. for (var ii = 0; ii < propertyPrefixes.length; ++ii) {
  1972. var prefix = propertyPrefixes[ii];
  1973. var name = prefix + propertyName;
  1974. console.log(name);
  1975. var property = obj[name];
  1976. if (property) {
  1977. return property;
  1978. }
  1979. if (ii == 0) {
  1980. propertyName = upperCaseFirstLetter(propertyName);
  1981. }
  1982. }
  1983. return undefined;
  1984. };
  1985. var _requestAnimFrame;
  1986. /**
  1987. * Provides requestAnimationFrame in a cross browser way.
  1988. */
  1989. var requestAnimFrame = function(callback) {
  1990. if (!_requestAnimFrame) {
  1991. _requestAnimFrame = getPrefixedProperty(window, "requestAnimationFrame") ||
  1992. function(callback, element) {
  1993. return window.setTimeout(callback, 1000 / 70);
  1994. };
  1995. }
  1996. _requestAnimFrame.call(window, callback);
  1997. };
  1998. var _cancelAnimFrame;
  1999. /**
  2000. * Provides cancelAnimationFrame in a cross browser way.
  2001. */
  2002. var cancelAnimFrame = function(request) {
  2003. if (!_cancelAnimFrame) {
  2004. _cancelAnimFrame = getPrefixedProperty(window, "cancelAnimationFrame") ||
  2005. window.clearTimeout;
  2006. }
  2007. _cancelAnimFrame.call(window, request);
  2008. };
  2009. /**
  2010. * Provides requestFullScreen in a cross browser way.
  2011. */
  2012. var requestFullScreen = function(element) {
  2013. var fn = getPrefixedProperty(element, "requestFullScreen");
  2014. if (fn) {
  2015. fn.call(element);
  2016. }
  2017. };
  2018. /**
  2019. * Provides cancelFullScreen in a cross browser way.
  2020. */
  2021. var cancelFullScreen = function() {
  2022. var fn = getPrefixedProperty(document, "cancelFullScreen");
  2023. if (fn) {
  2024. fn.call(document);
  2025. }
  2026. };
  2027. var fullScreenStateName;
  2028. (function() {
  2029. var fullScreenStateNames = [
  2030. "isFullScreen",
  2031. "fullScreen",
  2032. ];
  2033. for (var ii = 0; ii < fullScreenStateNames.length; ++ii) {
  2034. var propertyName = fullScreenStateNames[ii];
  2035. for (var jj = 0; jj < propertyPrefixes.length; ++jj) {
  2036. var prefix = propertyPrefixes[jj];
  2037. if (prefix.length) {
  2038. propertyName = upperCaseFirstLetter(propertyName);
  2039. fullScreenStateName = prefix + propertyName;
  2040. if (document[fullScreenStateName] !== undefined) {
  2041. return;
  2042. }
  2043. }
  2044. }
  2045. fullScreenStateName = undefined;
  2046. }
  2047. }());
  2048. /**
  2049. * @return {boolean} True if fullscreen mode is active.
  2050. */
  2051. var getFullScreenState = function() {
  2052. console.log("fullscreenstatename:" + fullScreenStateName);
  2053. console.log(document[fullScreenStateName]);
  2054. return document[fullScreenStateName];
  2055. };
  2056. /**
  2057. * @param {!HTMLElement} element The element to go fullscreen.
  2058. * @param {!function(boolean)} callback A function that will be called
  2059. * when entering/exiting fullscreen. It is passed true if
  2060. * entering fullscreen, false if exiting.
  2061. */
  2062. var onFullScreenChange = function(element, callback) {
  2063. propertyPrefixes.forEach(function(prefix) {
  2064. var eventName = prefix + "fullscreenchange";
  2065. console.log("addevent: " + eventName);
  2066. document.addEventListener(eventName, function(event) {
  2067. console.log("event: " + eventName);
  2068. callback(getFullScreenState());
  2069. });
  2070. });
  2071. };
  2072. /**
  2073. * @param {!string} buttonId The id of the button that will toggle fullscreen
  2074. * mode.
  2075. * @param {!string} fullscreenId The id of the element to go fullscreen.
  2076. * @param {!function(boolean)} callback A function that will be called
  2077. * when entering/exiting fullscreen. It is passed true if
  2078. * entering fullscreen, false if exiting.
  2079. * @return {boolean} True if fullscreen mode is supported.
  2080. */
  2081. var setupFullscreen = function(buttonId, fullscreenId, callback) {
  2082. if (!fullScreenStateName) {
  2083. return false;
  2084. }
  2085. var fullscreenElement = document.getElementById(fullscreenId);
  2086. onFullScreenChange(fullscreenElement, callback);
  2087. var toggleFullScreen = function(event) {
  2088. if (getFullScreenState()) {
  2089. cancelFullScreen(fullscreenElement);
  2090. } else {
  2091. requestFullScreen(fullscreenElement);
  2092. }
  2093. event.preventDefault();
  2094. return false;
  2095. };
  2096. var buttonElement = document.getElementById(buttonId);
  2097. buttonElement.addEventListener('click', toggleFullScreen);
  2098. return true;
  2099. };
  2100. /**
  2101. * Waits for the browser to composite the canvas associated with
  2102. * the WebGL context passed in.
  2103. * @param {WebGLRenderingContext} gl The WebGLRenderingContext to use.
  2104. * @param {function()} callback A function to call after compositing has taken
  2105. * place.
  2106. */
  2107. var waitForComposite = function(gl, callback) {
  2108. var frames = 5;
  2109. var countDown = function() {
  2110. if (frames == 0) {
  2111. callback();
  2112. } else {
  2113. --frames;
  2114. requestAnimFrame.call(window, countDown);
  2115. }
  2116. };
  2117. countDown();
  2118. };
  2119. /**
  2120. * Runs an array of functions, yielding to the browser between each step.
  2121. * If you want to know when all the steps are finished add a last step.
  2122. * @param {!Array.<function(): void>} steps. Array of functions.
  2123. */
  2124. var runSteps = function(steps) {
  2125. if (!steps.length) {
  2126. return;
  2127. }
  2128. // copy steps so they can't be modifed.
  2129. var stepsToRun = steps.slice();
  2130. var currentStep = 0;
  2131. var runNextStep = function() {
  2132. stepsToRun[currentStep++]();
  2133. if (currentStep < stepsToRun.length) {
  2134. setTimeout(runNextStep, 1);
  2135. }
  2136. };
  2137. runNextStep();
  2138. };
  2139. /**
  2140. * Starts playing a video and waits for it to be consumable.
  2141. * @param {!HTMLVideoElement} video An HTML5 Video element.
  2142. * @param {!function(!HTMLVideoElement): void>} callback Function to call when
  2143. * video is ready.
  2144. */
  2145. var startPlayingAndWaitForVideo = function(video, callback) {
  2146. var gotPlaying = false;
  2147. var gotTimeUpdate = false;
  2148. var maybeCallCallback = function() {
  2149. if (gotPlaying && gotTimeUpdate && callback) {
  2150. callback(video);
  2151. callback = undefined;
  2152. video.removeEventListener('playing', playingListener, true);
  2153. video.removeEventListener('timeupdate', timeupdateListener, true);
  2154. }
  2155. };
  2156. var playingListener = function() {
  2157. gotPlaying = true;
  2158. maybeCallCallback();
  2159. };
  2160. var timeupdateListener = function() {
  2161. // Checking to make sure the current time has advanced beyond
  2162. // the start time seems to be a reliable heuristic that the
  2163. // video element has data that can be consumed.
  2164. if (video.currentTime > 0.0) {
  2165. gotTimeUpdate = true;
  2166. maybeCallCallback();
  2167. }
  2168. };
  2169. video.addEventListener('playing', playingListener, true);
  2170. video.addEventListener('timeupdate', timeupdateListener, true);
  2171. video.loop = true;
  2172. video.play();
  2173. };
  2174. return {
  2175. addShaderSource: addShaderSource,
  2176. cancelAnimFrame: cancelAnimFrame,
  2177. create3DContext: create3DContext,
  2178. create3DContextWithWrapperThatThrowsOnGLError:
  2179. create3DContextWithWrapperThatThrowsOnGLError,
  2180. checkAreaInAndOut: checkAreaInAndOut,
  2181. checkCanvas: checkCanvas,
  2182. checkCanvasRect: checkCanvasRect,
  2183. checkCanvasRectColor: checkCanvasRectColor,
  2184. clipToRange: clipToRange,
  2185. createColoredTexture: createColoredTexture,
  2186. createProgram: createProgram,
  2187. clearAndDrawUnitQuad: clearAndDrawUnitQuad,
  2188. clearAndDrawIndexedQuad: clearAndDrawIndexedQuad,
  2189. drawUnitQuad: drawUnitQuad,
  2190. drawIndexedQuad: drawIndexedQuad,
  2191. drawUByteColorQuad: drawUByteColorQuad,
  2192. drawFloatColorQuad: drawFloatColorQuad,
  2193. endsWith: endsWith,
  2194. fillTexture: fillTexture,
  2195. getBytesPerComponent: getBytesPerComponent,
  2196. getExtensionWithKnownPrefixes: getExtensionWithKnownPrefixes,
  2197. getFileListAsync: getFileListAsync,
  2198. getLastError: getLastError,
  2199. getPrefixedProperty: getPrefixedProperty,
  2200. getScript: getScript,
  2201. getSupportedExtensionWithKnownPrefixes: getSupportedExtensionWithKnownPrefixes,
  2202. getUrlArguments: getUrlArguments,
  2203. getUrlOptions: getUrlOptions,
  2204. getAttribMap: getAttribMap,
  2205. getUniformMap: getUniformMap,
  2206. glEnumToString: glEnumToString,
  2207. glErrorShouldBe: glErrorShouldBe,
  2208. glTypeToArrayBufferType: glTypeToArrayBufferType,
  2209. hasAttributeCaseInsensitive: hasAttributeCaseInsensitive,
  2210. insertImage: insertImage,
  2211. loadImageAsync: loadImageAsync,
  2212. loadImagesAsync: loadImagesAsync,
  2213. loadProgram: loadProgram,
  2214. loadProgramFromFile: loadProgramFromFile,
  2215. loadProgramFromScript: loadProgramFromScript,
  2216. loadProgramFromScriptExpectError: loadProgramFromScriptExpectError,
  2217. loadShader: loadShader,
  2218. loadShaderFromFile: loadShaderFromFile,
  2219. loadShaderFromScript: loadShaderFromScript,
  2220. loadStandardProgram: loadStandardProgram,
  2221. loadStandardVertexShader: loadStandardVertexShader,
  2222. loadStandardFragmentShader: loadStandardFragmentShader,
  2223. loadTextFileAsync: loadTextFileAsync,
  2224. loadTexture: loadTexture,
  2225. log: log,
  2226. loggingOff: loggingOff,
  2227. makeImage: makeImage,
  2228. error: error,
  2229. shallowCopyObject: shallowCopyObject,
  2230. setupColorQuad: setupColorQuad,
  2231. setupProgram: setupProgram,
  2232. setupQuad: setupQuad,
  2233. setupIndexedQuad: setupIndexedQuad,
  2234. setupIndexedQuadWithOptions: setupIndexedQuadWithOptions,
  2235. setupSimpleColorFragmentShader: setupSimpleColorFragmentShader,
  2236. setupSimpleColorVertexShader: setupSimpleColorVertexShader,
  2237. setupSimpleColorProgram: setupSimpleColorProgram,
  2238. setupSimpleTextureFragmentShader: setupSimpleTextureFragmentShader,
  2239. setupSimpleTextureProgram: setupSimpleTextureProgram,
  2240. setupSimpleTextureVertexShader: setupSimpleTextureVertexShader,
  2241. setupSimpleVertexColorFragmentShader: setupSimpleVertexColorFragmentShader,
  2242. setupSimpleVertexColorProgram: setupSimpleVertexColorProgram,
  2243. setupSimpleVertexColorVertexShader: setupSimpleVertexColorVertexShader,
  2244. setupNoTexCoordTextureProgram: setupNoTexCoordTextureProgram,
  2245. setupNoTexCoordTextureVertexShader: setupNoTexCoordTextureVertexShader,
  2246. setupTexturedQuad: setupTexturedQuad,
  2247. setupTexturedQuadWithTexCoords: setupTexturedQuadWithTexCoords,
  2248. setupUnitQuad: setupUnitQuad,
  2249. setupUnitQuadWithTexCoords: setupUnitQuadWithTexCoords,
  2250. setFloatDrawColor: setFloatDrawColor,
  2251. setUByteDrawColor: setUByteDrawColor,
  2252. startPlayingAndWaitForVideo: startPlayingAndWaitForVideo,
  2253. startsWith: startsWith,
  2254. shouldGenerateGLError: shouldGenerateGLError,
  2255. readFile: readFile,
  2256. readFileList: readFileList,
  2257. replaceParams: replaceParams,
  2258. requestAnimFrame: requestAnimFrame,
  2259. runSteps: runSteps,
  2260. waitForComposite: waitForComposite,
  2261. // fullscreen api
  2262. setupFullscreen: setupFullscreen,
  2263. none: false
  2264. };
  2265. }());