PageRenderTime 65ms CodeModel.GetById 31ms RepoModel.GetById 0ms app.codeStats 1ms

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

https://github.com/gabadie/WebGL
JavaScript | 2402 lines | 1488 code | 161 blank | 753 comment | 250 complexity | e583072b656306ab937c7af41d828109 MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-2.1

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. for (var p in gl) {
  63. if (gl[p] == value) {
  64. return p;
  65. }
  66. }
  67. return "0x" + value.toString(16);
  68. };
  69. var lastError = "";
  70. /**
  71. * Returns the last compiler/linker error.
  72. * @return {string} The last compiler/linker error.
  73. */
  74. var getLastError = function() {
  75. return lastError;
  76. };
  77. /**
  78. * Whether a haystack ends with a needle.
  79. * @param {string} haystack String to search
  80. * @param {string} needle String to search for.
  81. * @param {boolean} True if haystack ends with needle.
  82. */
  83. var endsWith = function(haystack, needle) {
  84. return haystack.substr(haystack.length - needle.length) === needle;
  85. };
  86. /**
  87. * Whether a haystack starts with a needle.
  88. * @param {string} haystack String to search
  89. * @param {string} needle String to search for.
  90. * @param {boolean} True if haystack starts with needle.
  91. */
  92. var startsWith = function(haystack, needle) {
  93. return haystack.substr(0, needle.length) === needle;
  94. };
  95. /**
  96. * A vertex shader for a single texture.
  97. * @type {string}
  98. */
  99. var simpleTextureVertexShader = [
  100. 'attribute vec4 vPosition;',
  101. 'attribute vec2 texCoord0;',
  102. 'varying vec2 texCoord;',
  103. 'void main() {',
  104. ' gl_Position = vPosition;',
  105. ' texCoord = texCoord0;',
  106. '}'].join('\n');
  107. /**
  108. * A fragment shader for a single texture.
  109. * @type {string}
  110. */
  111. var simpleTextureFragmentShader = [
  112. 'precision mediump float;',
  113. 'uniform sampler2D tex;',
  114. 'varying vec2 texCoord;',
  115. 'void main() {',
  116. ' gl_FragData[0] = texture2D(tex, texCoord);',
  117. '}'].join('\n');
  118. /**
  119. * A vertex shader for a single texture.
  120. * @type {string}
  121. */
  122. var noTexCoordTextureVertexShader = [
  123. 'attribute vec4 vPosition;',
  124. 'varying vec2 texCoord;',
  125. 'void main() {',
  126. ' gl_Position = vPosition;',
  127. ' texCoord = vPosition.xy * 0.5 + 0.5;',
  128. '}'].join('\n');
  129. /**
  130. * A vertex shader for a uniform color.
  131. * @type {string}
  132. */
  133. var simpleColorVertexShader = [
  134. 'attribute vec4 vPosition;',
  135. 'void main() {',
  136. ' gl_Position = vPosition;',
  137. '}'].join('\n');
  138. /**
  139. * A fragment shader for a uniform color.
  140. * @type {string}
  141. */
  142. var simpleColorFragmentShader = [
  143. 'precision mediump float;',
  144. 'uniform vec4 u_color;',
  145. 'void main() {',
  146. ' gl_FragData[0] = u_color;',
  147. '}'].join('\n');
  148. /**
  149. * A vertex shader for vertex colors.
  150. * @type {string}
  151. */
  152. var simpleVertexColorVertexShader = [
  153. 'attribute vec4 vPosition;',
  154. 'attribute vec4 a_color;',
  155. 'varying vec4 v_color;',
  156. 'void main() {',
  157. ' gl_Position = vPosition;',
  158. ' v_color = a_color;',
  159. '}'].join('\n');
  160. /**
  161. * A fragment shader for vertex colors.
  162. * @type {string}
  163. */
  164. var simpleVertexColorFragmentShader = [
  165. 'precision mediump float;',
  166. 'varying vec4 v_color;',
  167. 'void main() {',
  168. ' gl_FragData[0] = v_color;',
  169. '}'].join('\n');
  170. /**
  171. * Creates a simple texture vertex shader.
  172. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  173. * @return {!WebGLShader}
  174. */
  175. var setupSimpleTextureVertexShader = function(gl) {
  176. return loadShader(gl, simpleTextureVertexShader, gl.VERTEX_SHADER);
  177. };
  178. /**
  179. * Creates a simple texture fragment shader.
  180. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  181. * @return {!WebGLShader}
  182. */
  183. var setupSimpleTextureFragmentShader = function(gl) {
  184. return loadShader(
  185. gl, simpleTextureFragmentShader, gl.FRAGMENT_SHADER);
  186. };
  187. /**
  188. * Creates a texture vertex shader that doesn't need texcoords.
  189. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  190. * @return {!WebGLShader}
  191. */
  192. var setupNoTexCoordTextureVertexShader = function(gl) {
  193. return loadShader(gl, noTexCoordTextureVertexShader, gl.VERTEX_SHADER);
  194. };
  195. /**
  196. * Creates a simple vertex color vertex shader.
  197. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  198. * @return {!WebGLShader}
  199. */
  200. var setupSimpleVertexColorVertexShader = function(gl) {
  201. return loadShader(gl, simpleVertexColorVertexShader, gl.VERTEX_SHADER);
  202. };
  203. /**
  204. * Creates a simple vertex color fragment shader.
  205. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  206. * @return {!WebGLShader}
  207. */
  208. var setupSimpleVertexColorFragmentShader = function(gl) {
  209. return loadShader(
  210. gl, simpleVertexColorFragmentShader, gl.FRAGMENT_SHADER);
  211. };
  212. /**
  213. * Creates a simple color vertex shader.
  214. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  215. * @return {!WebGLShader}
  216. */
  217. var setupSimpleColorVertexShader = function(gl) {
  218. return loadShader(gl, simpleColorVertexShader, gl.VERTEX_SHADER);
  219. };
  220. /**
  221. * Creates a simple color fragment shader.
  222. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  223. * @return {!WebGLShader}
  224. */
  225. var setupSimpleColorFragmentShader = function(gl) {
  226. return loadShader(
  227. gl, simpleColorFragmentShader, gl.FRAGMENT_SHADER);
  228. };
  229. /**
  230. * Creates a program, attaches shaders, binds attrib locations, links the
  231. * program and calls useProgram.
  232. * @param {!Array.<!WebGLShader|string>} shaders The shaders to
  233. * attach, or the source, or the id of a script to get
  234. * the source from.
  235. * @param {!Array.<string>} opt_attribs The attribs names.
  236. * @param {!Array.<number>} opt_locations The locations for the attribs.
  237. */
  238. var setupProgram = function(gl, shaders, opt_attribs, opt_locations) {
  239. var realShaders = [];
  240. var program = gl.createProgram();
  241. var shaderType = undefined;
  242. var shaderCount = 0;
  243. for (var ii = 0; ii < shaders.length; ++ii) {
  244. var shader = shaders[ii];
  245. if (typeof shader == 'string') {
  246. var element = document.getElementById(shader);
  247. if (element) {
  248. if (element.type != "x-shader/x-vertex" && element.type != "x-shader/x-fragment")
  249. shaderType = ii ? gl.FRAGMENT_SHADER : gl.VERTEX_SHADER;
  250. shader = loadShaderFromScript(gl, shader, shaderType);
  251. } else if (endsWith(shader, ".vert")) {
  252. shader = loadShaderFromFile(gl, shader, gl.VERTEX_SHADER);
  253. } else if (endsWith(shader, ".frag")) {
  254. shader = loadShaderFromFile(gl, shader, gl.FRAGMENT_SHADER);
  255. } else {
  256. shader = loadShader(gl, shader, ii ? gl.FRAGMENT_SHADER : gl.VERTEX_SHADER);
  257. }
  258. }
  259. if (shader) {
  260. ++shaderCount;
  261. gl.attachShader(program, shader);
  262. }
  263. }
  264. if (shaderCount != 2) {
  265. error("Error in compiling shader");
  266. return null;
  267. }
  268. if (opt_attribs) {
  269. for (var ii = 0; ii < opt_attribs.length; ++ii) {
  270. gl.bindAttribLocation(
  271. program,
  272. opt_locations ? opt_locations[ii] : ii,
  273. opt_attribs[ii]);
  274. }
  275. }
  276. gl.linkProgram(program);
  277. // Check the link status
  278. var linked = gl.getProgramParameter(program, gl.LINK_STATUS);
  279. if (!linked) {
  280. // something went wrong with the link
  281. lastError = gl.getProgramInfoLog (program);
  282. error("Error in program linking:" + lastError);
  283. gl.deleteProgram(program);
  284. return null;
  285. }
  286. gl.useProgram(program);
  287. return program;
  288. };
  289. /**
  290. * Creates a simple texture program.
  291. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  292. * @param {number} opt_positionLocation The attrib location for position.
  293. * @param {number} opt_texcoordLocation The attrib location for texture coords.
  294. * @return {WebGLProgram}
  295. */
  296. var setupSimpleTextureProgram = function(
  297. gl, opt_positionLocation, opt_texcoordLocation) {
  298. opt_positionLocation = opt_positionLocation || 0;
  299. opt_texcoordLocation = opt_texcoordLocation || 1;
  300. var vs = setupSimpleTextureVertexShader(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', 'texCoord0'],
  309. [opt_positionLocation, opt_texcoordLocation]);
  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. * @return {WebGLProgram}
  321. */
  322. var setupNoTexCoordTextureProgram = function(gl) {
  323. var vs = setupNoTexCoordTextureVertexShader(gl);
  324. var fs = setupSimpleTextureFragmentShader(gl);
  325. if (!vs || !fs) {
  326. return null;
  327. }
  328. var program = setupProgram(
  329. gl,
  330. [vs, fs],
  331. ['vPosition'],
  332. [0]);
  333. if (!program) {
  334. gl.deleteShader(fs);
  335. gl.deleteShader(vs);
  336. }
  337. gl.useProgram(program);
  338. return program;
  339. };
  340. /**
  341. * Creates a simple texture program.
  342. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  343. * @param {number} opt_positionLocation The attrib location for position.
  344. * @param {number} opt_texcoordLocation The attrib location for texture coords.
  345. * @return {WebGLProgram}
  346. */
  347. var setupSimpleTextureProgram = function(
  348. gl, opt_positionLocation, opt_texcoordLocation) {
  349. opt_positionLocation = opt_positionLocation || 0;
  350. opt_texcoordLocation = opt_texcoordLocation || 1;
  351. var vs = setupSimpleTextureVertexShader(gl);
  352. var fs = setupSimpleTextureFragmentShader(gl);
  353. if (!vs || !fs) {
  354. return null;
  355. }
  356. var program = setupProgram(
  357. gl,
  358. [vs, fs],
  359. ['vPosition', 'texCoord0'],
  360. [opt_positionLocation, opt_texcoordLocation]);
  361. if (!program) {
  362. gl.deleteShader(fs);
  363. gl.deleteShader(vs);
  364. }
  365. gl.useProgram(program);
  366. return program;
  367. };
  368. /**
  369. * Creates a simple vertex color program.
  370. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  371. * @param {number} opt_positionLocation The attrib location for position.
  372. * @param {number} opt_vertexColorLocation The attrib location
  373. * for vertex colors.
  374. * @return {WebGLProgram}
  375. */
  376. var setupSimpleVertexColorProgram = function(
  377. gl, opt_positionLocation, opt_vertexColorLocation) {
  378. opt_positionLocation = opt_positionLocation || 0;
  379. opt_vertexColorLocation = opt_vertexColorLocation || 1;
  380. var vs = setupSimpleVertexColorVertexShader(gl);
  381. var fs = setupSimpleVertexColorFragmentShader(gl);
  382. if (!vs || !fs) {
  383. return null;
  384. }
  385. var program = setupProgram(
  386. gl,
  387. [vs, fs],
  388. ['vPosition', 'a_color'],
  389. [opt_positionLocation, opt_vertexColorLocation]);
  390. if (!program) {
  391. gl.deleteShader(fs);
  392. gl.deleteShader(vs);
  393. }
  394. gl.useProgram(program);
  395. return program;
  396. };
  397. /**
  398. * Creates a simple color program.
  399. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  400. * @param {number} opt_positionLocation The attrib location for position.
  401. * @return {WebGLProgram}
  402. */
  403. var setupSimpleColorProgram = function(gl, opt_positionLocation) {
  404. opt_positionLocation = opt_positionLocation || 0;
  405. var vs = setupSimpleColorVertexShader(gl);
  406. var fs = setupSimpleColorFragmentShader(gl);
  407. if (!vs || !fs) {
  408. return null;
  409. }
  410. var program = setupProgram(
  411. gl,
  412. [vs, fs],
  413. ['vPosition'],
  414. [opt_positionLocation]);
  415. if (!program) {
  416. gl.deleteShader(fs);
  417. gl.deleteShader(vs);
  418. }
  419. gl.useProgram(program);
  420. return program;
  421. };
  422. /**
  423. * Creates buffers for a textured unit quad and attaches them to vertex attribs.
  424. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  425. * @param {number} opt_positionLocation The attrib location for position.
  426. * @param {number} opt_texcoordLocation The attrib location for texture coords.
  427. * @return {!Array.<WebGLBuffer>} The buffer objects that were
  428. * created.
  429. */
  430. var setupUnitQuad = function(gl, opt_positionLocation, opt_texcoordLocation) {
  431. return setupUnitQuadWithTexCoords(gl, [ 0.0, 0.0 ], [ 1.0, 1.0 ],
  432. opt_positionLocation, opt_texcoordLocation);
  433. };
  434. /**
  435. * Creates buffers for a textured unit quad with specified lower left
  436. * and upper right texture coordinates, and attaches them to vertex
  437. * attribs.
  438. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  439. * @param {!Array.<number>} lowerLeftTexCoords The texture coordinates for the lower left corner.
  440. * @param {!Array.<number>} upperRightTexCoords The texture coordinates for the upper right corner.
  441. * @param {number} opt_positionLocation The attrib location for position.
  442. * @param {number} opt_texcoordLocation The attrib location for texture coords.
  443. * @return {!Array.<WebGLBuffer>} The buffer objects that were
  444. * created.
  445. */
  446. var setupUnitQuadWithTexCoords = function(
  447. gl, lowerLeftTexCoords, upperRightTexCoords,
  448. opt_positionLocation, opt_texcoordLocation) {
  449. return setupQuad(gl, {
  450. positionLocation: opt_positionLocation || 0,
  451. texcoordLocation: opt_texcoordLocation || 1,
  452. lowerLeftTexCoords: lowerLeftTexCoords,
  453. upperRightTexCoords: upperRightTexCoords,
  454. });
  455. };
  456. /**
  457. * Makes a quad with various options.
  458. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  459. * @param {!Object} options.
  460. *
  461. * scale: scale to multiple unit quad values by. default 1.0
  462. * positionLocation: attribute location for position
  463. * texcoordLocation: attribute location for texcoords.
  464. * If this does not exist no texture coords are created.
  465. * lowerLeftTexCoords: an array of 2 values for the
  466. * lowerLeftTexCoords.
  467. * upperRightTexCoords: an array of 2 values for the
  468. * upperRightTexCoords.
  469. */
  470. var setupQuad = function(gl, options) {
  471. var positionLocation = options.positionLocation || 0;
  472. var scale = options.scale || 1;
  473. var objects = [];
  474. var vertexObject = gl.createBuffer();
  475. gl.bindBuffer(gl.ARRAY_BUFFER, vertexObject);
  476. gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
  477. 1.0 * scale , 1.0 * scale,
  478. -1.0 * scale , 1.0 * scale,
  479. -1.0 * scale , -1.0 * scale,
  480. 1.0 * scale , 1.0 * scale,
  481. -1.0 * scale , -1.0 * scale,
  482. 1.0 * scale , -1.0 * scale,]), gl.STATIC_DRAW);
  483. gl.enableVertexAttribArray(positionLocation);
  484. gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
  485. objects.push(vertexObject);
  486. if (options.texcoordLocation !== undefined) {
  487. var llx = options.lowerLeftTexCoords[0];
  488. var lly = options.lowerLeftTexCoords[1];
  489. var urx = options.upperRightTexCoords[0];
  490. var ury = options.upperRightTexCoords[1];
  491. var vertexObject = gl.createBuffer();
  492. gl.bindBuffer(gl.ARRAY_BUFFER, vertexObject);
  493. gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
  494. urx, ury,
  495. llx, ury,
  496. llx, lly,
  497. urx, ury,
  498. llx, lly,
  499. urx, lly]), gl.STATIC_DRAW);
  500. gl.enableVertexAttribArray(options.texcoordLocation);
  501. gl.vertexAttribPointer(options.texcoordLocation, 2, gl.FLOAT, false, 0, 0);
  502. objects.push(vertexObject);
  503. }
  504. return objects;
  505. };
  506. /**
  507. * Creates a program and buffers for rendering a textured quad.
  508. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  509. * @param {number} opt_positionLocation The attrib location for
  510. * position. Default = 0.
  511. * @param {number} opt_texcoordLocation The attrib location for
  512. * texture coords. Default = 1.
  513. * @return {!WebGLProgram}
  514. */
  515. var setupTexturedQuad = function(
  516. gl, opt_positionLocation, opt_texcoordLocation) {
  517. var program = setupSimpleTextureProgram(
  518. gl, opt_positionLocation, opt_texcoordLocation);
  519. setupUnitQuad(gl, opt_positionLocation, opt_texcoordLocation);
  520. return program;
  521. };
  522. /**
  523. * Creates a program and buffers for rendering a color quad.
  524. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  525. * @param {number} opt_positionLocation The attrib location for position.
  526. * @return {!WebGLProgram}
  527. */
  528. var setupColorQuad = function(gl, opt_positionLocation) {
  529. opt_positionLocation = opt_positionLocation || 0;
  530. var program = setupSimpleColorProgram(gl);
  531. setupUnitQuad(gl, opt_positionLocation);
  532. return program;
  533. };
  534. /**
  535. * Creates a program and buffers for rendering a textured quad with
  536. * specified lower left and upper right texture coordinates.
  537. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  538. * @param {!Array.<number>} lowerLeftTexCoords The texture coordinates for the lower left corner.
  539. * @param {!Array.<number>} upperRightTexCoords The texture coordinates for the upper right corner.
  540. * @param {number} opt_positionLocation The attrib location for position.
  541. * @param {number} opt_texcoordLocation The attrib location for texture coords.
  542. * @return {!WebGLProgram}
  543. */
  544. var setupTexturedQuadWithTexCoords = function(
  545. gl, lowerLeftTexCoords, upperRightTexCoords,
  546. opt_positionLocation, opt_texcoordLocation) {
  547. var program = setupSimpleTextureProgram(
  548. gl, opt_positionLocation, opt_texcoordLocation);
  549. setupUnitQuadWithTexCoords(gl, lowerLeftTexCoords, upperRightTexCoords,
  550. opt_positionLocation, opt_texcoordLocation);
  551. return program;
  552. };
  553. /**
  554. * Creates a unit quad with only positions of a given resolution.
  555. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  556. * @param {number} gridRes The resolution of the mesh grid,
  557. * expressed in the number of quads across and down.
  558. * @param {number} opt_positionLocation The attrib location for position.
  559. */
  560. var setupIndexedQuad = function (
  561. gl, gridRes, opt_positionLocation, opt_flipOddTriangles) {
  562. return setupIndexedQuadWithOptions(gl,
  563. { gridRes: gridRes,
  564. positionLocation: opt_positionLocation,
  565. flipOddTriangles: opt_flipOddTriangles
  566. });
  567. };
  568. /**
  569. * Creates a quad with various options.
  570. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  571. * @param {!Object) options The options. See below.
  572. * @return {!Array.<WebGLBuffer>} The created buffers.
  573. * [positions, <colors>, indices]
  574. *
  575. * Options:
  576. * gridRes: number of quads across and down grid.
  577. * positionLocation: attrib location for position
  578. * flipOddTriangles: reverse order of vertices of every other
  579. * triangle
  580. * positionOffset: offset added to each vertex
  581. * positionMult: multipier for each vertex
  582. * colorLocation: attrib location for vertex colors. If
  583. * undefined no vertex colors will be created.
  584. */
  585. var setupIndexedQuadWithOptions = function (gl, options) {
  586. var positionLocation = options.positionLocation || 0;
  587. var objects = [];
  588. var gridRes = options.gridRes || 1;
  589. var positionOffset = options.positionOffset || 0;
  590. var positionMult = options.positionMult || 1;
  591. var vertsAcross = gridRes + 1;
  592. var numVerts = vertsAcross * vertsAcross;
  593. var positions = new Float32Array(numVerts * 3);
  594. var indices = new Uint16Array(6 * gridRes * gridRes);
  595. var poffset = 0;
  596. for (var yy = 0; yy <= gridRes; ++yy) {
  597. for (var xx = 0; xx <= gridRes; ++xx) {
  598. positions[poffset + 0] = (-1 + 2 * xx / gridRes) * positionMult + positionOffset;
  599. positions[poffset + 1] = (-1 + 2 * yy / gridRes) * positionMult + positionOffset;
  600. positions[poffset + 2] = 0;
  601. poffset += 3;
  602. }
  603. }
  604. var buf = gl.createBuffer();
  605. gl.bindBuffer(gl.ARRAY_BUFFER, buf);
  606. gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW);
  607. gl.enableVertexAttribArray(positionLocation);
  608. gl.vertexAttribPointer(positionLocation, 3, gl.FLOAT, false, 0, 0);
  609. objects.push(buf);
  610. if (options.colorLocation !== undefined) {
  611. var colors = new Float32Array(numVerts * 4);
  612. for (var yy = 0; yy <= gridRes; ++yy) {
  613. for (var xx = 0; xx <= gridRes; ++xx) {
  614. if (options.color !== undefined) {
  615. colors[poffset + 0] = options.color[0];
  616. colors[poffset + 1] = options.color[1];
  617. colors[poffset + 2] = options.color[2];
  618. colors[poffset + 3] = options.color[3];
  619. } else {
  620. colors[poffset + 0] = xx / gridRes;
  621. colors[poffset + 1] = yy / gridRes;
  622. colors[poffset + 2] = (xx / gridRes) * (yy / gridRes);
  623. colors[poffset + 3] = (yy % 2) * 0.5 + 0.5;
  624. }
  625. poffset += 4;
  626. }
  627. }
  628. var buf = gl.createBuffer();
  629. gl.bindBuffer(gl.ARRAY_BUFFER, buf);
  630. gl.bufferData(gl.ARRAY_BUFFER, colors, gl.STATIC_DRAW);
  631. gl.enableVertexAttribArray(options.colorLocation);
  632. gl.vertexAttribPointer(options.colorLocation, 4, gl.FLOAT, false, 0, 0);
  633. objects.push(buf);
  634. }
  635. var tbase = 0;
  636. for (var yy = 0; yy < gridRes; ++yy) {
  637. var index = yy * vertsAcross;
  638. for (var xx = 0; xx < gridRes; ++xx) {
  639. indices[tbase + 0] = index + 0;
  640. indices[tbase + 1] = index + 1;
  641. indices[tbase + 2] = index + vertsAcross;
  642. indices[tbase + 3] = index + vertsAcross;
  643. indices[tbase + 4] = index + 1;
  644. indices[tbase + 5] = index + vertsAcross + 1;
  645. if (options.flipOddTriangles) {
  646. indices[tbase + 4] = index + vertsAcross + 1;
  647. indices[tbase + 5] = index + 1;
  648. }
  649. index += 1;
  650. tbase += 6;
  651. }
  652. }
  653. var buf = gl.createBuffer();
  654. gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buf);
  655. gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);
  656. objects.push(buf);
  657. return objects;
  658. };
  659. /**
  660. * Returns the constructor for an ArrayBuffer that
  661. * corresponds to the given WebGL type.
  662. * @param {!WebGLRenderingContext} gl A WebGLRenderingContext.
  663. * @param {number} type The WebGL type (eg, gl.UNSIGNED_BYTE)
  664. * @return {!Constructor} The ArrayBuffer constructor that
  665. * corresponds to the given type.
  666. */
  667. var glTypeToArrayBufferType = function(gl, type) {
  668. switch (type) {
  669. case gl.BYTE:
  670. return window.Int8Array;
  671. case gl.UNSIGNED_BYTE:
  672. return window.Uint8Array;
  673. case gl.SHORT:
  674. return window.Int16Array;
  675. case gl.UNSIGNED_SHORT:
  676. case gl.UNSIGNED_SHORT_5_6_5:
  677. case gl.UNSIGNED_SHORT_4_4_4_4:
  678. case gl.UNSIGNED_SHORT_5_5_5_1:
  679. return window.Uint16Array;
  680. case gl.INT:
  681. return window.Int32Array;
  682. case gl.UNSIGNED_INT:
  683. return window.Uint32Array;
  684. default:
  685. throw 'unknown gl type ' + glEnumToString(gl, type);
  686. }
  687. };
  688. /**
  689. * Returns the number of bytes per component for a given WebGL
  690. * type.
  691. * @param {!WebGLRenderingContext} gl A WebGLRenderingContext.
  692. * @param {number} type The WebGL type (eg, gl.UNSIGNED_BYTE)
  693. * @return {!Constructor} The ArrayBuffer constructor that
  694. * corresponds to the given type.
  695. */
  696. var getBytesPerComponent = function(gl, type) {
  697. switch (type) {
  698. case gl.BYTE:
  699. case gl.UNSIGNED_BYTE:
  700. return 1;
  701. case gl.SHORT:
  702. case gl.UNSIGNED_SHORT:
  703. case gl.UNSIGNED_SHORT_5_6_5:
  704. case gl.UNSIGNED_SHORT_4_4_4_4:
  705. case gl.UNSIGNED_SHORT_5_5_5_1:
  706. return 2;
  707. case gl.INT:
  708. case gl.UNSIGNED_INT:
  709. return 4;
  710. default:
  711. throw 'unknown gl type ' + glEnumToString(gl, type);
  712. }
  713. };
  714. /**
  715. * Fills the given texture with a solid color
  716. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  717. * @param {!WebGLTexture} tex The texture to fill.
  718. * @param {number} width The width of the texture to create.
  719. * @param {number} height The height of the texture to create.
  720. * @param {!Array.<number>} color The color to fill with.
  721. * where each element is in the range 0 to 255.
  722. * @param {number} opt_level The level of the texture to fill. Default = 0.
  723. * @param {number} opt_format The format for the texture.
  724. */
  725. var fillTexture = function(gl, tex, width, height, color, opt_level, opt_format, opt_type) {
  726. opt_level = opt_level || 0;
  727. opt_format = opt_format || gl.RGBA;
  728. opt_type = opt_type || gl.UNSIGNED_BYTE;
  729. var pack = gl.getParameter(gl.UNPACK_ALIGNMENT);
  730. var numComponents = color.length;
  731. var bytesPerComponent = getBytesPerComponent(gl, opt_type);
  732. var rowSize = numComponents * width * bytesPerComponent;
  733. var paddedRowSize = Math.floor((rowSize + pack - 1) / pack) * pack;
  734. var size = rowSize + (height - 1) * paddedRowSize;
  735. size = Math.floor((size + bytesPerComponent - 1) / bytesPerComponent) * bytesPerComponent;
  736. var buf = new (glTypeToArrayBufferType(gl, opt_type))(size);
  737. for (var yy = 0; yy < height; ++yy) {
  738. var off = yy * paddedRowSize;
  739. for (var xx = 0; xx < width; ++xx) {
  740. for (var jj = 0; jj < numComponents; ++jj) {
  741. buf[off++] = color[jj];
  742. }
  743. }
  744. }
  745. gl.bindTexture(gl.TEXTURE_2D, tex);
  746. gl.texImage2D(
  747. gl.TEXTURE_2D, opt_level, opt_format, width, height, 0,
  748. opt_format, opt_type, buf);
  749. };
  750. /**
  751. * Creates a textures and fills it with a solid color
  752. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  753. * @param {number} width The width of the texture to create.
  754. * @param {number} height The height of the texture to create.
  755. * @param {!Array.<number>} color The color to fill with. A 4 element array
  756. * where each element is in the range 0 to 255.
  757. * @return {!WebGLTexture}
  758. */
  759. var createColoredTexture = function(gl, width, height, color) {
  760. var tex = gl.createTexture();
  761. fillTexture(gl, tex, width, height, color);
  762. return tex;
  763. };
  764. var ubyteToFloat = function(c) {
  765. return c / 255;
  766. };
  767. var ubyteColorToFloatColor = function(color) {
  768. var floatColor = [];
  769. for (var ii = 0; ii < color.length; ++ii) {
  770. floatColor[ii] = ubyteToFloat(color[ii]);
  771. }
  772. return floatColor;
  773. };
  774. /**
  775. * Sets the "u_color" uniform of the current program to color.
  776. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  777. * @param {!Array.<number> color 4 element array of 0-1 color
  778. * components.
  779. */
  780. var setFloatDrawColor = function(gl, color) {
  781. var program = gl.getParameter(gl.CURRENT_PROGRAM);
  782. var colorLocation = gl.getUniformLocation(program, "u_color");
  783. gl.uniform4fv(colorLocation, color);
  784. };
  785. /**
  786. * Sets the "u_color" uniform of the current program to color.
  787. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  788. * @param {!Array.<number> color 4 element array of 0-255 color
  789. * components.
  790. */
  791. var setUByteDrawColor = function(gl, color) {
  792. setFloatDrawColor(gl, ubyteColorToFloatColor(color));
  793. };
  794. /**
  795. * Draws a previously setup quad in the given color.
  796. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  797. * @param {!Array.<number>} color The color to draw with. A 4
  798. * element array where each element is in the range 0 to
  799. * 1.
  800. */
  801. var drawFloatColorQuad = function(gl, color) {
  802. var program = gl.getParameter(gl.CURRENT_PROGRAM);
  803. var colorLocation = gl.getUniformLocation(program, "u_color");
  804. gl.uniform4fv(colorLocation, color);
  805. gl.drawArrays(gl.TRIANGLES, 0, 6);
  806. };
  807. /**
  808. * Draws a previously setup quad in the given color.
  809. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  810. * @param {!Array.<number>} color The color to draw with. A 4
  811. * element array where each element is in the range 0 to
  812. * 255.
  813. */
  814. var drawUByteColorQuad = function(gl, color) {
  815. drawFloatColorQuad(gl, ubyteColorToFloatColor(color));
  816. };
  817. /**
  818. * Draws a previously setupUnitQuad.
  819. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  820. */
  821. var drawUnitQuad = function(gl) {
  822. gl.drawArrays(gl.TRIANGLES, 0, 6);
  823. };
  824. /**
  825. * Clears then Draws a previously setupUnitQuad.
  826. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  827. * @param {!Array.<number>} opt_color The color to fill clear with before
  828. * drawing. A 4 element array where each element is in the range 0 to
  829. * 255. Default [255, 255, 255, 255]
  830. */
  831. var clearAndDrawUnitQuad = function(gl, opt_color) {
  832. opt_color = opt_color || [255, 255, 255, 255];
  833. gl.clearColor(
  834. opt_color[0] / 255,
  835. opt_color[1] / 255,
  836. opt_color[2] / 255,
  837. opt_color[3] / 255);
  838. gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
  839. drawUnitQuad(gl);
  840. };
  841. /**
  842. * Draws a quad previsouly settup with setupIndexedQuad.
  843. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  844. * @param {number} gridRes Resolution of grid.
  845. */
  846. var drawIndexedQuad = function(gl, gridRes) {
  847. gl.drawElements(gl.TRIANGLES, gridRes * gridRes * 6, gl.UNSIGNED_SHORT, 0);
  848. };
  849. /**
  850. * Draws a previously setupIndexedQuad
  851. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  852. * @param {number} gridRes Resolution of grid.
  853. * @param {!Array.<number>} opt_color The color to fill clear with before
  854. * drawing. A 4 element array where each element is in the range 0 to
  855. * 255. Default [255, 255, 255, 255]
  856. */
  857. var clearAndDrawIndexedQuad = function(gl, gridRes, opt_color) {
  858. opt_color = opt_color || [255, 255, 255, 255];
  859. gl.clearColor(
  860. opt_color[0] / 255,
  861. opt_color[1] / 255,
  862. opt_color[2] / 255,
  863. opt_color[3] / 255);
  864. gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
  865. drawIndexedQuad(gl, gridRes);
  866. };
  867. /**
  868. * Clips a range to min, max
  869. * (Eg. clipToRange(-5,7,0,20) would return {value:0,extent:2}
  870. * @param {number} value start of range
  871. * @param {number} extent extent of range
  872. * @param {number} min min.
  873. * @param {number} max max.
  874. * @return {!{value:number,extent:number} The clipped value.
  875. */
  876. var clipToRange = function(value, extent, min, max) {
  877. if (value < min) {
  878. extent -= min - value;
  879. value = min;
  880. }
  881. var end = value + extent;
  882. if (end > max) {
  883. extent -= end - max;
  884. }
  885. if (extent < 0) {
  886. value = max;
  887. extent = 0;
  888. }
  889. return {value:value, extent: extent};
  890. };
  891. /**
  892. * Checks that a portion of a canvas is 1 color.
  893. * @param {!WebGLRenderingContext|CanvasRenderingContext2D} gl The
  894. * WebGLRenderingContext or 2D context to use.
  895. * @param {number} x left corner of region to check.
  896. * @param {number} y bottom corner of region to check in case of checking from
  897. * a GL context or top corner in case of checking from a 2D context.
  898. * @param {number} width width of region to check.
  899. * @param {number} height width of region to check.
  900. * @param {!Array.<number>} color The color expected. A 4 element array where
  901. * each element is in the range 0 to 255.
  902. * @param {number} opt_errorRange Optional. Acceptable error in
  903. * color checking. 0 by default.
  904. * @param {!function()} sameFn Function to call if all pixels
  905. * are the same as color.
  906. * @param {!function()} differentFn Function to call if a pixel
  907. * is different than color
  908. * @param {!function()} logFn Function to call for logging.
  909. */
  910. var checkCanvasRectColor = function(gl, x, y, width, height, color, opt_errorRange, sameFn, differentFn, logFn) {
  911. if ((gl instanceof WebGLRenderingContext) && !gl.getParameter(gl.FRAMEBUFFER_BINDING)) {
  912. // We're reading the backbuffer so clip.
  913. var xr = clipToRange(x, width, 0, gl.canvas.width);
  914. var yr = clipToRange(y, height, 0, gl.canvas.height);
  915. if (!xr.extent || !yr.extent) {
  916. logFn("checking rect: effective width or heigh is zero");
  917. sameFn();
  918. return;
  919. }
  920. x = xr.value;
  921. y = yr.value;
  922. width = xr.extent;
  923. height = yr.extent;
  924. }
  925. var errorRange = opt_errorRange || 0;
  926. if (!errorRange.length) {
  927. errorRange = [errorRange, errorRange, errorRange, errorRange]
  928. }
  929. var buf;
  930. if (gl instanceof WebGLRenderingContext) {
  931. buf = new Uint8Array(width * height * 4);
  932. gl.readPixels(x, y, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buf);
  933. } else {
  934. buf = gl.getImageData(x, y, width, height).data;
  935. }
  936. for (var i = 0; i < width * height; ++i) {
  937. var offset = i * 4;
  938. for (var j = 0; j < color.length; ++j) {
  939. if (Math.abs(buf[offset + j] - color[j]) > errorRange[j]) {
  940. differentFn();
  941. var was = buf[offset + 0].toString();
  942. for (j = 1; j < color.length; ++j) {
  943. was += "," + buf[offset + j];
  944. }
  945. logFn('at (' + (x + (i % width)) + ', ' + (y + Math.floor(i / width)) +
  946. ') expected: ' + color + ' was ' + was);
  947. return;
  948. }
  949. }
  950. }
  951. sameFn();
  952. };
  953. /**
  954. * Checks that a portion of a canvas is 1 color.
  955. * @param {!WebGLRenderingContext|CanvasRenderingContext2D} gl The
  956. * WebGLRenderingContext or 2D context to use.
  957. * @param {number} x left corner of region to check.
  958. * @param {number} y bottom corner of region to check in case of checking from
  959. * a GL context or top corner in case of checking from a 2D context.
  960. * @param {number} width width of region to check.
  961. * @param {number} height width of region to check.
  962. * @param {!Array.<number>} color The color expected. A 4 element array where
  963. * each element is in the range 0 to 255.
  964. * @param {string} opt_msg Message to associate with success. Eg
  965. * ("should be red").
  966. * @param {number} opt_errorRange Optional. Acceptable error in
  967. * color checking. 0 by default.
  968. */
  969. var checkCanvasRect = function(gl, x, y, width, height, color, opt_msg, opt_errorRange) {
  970. var msg = opt_msg;
  971. if (msg === undefined) {
  972. msg = "should be " + color.toString();
  973. }
  974. checkCanvasRectColor(
  975. gl, x, y, width, height, color, opt_errorRange,
  976. function() {
  977. testPassed(msg);
  978. },
  979. function() {
  980. testFailed(msg);
  981. },
  982. debug);
  983. };
  984. /**
  985. * Checks that an entire canvas is 1 color.
  986. * @param {!WebGLRenderingContext|CanvasRenderingContext2D} gl The
  987. * WebGLRenderingContext or 2D context to use.
  988. * @param {!Array.<number>} color The color expected. A 4 element array where
  989. * each element is in the range 0 to 255.
  990. * @param {string} msg Message to associate with success. Eg ("should be red").
  991. * @param {number} errorRange Optional. Acceptable error in
  992. * color checking. 0 by default.
  993. */
  994. var checkCanvas = function(gl, color, msg, errorRange) {
  995. checkCanvasRect(gl, 0, 0, gl.canvas.width, gl.canvas.height, color, msg, errorRange);
  996. };
  997. /**
  998. * Checks a rectangular area both inside the area and outside
  999. * the area.
  1000. * @param {!WebGLRenderingContext|CanvasRenderingContext2D} gl The
  1001. * WebGLRenderingContext or 2D context to use.
  1002. * @param {number} x left corner of region to check.
  1003. * @param {number} y bottom corner of region to check in case of checking from
  1004. * a GL context or top corner in case of checking from a 2D context.
  1005. * @param {number} width width of region to check.
  1006. * @param {number} height width of region to check.
  1007. * @param {!Array.<number>} innerColor The color expected inside
  1008. * the area. A 4 element array where each element is in the
  1009. * range 0 to 255.
  1010. * @param {!Array.<number>} outerColor The color expected
  1011. * outside. A 4 element array where each element is in the
  1012. * range 0 to 255.
  1013. * @param {!number} opt_edgeSize: The number of pixels to skip
  1014. * around the edges of the area. Defaut 0.
  1015. * @param {!{width:number, height:number}} opt_outerDimensions
  1016. * The outer dimensions. Default the size of gl.canvas.
  1017. */
  1018. var checkAreaInAndOut = function(gl, x, y, width, height, innerColor, outerColor, opt_edgeSize, opt_outerDimensions) {
  1019. var outerDimensions = opt_outerDimensions || { width: gl.canvas.width, height: gl.canvas.height };
  1020. var edgeSize = opt_edgeSize || 0;
  1021. checkCanvasRect(gl, x + edgeSize, y + edgeSize, width - edgeSize * 2, height - edgeSize * 2, innerColor);
  1022. checkCanvasRect(gl, 0, 0, x - edgeSize, outerDimensions.height, outerColor);
  1023. checkCanvasRect(gl, x + width + edgeSize, 0, outerDimensions.width - x - width - edgeSize, outerDimensions.height, outerColor);
  1024. checkCanvasRect(gl, 0, 0, outerDimensions.width, y - edgeSize, outerColor);
  1025. checkCanvasRect(gl, 0, y + height + edgeSize, outerDimensions.width, outerDimensions.height - y - height - edgeSize, outerColor);
  1026. };
  1027. /**
  1028. * Loads a texture, calls callback when finished.
  1029. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  1030. * @param {string} url URL of image to load
  1031. * @param {function(!Image): void} callback Function that gets called after
  1032. * image has loaded
  1033. * @return {!WebGLTexture} The created texture.
  1034. */
  1035. var loadTexture = function(gl, url, callback) {
  1036. var texture = gl.createTexture();
  1037. gl.bindTexture(gl.TEXTURE_2D, texture);
  1038. gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
  1039. gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
  1040. var image = new Image();
  1041. image.onload = function() {
  1042. gl.bindTexture(gl.TEXTURE_2D, texture);
  1043. gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
  1044. gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
  1045. callback(image);
  1046. };
  1047. image.src = url;
  1048. return texture;
  1049. };
  1050. /**
  1051. * Makes a shallow copy of an object.
  1052. * @param {!Object) src Object to copy
  1053. * @return {!Object} The copy of src.
  1054. */
  1055. var shallowCopyObject = function(src) {
  1056. var dst = {};
  1057. for (var attr in src) {
  1058. if (src.hasOwnProperty(attr)) {
  1059. dst[attr] = src[attr];
  1060. }
  1061. }
  1062. return dst;
  1063. };
  1064. /**
  1065. * Checks if an attribute exists on an object case insensitive.
  1066. * @param {!Object) obj Object to check
  1067. * @param {string} attr Name of attribute to look for.
  1068. * @return {string?} The name of the attribute if it exists,
  1069. * undefined if not.
  1070. */
  1071. var hasAttributeCaseInsensitive = function(obj, attr) {
  1072. var lower = attr.toLowerCase();
  1073. for (var key in obj) {
  1074. if (obj.hasOwnProperty(key) && key.toLowerCase() == lower) {
  1075. return key;
  1076. }
  1077. }
  1078. };
  1079. /**
  1080. * Creates a webgl context.
  1081. * @param {!Canvas|string} opt_canvas The canvas tag to get
  1082. * context from. If one is not passed in one will be
  1083. * created. If it's a string it's assumed to be the id of a
  1084. * canvas.
  1085. * @return {!WebGLRenderingContext} The created context.
  1086. */
  1087. var create3DContext = function(opt_canvas, opt_attributes) {
  1088. if (window.initTestingHarness) {
  1089. window.initTestingHarness();
  1090. }
  1091. var attributes = shallowCopyObject(opt_attributes || {});
  1092. if (!hasAttributeCaseInsensitive(attributes, "antialias")) {
  1093. attributes.antialias = false;
  1094. }
  1095. opt_canvas = opt_canvas || document.createElement("canvas");
  1096. if (typeof opt_canvas == 'string') {
  1097. opt_canvas = document.getElementById(opt_canvas);
  1098. }
  1099. var context = null;
  1100. var names = ["webgl", "experimental-webgl"];
  1101. for (var i = 0; i < names.length; ++i) {
  1102. try {
  1103. context = opt_canvas.getContext(names[i], attributes);
  1104. } catch (e) {
  1105. }
  1106. if (context) {
  1107. break;
  1108. }
  1109. }
  1110. if (!context) {
  1111. testFailed("Unable to fetch WebGL rendering context for Canvas");
  1112. }
  1113. return context;
  1114. }
  1115. /**
  1116. * Gets a GLError value as a string.
  1117. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  1118. * @param {number} err The webgl error as retrieved from gl.getError().
  1119. * @return {string} the error as a string.
  1120. */
  1121. var getGLErrorAsString = function(gl, err) {
  1122. if (err === gl.NO_ERROR) {
  1123. return "NO_ERROR";
  1124. }
  1125. for (var name in gl) {
  1126. if (gl[name] === err) {
  1127. return name;
  1128. }
  1129. }
  1130. return err.toString();
  1131. };
  1132. /**
  1133. * Wraps a WebGL function with a function that throws an exception if there is
  1134. * an error.
  1135. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  1136. * @param {string} fname Name of function to wrap.
  1137. * @return {function} The wrapped function.
  1138. */
  1139. var createGLErrorWrapper = function(context, fname) {
  1140. return function() {
  1141. var rv = context[fname].apply(context, arguments);
  1142. var err = context.getError();
  1143. if (err != context.NO_ERROR)
  1144. throw "GL error " + getGLErrorAsString(context, err) + " in " + fname;
  1145. return rv;
  1146. };
  1147. };
  1148. /**
  1149. * Creates a WebGL context where all functions are wrapped to throw an exception
  1150. * if there is an error.
  1151. * @param {!Canvas} canvas The HTML canvas to get a context from.
  1152. * @return {!Object} The wrapped context.
  1153. */
  1154. function create3DContextWithWrapperThatThrowsOnGLError(canvas) {
  1155. var context = create3DContext(canvas);
  1156. var wrap = {};
  1157. for (var i in context) {
  1158. try {
  1159. if (typeof context[i] == 'function') {
  1160. wrap[i] = createGLErrorWrapper(context, i);
  1161. } else {
  1162. wrap[i] = context[i];
  1163. }
  1164. } catch (e) {
  1165. error("createContextWrapperThatThrowsOnGLError: Error accessing " + i);
  1166. }
  1167. }
  1168. wrap.getError = function() {
  1169. return context.getError();
  1170. };
  1171. return wrap;
  1172. };
  1173. /**
  1174. * Tests that an evaluated expression generates a specific GL error.
  1175. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  1176. * @param {number} glError The expected gl error.
  1177. * @param {string} evalSTr The string to evaluate.
  1178. */
  1179. var shouldGenerateGLError = function(gl, glError, evalStr) {
  1180. var exception;
  1181. try {
  1182. eval(evalStr);
  1183. } catch (e) {
  1184. exception = e;
  1185. }
  1186. if (exception) {
  1187. testFailed(evalStr + " threw exception " + exception);
  1188. } else {
  1189. var err = gl.getError();
  1190. if (err != glError) {
  1191. testFailed(evalStr + " expected: " + getGLErrorAsString(gl, glError) + ". Was " + getGLErrorAsString(gl, err) + ".");
  1192. } else {
  1193. testPassed(evalStr + " was expected value: " + getGLErrorAsString(gl, glError) + ".");
  1194. }
  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} glError The expected gl error.
  1201. * @param {string} opt_msg
  1202. */
  1203. var glErrorShouldBe = function(gl, glError, opt_msg) {
  1204. opt_msg = opt_msg || "";
  1205. var err = gl.getError();
  1206. if (err != glError) {
  1207. testFailed("getError expected: " + getGLErrorAsString(gl, glError) +
  1208. ". Was " + getGLErrorAsString(gl, err) + " : " + opt_msg);
  1209. } else {
  1210. testPassed("getError was expected value: " +
  1211. getGLErrorAsString(gl, glError) + " : " + opt_msg);
  1212. }
  1213. };
  1214. /**
  1215. * Links a WebGL program, throws if there are errors.
  1216. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  1217. * @param {!WebGLProgram} program The WebGLProgram to link.
  1218. * @param {function(string): void) opt_errorCallback callback for errors.
  1219. */
  1220. var linkProgram = function(gl, program, opt_errorCallback) {
  1221. var errFn = opt_errorCallback || testFailed;
  1222. // Link the program
  1223. gl.linkProgram(program);
  1224. // Check the link status
  1225. var linked = gl.getProgramParameter(program, gl.LINK_STATUS);
  1226. if (!linked) {
  1227. // something went wrong with the link
  1228. var error = gl.getProgramInfoLog (program);
  1229. errFn("Error in program linking:" + error);
  1230. gl.deleteProgram(program);
  1231. }
  1232. };
  1233. /**
  1234. * Loads text from an external file. This function is synchronous.
  1235. * @param {string} url The url of the external file.
  1236. * @param {!function(bool, string): void} callback that is sent a bool for
  1237. * success and the string.
  1238. */
  1239. var loadTextFileAsync = function(url, callback) {
  1240. log ("loading: " + url);
  1241. var error = 'loadTextFileSynchronous failed to load url "' + url + '"';
  1242. var request;
  1243. if (window.XMLHttpRequest) {
  1244. request = new XMLHttpRequest();
  1245. if (request.overrideMimeType) {
  1246. request.overrideMimeType('text/plain');
  1247. }
  1248. } else {
  1249. throw 'XMLHttpRequest is disabled';
  1250. }
  1251. try {
  1252. request.open('GET', url, true);
  1253. request.onreadystatechange = function() {
  1254. if (request.readyState == 4) {
  1255. var text = '';
  1256. // HTTP reports success with a 200 status. The file protocol reports
  1257. // success with zero. HTTP does not use zero as a status code (they
  1258. // start at 100).
  1259. // https://developer.mozilla.org/En/Using_XMLHttpRequest
  1260. var success = request.status == 200 || request.status == 0;
  1261. if (success) {
  1262. text = request.responseText;
  1263. }
  1264. log("loaded: " + url);
  1265. callback(success, text);
  1266. }
  1267. };
  1268. request.send(null);
  1269. } catch (e) {
  1270. log("failed to load: " + url);
  1271. callback(false, '');
  1272. }
  1273. };
  1274. /**
  1275. * Recursively loads a file as a list. Each line is parsed for a relative
  1276. * path. If the file ends in .txt the contents of that file is inserted in
  1277. * the list.
  1278. *
  1279. * @param {string} url The url of the external file.
  1280. * @param {!function(bool, Array<string>): void} callback that is sent a bool
  1281. * for success and the array of strings.
  1282. */
  1283. var getFileListAsync = function(url, callback) {
  1284. var files = [];
  1285. var getFileListImpl = function(url, callback) {
  1286. var files = [];
  1287. if (url.substr(url.length - 4) == '.txt') {
  1288. loadTextFileAsync(url, function() {
  1289. return function(success, text) {
  1290. if (!success) {
  1291. callback(false, '');
  1292. return;
  1293. }
  1294. var lines = text.split('\n');
  1295. var prefix = '';
  1296. var lastSlash = url.lastIndexOf('/');
  1297. if (lastSlash >= 0) {
  1298. prefix = url.substr(0, lastSlash + 1);
  1299. }
  1300. var fail = false;
  1301. var count = 1;
  1302. var index = 0;
  1303. for (var ii = 0; ii < lines.length; ++ii) {
  1304. var str = lines[ii].replace(/^\s\s*/, '').replace(/\s\s*$/, '');
  1305. if (str.length > 4 &&
  1306. str[0] != '#' &&
  1307. str[0] != ";" &&
  1308. str.substr(0, 2) != "//") {
  1309. var names = str.split(/ +/);
  1310. new_url = prefix + str;
  1311. if (names.length == 1) {
  1312. new_url = prefix + str;
  1313. ++count;
  1314. getFileListImpl(new_url, function(index) {
  1315. return function(success, new_files) {
  1316. log("got files: " + new_files.length);
  1317. if (success) {
  1318. files[index] = new_files;
  1319. }
  1320. finish(success);
  1321. };
  1322. }(index++));
  1323. } else {
  1324. var s = "";
  1325. var p = "";
  1326. for (var jj = 0; jj < names.length; ++jj) {
  1327. s += p + prefix + names[jj];
  1328. p = " ";
  1329. }
  1330. files[index++] = s;
  1331. }
  1332. }
  1333. }
  1334. finish(true);
  1335. function finish(success) {
  1336. if (!success) {
  1337. fail = true;
  1338. }
  1339. --count;
  1340. log("count: " + count);
  1341. if (!count) {
  1342. callback(!fail, files);
  1343. }
  1344. }
  1345. }
  1346. }());
  1347. } else {
  1348. files.push(url);
  1349. callback(true, files);
  1350. }
  1351. };
  1352. getFileListImpl(url, function(success, files) {
  1353. // flatten
  1354. var flat = [];
  1355. flatten(files);
  1356. function flatten(files) {
  1357. for (var ii = 0; ii < files.length; ++ii) {
  1358. var value = files[ii];
  1359. if (typeof(value) == "string") {
  1360. flat.push(value);
  1361. } else {
  1362. flatten(value);
  1363. }
  1364. }
  1365. }
  1366. callback(success, flat);
  1367. });
  1368. };
  1369. /**
  1370. * Gets a file from a file/URL
  1371. * @param {string} file the URL of the file to get.
  1372. * @return {string} The contents of the file.
  1373. */
  1374. var readFile = function(file) {
  1375. var xhr = new XMLHttpRequest();
  1376. xhr.open("GET", file, false);
  1377. xhr.send();
  1378. return xhr.responseText.replace(/\r/g, "");
  1379. };
  1380. var readFileList = function(url) {
  1381. var files = [];
  1382. if (url.substr(url.length - 4) == '.txt') {
  1383. var lines = readFile(url).split('\n');
  1384. var prefix = '';
  1385. var lastSlash = url.lastIndexOf('/');
  1386. if (lastSlash >= 0) {
  1387. prefix = url.substr(0, lastSlash + 1);
  1388. }
  1389. for (var ii = 0; ii < lines.length; ++ii) {
  1390. var str = lines[ii].replace(/^\s\s*/, '').replace(/\s\s*$/, '');
  1391. if (str.length > 4 &&
  1392. str[0] != '#' &&
  1393. str[0] != ";" &&
  1394. str.substr(0, 2) != "//") {
  1395. var names = str.split(/ +/);
  1396. if (names.length == 1) {
  1397. new_url = prefix + str;
  1398. files = files.concat(readFileList(new_url));
  1399. } else {
  1400. var s = "";
  1401. var p = "";
  1402. for (var jj = 0; jj < names.length; ++jj) {
  1403. s += p + prefix + names[jj];
  1404. p = " ";
  1405. }
  1406. files.push(s);
  1407. }
  1408. }
  1409. }
  1410. } else {
  1411. files.push(url);
  1412. }
  1413. return files;
  1414. };
  1415. /**
  1416. * Loads a shader.
  1417. * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
  1418. * @param {string} shaderSource The shader source.
  1419. * @param {number} shaderType The type of shader.
  1420. * @param {function(string): void) opt_errorCallback callback for errors.
  1421. * @return {!WebGLShader} The created shader.
  1422. */
  1423. var loadShader = function(gl, shaderSource, shaderType, opt_errorCallback) {
  1424. var errFn = opt_errorCallback || error;
  1425. // Create the shader object
  1426. var shader = gl.createShader(shaderType);
  1427. if (shader == null) {
  1428. errFn("*** Error: unable to create shader '"+shaderSource+"'");
  1429. return null;
  1430. }
  1431. // Load the shader source
  1432. gl.shaderSource(shader, shaderSource);
  1433. var err = gl.getError();
  1434. if (err != gl.NO_ERROR) {
  1435. errFn("*** Error loading shader '" + shader + "':" + glEnumToString(gl, err));
  1436. return null;
  1437. }
  1438. // Compileā€¦

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