PageRenderTime 66ms CodeModel.GetById 27ms 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

Large files files are truncated, but you can click here to view the full 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(shaderTyp

Large files files are truncated, but you can click here to view the full file