webgl-utils.js 46 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289
  1. /*
  2. * Copyright 2012, Gregg Tavares.
  3. * All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are
  7. * met:
  8. *
  9. * * Redistributions of source code must retain the above copyright
  10. * notice, this list of conditions and the following disclaimer.
  11. * * Redistributions in binary form must reproduce the above
  12. * copyright notice, this list of conditions and the following disclaimer
  13. * in the documentation and/or other materials provided with the
  14. * distribution.
  15. * * Neither the name of Gregg Tavares. nor the names of his
  16. * contributors may be used to endorse or promote products derived from
  17. * this software without specific prior written permission.
  18. *
  19. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  20. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  21. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  22. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  23. * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  24. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  25. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  26. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  27. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  28. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  29. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  30. */
  31. (function(root, factory) { // eslint-disable-line
  32. if (typeof define === 'function' && define.amd) {
  33. // AMD. Register as an anonymous module.
  34. define([], function() {
  35. return factory.call(root);
  36. });
  37. } else {
  38. // Browser globals
  39. root.webglUtils = factory.call(root);
  40. }
  41. }(this, function() {
  42. "use strict";
  43. var topWindow = this;
  44. /** @module webgl-utils */
  45. function isInIFrame(w) {
  46. w = w || topWindow;
  47. return w !== w.top;
  48. }
  49. if (!isInIFrame()) {
  50. console.log("%c%s", 'color:blue;font-weight:bold;', 'for more about webgl-utils.js see:'); // eslint-disable-line
  51. console.log("%c%s", 'color:blue;font-weight:bold;', 'http://webglfundamentals.org/webgl/lessons/webgl-boilerplate.html'); // eslint-disable-line
  52. }
  53. /**
  54. * Wrapped logging function.
  55. * @param {string} msg The message to log.
  56. */
  57. function error(msg) {
  58. if (topWindow.console) {
  59. if (topWindow.console.error) {
  60. topWindow.console.error(msg);
  61. } else if (topWindow.console.log) {
  62. topWindow.console.log(msg);
  63. }
  64. }
  65. }
  66. /**
  67. * Error Callback
  68. * @callback ErrorCallback
  69. * @param {string} msg error message.
  70. * @memberOf module:webgl-utils
  71. */
  72. /**
  73. * Loads a shader.
  74. * @param {WebGLRenderingContext} gl The WebGLRenderingContext to use.
  75. * @param {string} shaderSource The shader source.
  76. * @param {number} shaderType The type of shader.
  77. * @param {module:webgl-utils.ErrorCallback} opt_errorCallback callback for errors.
  78. * @return {WebGLShader} The created shader.
  79. */
  80. function loadShader(gl, shaderSource, shaderType, opt_errorCallback) {
  81. var errFn = opt_errorCallback || error;
  82. // Create the shader object
  83. var shader = gl.createShader(shaderType);
  84. // Load the shader source
  85. gl.shaderSource(shader, shaderSource);
  86. // Compile the shader
  87. gl.compileShader(shader);
  88. // Check the compile status
  89. var compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
  90. if (!compiled) {
  91. // Something went wrong during compilation; get the error
  92. var lastError = gl.getShaderInfoLog(shader);
  93. errFn("*** Error compiling shader '" + shader + "':" + lastError);
  94. gl.deleteShader(shader);
  95. return null;
  96. }
  97. return shader;
  98. }
  99. /**
  100. * Creates a program, attaches shaders, binds attrib locations, links the
  101. * program and calls useProgram.
  102. * @param {WebGLShader[]} shaders The shaders to attach
  103. * @param {string[]} [opt_attribs] An array of attribs names. Locations will be assigned by index if not passed in
  104. * @param {number[]} [opt_locations] The locations for the. A parallel array to opt_attribs letting you assign locations.
  105. * @param {module:webgl-utils.ErrorCallback} opt_errorCallback callback for errors. By default it just prints an error to the console
  106. * on error. If you want something else pass an callback. It's passed an error message.
  107. * @memberOf module:webgl-utils
  108. */
  109. function createProgram(
  110. gl, shaders, opt_attribs, opt_locations, opt_errorCallback) {
  111. var errFn = opt_errorCallback || error;
  112. var program = gl.createProgram();
  113. shaders.forEach(function(shader) {
  114. gl.attachShader(program, shader);
  115. });
  116. if (opt_attribs) {
  117. opt_attribs.forEach(function(attrib, ndx) {
  118. gl.bindAttribLocation(
  119. program,
  120. opt_locations ? opt_locations[ndx] : ndx,
  121. attrib);
  122. });
  123. }
  124. gl.linkProgram(program);
  125. // Check the link status
  126. var linked = gl.getProgramParameter(program, gl.LINK_STATUS);
  127. if (!linked) {
  128. // something went wrong with the link
  129. var lastError = gl.getProgramInfoLog(program);
  130. errFn("Error in program linking:" + lastError);
  131. gl.deleteProgram(program);
  132. return null;
  133. }
  134. return program;
  135. }
  136. /**
  137. * Loads a shader from a script tag.
  138. * @param {WebGLRenderingContext} gl The WebGLRenderingContext to use.
  139. * @param {string} scriptId The id of the script tag.
  140. * @param {number} opt_shaderType The type of shader. If not passed in it will
  141. * be derived from the type of the script tag.
  142. * @param {module:webgl-utils.ErrorCallback} opt_errorCallback callback for errors.
  143. * @return {WebGLShader} The created shader.
  144. */
  145. function createShaderFromScript(
  146. gl, scriptId, opt_shaderType, opt_errorCallback) {
  147. var shaderSource = "";
  148. var shaderType;
  149. var shaderScript = document.getElementById(scriptId);
  150. if (!shaderScript) {
  151. throw ("*** Error: unknown script element" + scriptId);
  152. }
  153. shaderSource = shaderScript.text;
  154. if (!opt_shaderType) {
  155. if (shaderScript.type === "x-shader/x-vertex") {
  156. shaderType = gl.VERTEX_SHADER;
  157. } else if (shaderScript.type === "x-shader/x-fragment") {
  158. shaderType = gl.FRAGMENT_SHADER;
  159. } else if (shaderType !== gl.VERTEX_SHADER && shaderType !== gl.FRAGMENT_SHADER) {
  160. throw ("*** Error: unknown shader type");
  161. }
  162. }
  163. return loadShader(
  164. gl, shaderSource, opt_shaderType ? opt_shaderType : shaderType,
  165. opt_errorCallback);
  166. }
  167. var defaultShaderType = [
  168. "VERTEX_SHADER",
  169. "FRAGMENT_SHADER",
  170. ];
  171. /**
  172. * Creates a program from 2 script tags.
  173. *
  174. * @param {WebGLRenderingContext} gl The WebGLRenderingContext
  175. * to use.
  176. * @param {string[]} shaderScriptIds Array of ids of the script
  177. * tags for the shaders. The first is assumed to be the
  178. * vertex shader, the second the fragment shader.
  179. * @param {string[]} [opt_attribs] An array of attribs names. Locations will be assigned by index if not passed in
  180. * @param {number[]} [opt_locations] The locations for the. A parallel array to opt_attribs letting you assign locations.
  181. * @param {module:webgl-utils.ErrorCallback} opt_errorCallback callback for errors. By default it just prints an error to the console
  182. * on error. If you want something else pass an callback. It's passed an error message.
  183. * @return {WebGLProgram} The created program.
  184. * @memberOf module:webgl-utils
  185. */
  186. function createProgramFromScripts(
  187. gl, shaderScriptIds, opt_attribs, opt_locations, opt_errorCallback) {
  188. var shaders = [];
  189. for (var ii = 0; ii < shaderScriptIds.length; ++ii) {
  190. shaders.push(createShaderFromScript(
  191. gl, shaderScriptIds[ii], gl[defaultShaderType[ii]], opt_errorCallback));
  192. }
  193. return createProgram(gl, shaders, opt_attribs, opt_locations, opt_errorCallback);
  194. }
  195. /**
  196. * Creates a program from 2 sources.
  197. *
  198. * @param {WebGLRenderingContext} gl The WebGLRenderingContext
  199. * to use.
  200. * @param {string[]} shaderSourcess Array of sources for the
  201. * shaders. The first is assumed to be the vertex shader,
  202. * the second the fragment shader.
  203. * @param {string[]} [opt_attribs] An array of attribs names. Locations will be assigned by index if not passed in
  204. * @param {number[]} [opt_locations] The locations for the. A parallel array to opt_attribs letting you assign locations.
  205. * @param {module:webgl-utils.ErrorCallback} opt_errorCallback callback for errors. By default it just prints an error to the console
  206. * on error. If you want something else pass an callback. It's passed an error message.
  207. * @return {WebGLProgram} The created program.
  208. * @memberOf module:webgl-utils
  209. */
  210. function createProgramFromSources(
  211. gl, shaderSources, opt_attribs, opt_locations, opt_errorCallback) {
  212. var shaders = [];
  213. for (var ii = 0; ii < shaderSources.length; ++ii) {
  214. shaders.push(loadShader(
  215. gl, shaderSources[ii], gl[defaultShaderType[ii]], opt_errorCallback));
  216. }
  217. return createProgram(gl, shaders, opt_attribs, opt_locations, opt_errorCallback);
  218. }
  219. /**
  220. * Returns the corresponding bind point for a given sampler type
  221. */
  222. function getBindPointForSamplerType(gl, type) {
  223. if (type === gl.SAMPLER_2D) return gl.TEXTURE_2D; // eslint-disable-line
  224. if (type === gl.SAMPLER_CUBE) return gl.TEXTURE_CUBE_MAP; // eslint-disable-line
  225. return undefined;
  226. }
  227. /**
  228. * @typedef {Object.<string, function>} Setters
  229. */
  230. /**
  231. * Creates setter functions for all uniforms of a shader
  232. * program.
  233. *
  234. * @see {@link module:webgl-utils.setUniforms}
  235. *
  236. * @param {WebGLProgram} program the program to create setters for.
  237. * @returns {Object.<string, function>} an object with a setter by name for each uniform
  238. * @memberOf module:webgl-utils
  239. */
  240. function createUniformSetters(gl, program) {
  241. var textureUnit = 0;
  242. /**
  243. * Creates a setter for a uniform of the given program with it's
  244. * location embedded in the setter.
  245. * @param {WebGLProgram} program
  246. * @param {WebGLUniformInfo} uniformInfo
  247. * @returns {function} the created setter.
  248. */
  249. function createUniformSetter(program, uniformInfo) {
  250. var location = gl.getUniformLocation(program, uniformInfo.name);
  251. var type = uniformInfo.type;
  252. // Check if this uniform is an array
  253. var isArray = (uniformInfo.size > 1 && uniformInfo.name.substr(-3) === "[0]");
  254. if (type === gl.FLOAT && isArray) {
  255. return function(v) {
  256. gl.uniform1fv(location, v);
  257. };
  258. }
  259. if (type === gl.FLOAT) {
  260. return function(v) {
  261. gl.uniform1f(location, v);
  262. };
  263. }
  264. if (type === gl.FLOAT_VEC2) {
  265. return function(v) {
  266. gl.uniform2fv(location, v);
  267. };
  268. }
  269. if (type === gl.FLOAT_VEC3) {
  270. return function(v) {
  271. gl.uniform3fv(location, v);
  272. };
  273. }
  274. if (type === gl.FLOAT_VEC4) {
  275. return function(v) {
  276. gl.uniform4fv(location, v);
  277. };
  278. }
  279. if (type === gl.INT && isArray) {
  280. return function(v) {
  281. gl.uniform1iv(location, v);
  282. };
  283. }
  284. if (type === gl.INT) {
  285. return function(v) {
  286. gl.uniform1i(location, v);
  287. };
  288. }
  289. if (type === gl.INT_VEC2) {
  290. return function(v) {
  291. gl.uniform2iv(location, v);
  292. };
  293. }
  294. if (type === gl.INT_VEC3) {
  295. return function(v) {
  296. gl.uniform3iv(location, v);
  297. };
  298. }
  299. if (type === gl.INT_VEC4) {
  300. return function(v) {
  301. gl.uniform4iv(location, v);
  302. };
  303. }
  304. if (type === gl.BOOL) {
  305. return function(v) {
  306. gl.uniform1iv(location, v);
  307. };
  308. }
  309. if (type === gl.BOOL_VEC2) {
  310. return function(v) {
  311. gl.uniform2iv(location, v);
  312. };
  313. }
  314. if (type === gl.BOOL_VEC3) {
  315. return function(v) {
  316. gl.uniform3iv(location, v);
  317. };
  318. }
  319. if (type === gl.BOOL_VEC4) {
  320. return function(v) {
  321. gl.uniform4iv(location, v);
  322. };
  323. }
  324. if (type === gl.FLOAT_MAT2) {
  325. return function(v) {
  326. gl.uniformMatrix2fv(location, false, v);
  327. };
  328. }
  329. if (type === gl.FLOAT_MAT3) {
  330. return function(v) {
  331. gl.uniformMatrix3fv(location, false, v);
  332. };
  333. }
  334. if (type === gl.FLOAT_MAT4) {
  335. return function(v) {
  336. gl.uniformMatrix4fv(location, false, v);
  337. };
  338. }
  339. if ((type === gl.SAMPLER_2D || type === gl.SAMPLER_CUBE) && isArray) {
  340. var units = [];
  341. for (var ii = 0; ii < info.size; ++ii) {
  342. units.push(textureUnit++);
  343. }
  344. return function(bindPoint, units) {
  345. return function(textures) {
  346. gl.uniform1iv(location, units);
  347. textures.forEach(function(texture, index) {
  348. gl.activeTexture(gl.TEXTURE0 + units[index]);
  349. gl.bindTexture(bindPoint, texture);
  350. });
  351. };
  352. }(getBindPointForSamplerType(gl, type), units);
  353. }
  354. if (type === gl.SAMPLER_2D || type === gl.SAMPLER_CUBE) {
  355. return function(bindPoint, unit) {
  356. return function(texture) {
  357. gl.uniform1i(location, unit);
  358. gl.activeTexture(gl.TEXTURE0 + unit);
  359. gl.bindTexture(bindPoint, texture);
  360. };
  361. }(getBindPointForSamplerType(gl, type), textureUnit++);
  362. }
  363. throw ("unknown type: 0x" + type.toString(16)); // we should never get here.
  364. }
  365. var uniformSetters = { };
  366. var numUniforms = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS);
  367. for (var ii = 0; ii < numUniforms; ++ii) {
  368. var uniformInfo = gl.getActiveUniform(program, ii);
  369. if (!uniformInfo) {
  370. break;
  371. }
  372. var name = uniformInfo.name;
  373. // remove the array suffix.
  374. if (name.substr(-3) === "[0]") {
  375. name = name.substr(0, name.length - 3);
  376. }
  377. var setter = createUniformSetter(program, uniformInfo);
  378. uniformSetters[name] = setter;
  379. }
  380. return uniformSetters;
  381. }
  382. /**
  383. * Set uniforms and binds related textures.
  384. *
  385. * example:
  386. *
  387. * var programInfo = createProgramInfo(
  388. * gl, ["some-vs", "some-fs");
  389. *
  390. * var tex1 = gl.createTexture();
  391. * var tex2 = gl.createTexture();
  392. *
  393. * ... assume we setup the textures with data ...
  394. *
  395. * var uniforms = {
  396. * u_someSampler: tex1,
  397. * u_someOtherSampler: tex2,
  398. * u_someColor: [1,0,0,1],
  399. * u_somePosition: [0,1,1],
  400. * u_someMatrix: [
  401. * 1,0,0,0,
  402. * 0,1,0,0,
  403. * 0,0,1,0,
  404. * 0,0,0,0,
  405. * ],
  406. * };
  407. *
  408. * gl.useProgram(program);
  409. *
  410. * This will automatically bind the textures AND set the
  411. * uniforms.
  412. *
  413. * setUniforms(programInfo.uniformSetters, uniforms);
  414. *
  415. * For the example above it is equivalent to
  416. *
  417. * var texUnit = 0;
  418. * gl.activeTexture(gl.TEXTURE0 + texUnit);
  419. * gl.bindTexture(gl.TEXTURE_2D, tex1);
  420. * gl.uniform1i(u_someSamplerLocation, texUnit++);
  421. * gl.activeTexture(gl.TEXTURE0 + texUnit);
  422. * gl.bindTexture(gl.TEXTURE_2D, tex2);
  423. * gl.uniform1i(u_someSamplerLocation, texUnit++);
  424. * gl.uniform4fv(u_someColorLocation, [1, 0, 0, 1]);
  425. * gl.uniform3fv(u_somePositionLocation, [0, 1, 1]);
  426. * gl.uniformMatrix4fv(u_someMatrix, false, [
  427. * 1,0,0,0,
  428. * 0,1,0,0,
  429. * 0,0,1,0,
  430. * 0,0,0,0,
  431. * ]);
  432. *
  433. * Note it is perfectly reasonable to call `setUniforms` multiple times. For example
  434. *
  435. * var uniforms = {
  436. * u_someSampler: tex1,
  437. * u_someOtherSampler: tex2,
  438. * };
  439. *
  440. * var moreUniforms {
  441. * u_someColor: [1,0,0,1],
  442. * u_somePosition: [0,1,1],
  443. * u_someMatrix: [
  444. * 1,0,0,0,
  445. * 0,1,0,0,
  446. * 0,0,1,0,
  447. * 0,0,0,0,
  448. * ],
  449. * };
  450. *
  451. * setUniforms(programInfo.uniformSetters, uniforms);
  452. * setUniforms(programInfo.uniformSetters, moreUniforms);
  453. *
  454. * @param {Object.<string, function>|module:webgl-utils.ProgramInfo} setters the setters returned from
  455. * `createUniformSetters` or a ProgramInfo from {@link module:webgl-utils.createProgramInfo}.
  456. * @param {Object.<string, value>} an object with values for the
  457. * uniforms.
  458. * @memberOf module:webgl-utils
  459. */
  460. function setUniforms(setters, values) {
  461. setters = setters.uniformSetters || setters;
  462. Object.keys(values).forEach(function(name) {
  463. var setter = setters[name];
  464. if (setter) {
  465. setter(values[name]);
  466. }
  467. });
  468. }
  469. /**
  470. * Creates setter functions for all attributes of a shader
  471. * program. You can pass this to {@link module:webgl-utils.setBuffersAndAttributes} to set all your buffers and attributes.
  472. *
  473. * @see {@link module:webgl-utils.setAttributes} for example
  474. * @param {WebGLProgram} program the program to create setters for.
  475. * @return {Object.<string, function>} an object with a setter for each attribute by name.
  476. * @memberOf module:webgl-utils
  477. */
  478. function createAttributeSetters(gl, program) {
  479. var attribSetters = {
  480. };
  481. function createAttribSetter(index) {
  482. return function(b) {
  483. gl.bindBuffer(gl.ARRAY_BUFFER, b.buffer);
  484. gl.enableVertexAttribArray(index);
  485. gl.vertexAttribPointer(
  486. index, b.numComponents || b.size, b.type || gl.FLOAT, b.normalize || false, b.stride || 0, b.offset || 0);
  487. };
  488. }
  489. var numAttribs = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES);
  490. for (var ii = 0; ii < numAttribs; ++ii) {
  491. var attribInfo = gl.getActiveAttrib(program, ii);
  492. if (!attribInfo) {
  493. break;
  494. }
  495. var index = gl.getAttribLocation(program, attribInfo.name);
  496. attribSetters[attribInfo.name] = createAttribSetter(index);
  497. }
  498. return attribSetters;
  499. }
  500. /**
  501. * Sets attributes and binds buffers (deprecated... use {@link module:webgl-utils.setBuffersAndAttributes})
  502. *
  503. * Example:
  504. *
  505. * var program = createProgramFromScripts(
  506. * gl, ["some-vs", "some-fs");
  507. *
  508. * var attribSetters = createAttributeSetters(program);
  509. *
  510. * var positionBuffer = gl.createBuffer();
  511. * var texcoordBuffer = gl.createBuffer();
  512. *
  513. * var attribs = {
  514. * a_position: {buffer: positionBuffer, numComponents: 3},
  515. * a_texcoord: {buffer: texcoordBuffer, numComponents: 2},
  516. * };
  517. *
  518. * gl.useProgram(program);
  519. *
  520. * This will automatically bind the buffers AND set the
  521. * attributes.
  522. *
  523. * setAttributes(attribSetters, attribs);
  524. *
  525. * Properties of attribs. For each attrib you can add
  526. * properties:
  527. *
  528. * * type: the type of data in the buffer. Default = gl.FLOAT
  529. * * normalize: whether or not to normalize the data. Default = false
  530. * * stride: the stride. Default = 0
  531. * * offset: offset into the buffer. Default = 0
  532. *
  533. * For example if you had 3 value float positions, 2 value
  534. * float texcoord and 4 value uint8 colors you'd setup your
  535. * attribs like this
  536. *
  537. * var attribs = {
  538. * a_position: {buffer: positionBuffer, numComponents: 3},
  539. * a_texcoord: {buffer: texcoordBuffer, numComponents: 2},
  540. * a_color: {
  541. * buffer: colorBuffer,
  542. * numComponents: 4,
  543. * type: gl.UNSIGNED_BYTE,
  544. * normalize: true,
  545. * },
  546. * };
  547. *
  548. * @param {Object.<string, function>|model:webgl-utils.ProgramInfo} setters Attribute setters as returned from createAttributeSetters or a ProgramInfo as returned {@link module:webgl-utils.createProgramInfo}
  549. * @param {Object.<string, module:webgl-utils.AttribInfo>} attribs AttribInfos mapped by attribute name.
  550. * @memberOf module:webgl-utils
  551. * @deprecated use {@link module:webgl-utils.setBuffersAndAttributes}
  552. */
  553. function setAttributes(setters, attribs) {
  554. setters = setters.attribSetters || setters;
  555. Object.keys(attribs).forEach(function(name) {
  556. var setter = setters[name];
  557. if (setter) {
  558. setter(attribs[name]);
  559. }
  560. });
  561. }
  562. /**
  563. * Creates a vertex array object and then sets the attributes
  564. * on it
  565. *
  566. * @param {WebGLRenderingContext} gl The WebGLRenderingContext
  567. * to use.
  568. * @param {Object.<string, function>} setters Attribute setters as returned from createAttributeSetters
  569. * @param {Object.<string, module:webgl-utils.AttribInfo>} attribs AttribInfos mapped by attribute name.
  570. * @param {WebGLBuffer} [indices] an optional ELEMENT_ARRAY_BUFFER of indices
  571. */
  572. function createVAOAndSetAttributes(gl, setters, attribs, indices) {
  573. var vao = gl.createVertexArray();
  574. gl.bindVertexArray(vao);
  575. setAttributes(setters, attribs);
  576. if (indices) {
  577. gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indices);
  578. }
  579. // We unbind this because otherwise any change to ELEMENT_ARRAY_BUFFER
  580. // like when creating buffers for other stuff will mess up this VAO's binding
  581. gl.bindVertexArray(null);
  582. return vao;
  583. }
  584. /**
  585. * Creates a vertex array object and then sets the attributes
  586. * on it
  587. *
  588. * @param {WebGLRenderingContext} gl The WebGLRenderingContext
  589. * to use.
  590. * @param {Object.<string, function>| module:webgl-utils.ProgramInfo} programInfo as returned from createProgramInfo or Attribute setters as returned from createAttributeSetters
  591. * @param {module:webgl-utils:BufferInfo} bufferInfo BufferInfo as returned from createBufferInfoFromArrays etc...
  592. * @param {WebGLBuffer} [indices] an optional ELEMENT_ARRAY_BUFFER of indices
  593. */
  594. function createVAOFromBufferInfo(gl, programInfo, bufferInfo) {
  595. return createVAOAndSetAttributes(gl, programInfo.attribSetters || programInfo, bufferInfo.attribs, bufferInfo.indices);
  596. }
  597. /**
  598. * @typedef {Object} ProgramInfo
  599. * @property {WebGLProgram} program A shader program
  600. * @property {Object<string, function>} uniformSetters: object of setters as returned from createUniformSetters,
  601. * @property {Object<string, function>} attribSetters: object of setters as returned from createAttribSetters,
  602. * @memberOf module:webgl-utils
  603. */
  604. /**
  605. * Creates a ProgramInfo from 2 sources.
  606. *
  607. * A ProgramInfo contains
  608. *
  609. * programInfo = {
  610. * program: WebGLProgram,
  611. * uniformSetters: object of setters as returned from createUniformSetters,
  612. * attribSetters: object of setters as returned from createAttribSetters,
  613. * }
  614. *
  615. * @param {WebGLRenderingContext} gl The WebGLRenderingContext
  616. * to use.
  617. * @param {string[]} shaderSourcess Array of sources for the
  618. * shaders or ids. The first is assumed to be the vertex shader,
  619. * the second the fragment shader.
  620. * @param {string[]} [opt_attribs] An array of attribs names. Locations will be assigned by index if not passed in
  621. * @param {number[]} [opt_locations] The locations for the. A parallel array to opt_attribs letting you assign locations.
  622. * @param {module:webgl-utils.ErrorCallback} opt_errorCallback callback for errors. By default it just prints an error to the console
  623. * on error. If you want something else pass an callback. It's passed an error message.
  624. * @return {module:webgl-utils.ProgramInfo} The created program.
  625. * @memberOf module:webgl-utils
  626. */
  627. function createProgramInfo(
  628. gl, shaderSources, opt_attribs, opt_locations, opt_errorCallback) {
  629. shaderSources = shaderSources.map(function(source) {
  630. var script = document.getElementById(source);
  631. return script ? script.text : source;
  632. });
  633. var program = webglUtils.createProgramFromSources(gl, shaderSources, opt_attribs, opt_locations, opt_errorCallback);
  634. if (!program) {
  635. return null;
  636. }
  637. var uniformSetters = createUniformSetters(gl, program);
  638. var attribSetters = createAttributeSetters(gl, program);
  639. return {
  640. program: program,
  641. uniformSetters: uniformSetters,
  642. attribSetters: attribSetters,
  643. };
  644. }
  645. /**
  646. * Sets attributes and buffers including the `ELEMENT_ARRAY_BUFFER` if appropriate
  647. *
  648. * Example:
  649. *
  650. * var programInfo = createProgramInfo(
  651. * gl, ["some-vs", "some-fs");
  652. *
  653. * var arrays = {
  654. * position: { numComponents: 3, data: [0, 0, 0, 10, 0, 0, 0, 10, 0, 10, 10, 0], },
  655. * texcoord: { numComponents: 2, data: [0, 0, 0, 1, 1, 0, 1, 1], },
  656. * };
  657. *
  658. * var bufferInfo = createBufferInfoFromArrays(gl, arrays);
  659. *
  660. * gl.useProgram(programInfo.program);
  661. *
  662. * This will automatically bind the buffers AND set the
  663. * attributes.
  664. *
  665. * setBuffersAndAttributes(programInfo.attribSetters, bufferInfo);
  666. *
  667. * For the example above it is equivilent to
  668. *
  669. * gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
  670. * gl.enableVertexAttribArray(a_positionLocation);
  671. * gl.vertexAttribPointer(a_positionLocation, 3, gl.FLOAT, false, 0, 0);
  672. * gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer);
  673. * gl.enableVertexAttribArray(a_texcoordLocation);
  674. * gl.vertexAttribPointer(a_texcoordLocation, 4, gl.FLOAT, false, 0, 0);
  675. *
  676. * @param {WebGLRenderingContext} gl A WebGLRenderingContext.
  677. * @param {Object.<string, function>} setters Attribute setters as returned from `createAttributeSetters`
  678. * @param {module:webgl-utils.BufferInfo} buffers a BufferInfo as returned from `createBufferInfoFromArrays`.
  679. * @memberOf module:webgl-utils
  680. */
  681. function setBuffersAndAttributes(gl, setters, buffers) {
  682. setAttributes(setters, buffers.attribs);
  683. if (buffers.indices) {
  684. gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffers.indices);
  685. }
  686. }
  687. // Add your prefix here.
  688. var browserPrefixes = [
  689. "",
  690. "MOZ_",
  691. "OP_",
  692. "WEBKIT_",
  693. ];
  694. /**
  695. * Given an extension name like WEBGL_compressed_texture_s3tc
  696. * returns the supported version extension, like
  697. * WEBKIT_WEBGL_compressed_teture_s3tc
  698. * @param {string} name Name of extension to look for
  699. * @return {WebGLExtension} The extension or undefined if not
  700. * found.
  701. * @memberOf module:webgl-utils
  702. */
  703. function getExtensionWithKnownPrefixes(gl, name) {
  704. for (var ii = 0; ii < browserPrefixes.length; ++ii) {
  705. var prefixedName = browserPrefixes[ii] + name;
  706. var ext = gl.getExtension(prefixedName);
  707. if (ext) {
  708. return ext;
  709. }
  710. }
  711. return undefined;
  712. }
  713. /**
  714. * Resize a canvas to match the size its displayed.
  715. * @param {HTMLCanvasElement} canvas The canvas to resize.
  716. * @param {number} [multiplier] amount to multiply by.
  717. * Pass in window.devicePixelRatio for native pixels.
  718. * @return {boolean} true if the canvas was resized.
  719. * @memberOf module:webgl-utils
  720. */
  721. function resizeCanvasToDisplaySize(canvas, multiplier) {
  722. multiplier = multiplier || 1;
  723. var width = canvas.clientWidth * multiplier | 0;
  724. var height = canvas.clientHeight * multiplier | 0;
  725. if (canvas.width !== width || canvas.height !== height) {
  726. canvas.width = width;
  727. canvas.height = height;
  728. return true;
  729. }
  730. return false;
  731. }
  732. // Add `push` to a typed array. It just keeps a 'cursor'
  733. // and allows use to `push` values into the array so we
  734. // don't have to manually compute offsets
  735. function augmentTypedArray(typedArray, numComponents) {
  736. var cursor = 0;
  737. typedArray.push = function() {
  738. for (var ii = 0; ii < arguments.length; ++ii) {
  739. var value = arguments[ii];
  740. if (value instanceof Array || (value.buffer && value.buffer instanceof ArrayBuffer)) {
  741. for (var jj = 0; jj < value.length; ++jj) {
  742. typedArray[cursor++] = value[jj];
  743. }
  744. } else {
  745. typedArray[cursor++] = value;
  746. }
  747. }
  748. };
  749. typedArray.reset = function(opt_index) {
  750. cursor = opt_index || 0;
  751. };
  752. typedArray.numComponents = numComponents;
  753. Object.defineProperty(typedArray, 'numElements', {
  754. get: function() {
  755. return this.length / this.numComponents | 0;
  756. },
  757. });
  758. return typedArray;
  759. }
  760. /**
  761. * creates a typed array with a `push` function attached
  762. * so that you can easily *push* values.
  763. *
  764. * `push` can take multiple arguments. If an argument is an array each element
  765. * of the array will be added to the typed array.
  766. *
  767. * Example:
  768. *
  769. * var array = createAugmentedTypedArray(3, 2); // creates a Float32Array with 6 values
  770. * array.push(1, 2, 3);
  771. * array.push([4, 5, 6]);
  772. * // array now contains [1, 2, 3, 4, 5, 6]
  773. *
  774. * Also has `numComponents` and `numElements` properties.
  775. *
  776. * @param {number} numComponents number of components
  777. * @param {number} numElements number of elements. The total size of the array will be `numComponents * numElements`.
  778. * @param {constructor} opt_type A constructor for the type. Default = `Float32Array`.
  779. * @return {ArrayBuffer} A typed array.
  780. * @memberOf module:webgl-utils
  781. */
  782. function createAugmentedTypedArray(numComponents, numElements, opt_type) {
  783. var Type = opt_type || Float32Array;
  784. return augmentTypedArray(new Type(numComponents * numElements), numComponents);
  785. }
  786. function createBufferFromTypedArray(gl, array, type, drawType) {
  787. type = type || gl.ARRAY_BUFFER;
  788. var buffer = gl.createBuffer();
  789. gl.bindBuffer(type, buffer);
  790. gl.bufferData(type, array, drawType || gl.STATIC_DRAW);
  791. return buffer;
  792. }
  793. function allButIndices(name) {
  794. return name !== "indices";
  795. }
  796. function createMapping(obj) {
  797. var mapping = {};
  798. Object.keys(obj).filter(allButIndices).forEach(function(key) {
  799. mapping["a_" + key] = key;
  800. });
  801. return mapping;
  802. }
  803. function getGLTypeForTypedArray(gl, typedArray) {
  804. if (typedArray instanceof Int8Array) { return gl.BYTE; } // eslint-disable-line
  805. if (typedArray instanceof Uint8Array) { return gl.UNSIGNED_BYTE; } // eslint-disable-line
  806. if (typedArray instanceof Int16Array) { return gl.SHORT; } // eslint-disable-line
  807. if (typedArray instanceof Uint16Array) { return gl.UNSIGNED_SHORT; } // eslint-disable-line
  808. if (typedArray instanceof Int32Array) { return gl.INT; } // eslint-disable-line
  809. if (typedArray instanceof Uint32Array) { return gl.UNSIGNED_INT; } // eslint-disable-line
  810. if (typedArray instanceof Float32Array) { return gl.FLOAT; } // eslint-disable-line
  811. throw "unsupported typed array type";
  812. }
  813. // This is really just a guess. Though I can't really imagine using
  814. // anything else? Maybe for some compression?
  815. function getNormalizationForTypedArray(typedArray) {
  816. if (typedArray instanceof Int8Array) { return true; } // eslint-disable-line
  817. if (typedArray instanceof Uint8Array) { return true; } // eslint-disable-line
  818. return false;
  819. }
  820. function isArrayBuffer(a) {
  821. return a.buffer && a.buffer instanceof ArrayBuffer;
  822. }
  823. function guessNumComponentsFromName(name, length) {
  824. var numComponents;
  825. if (name.indexOf("coord") >= 0) {
  826. numComponents = 2;
  827. } else if (name.indexOf("color") >= 0) {
  828. numComponents = 4;
  829. } else {
  830. numComponents = 3; // position, normals, indices ...
  831. }
  832. if (length % numComponents > 0) {
  833. throw "can not guess numComponents. You should specify it.";
  834. }
  835. return numComponents;
  836. }
  837. function makeTypedArray(array, name) {
  838. if (isArrayBuffer(array)) {
  839. return array;
  840. }
  841. if (Array.isArray(array)) {
  842. array = {
  843. data: array,
  844. };
  845. }
  846. if (!array.numComponents) {
  847. array.numComponents = guessNumComponentsFromName(name, array.length);
  848. }
  849. var type = array.type;
  850. if (!type) {
  851. if (name === "indices") {
  852. type = Uint16Array;
  853. }
  854. }
  855. var typedArray = createAugmentedTypedArray(array.numComponents, array.data.length / array.numComponents | 0, type);
  856. typedArray.push(array.data);
  857. return typedArray;
  858. }
  859. /**
  860. * @typedef {Object} AttribInfo
  861. * @property {number} [numComponents] the number of components for this attribute.
  862. * @property {number} [size] the number of components for this attribute.
  863. * @property {number} [type] the type of the attribute (eg. `gl.FLOAT`, `gl.UNSIGNED_BYTE`, etc...) Default = `gl.FLOAT`
  864. * @property {boolean} [normalized] whether or not to normalize the data. Default = false
  865. * @property {number} [offset] offset into buffer in bytes. Default = 0
  866. * @property {number} [stride] the stride in bytes per element. Default = 0
  867. * @property {WebGLBuffer} buffer the buffer that contains the data for this attribute
  868. * @memberOf module:webgl-utils
  869. */
  870. /**
  871. * Creates a set of attribute data and WebGLBuffers from set of arrays
  872. *
  873. * Given
  874. *
  875. * var arrays = {
  876. * position: { numComponents: 3, data: [0, 0, 0, 10, 0, 0, 0, 10, 0, 10, 10, 0], },
  877. * texcoord: { numComponents: 2, data: [0, 0, 0, 1, 1, 0, 1, 1], },
  878. * normal: { numComponents: 3, data: [0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1], },
  879. * color: { numComponents: 4, data: [255, 255, 255, 255, 255, 0, 0, 255, 0, 0, 255, 255], type: Uint8Array, },
  880. * indices: { numComponents: 3, data: [0, 1, 2, 1, 2, 3], },
  881. * };
  882. *
  883. * returns something like
  884. *
  885. * var attribs = {
  886. * a_position: { numComponents: 3, type: gl.FLOAT, normalize: false, buffer: WebGLBuffer, },
  887. * a_texcoord: { numComponents: 2, type: gl.FLOAT, normalize: false, buffer: WebGLBuffer, },
  888. * a_normal: { numComponents: 3, type: gl.FLOAT, normalize: false, buffer: WebGLBuffer, },
  889. * a_color: { numComponents: 4, type: gl.UNSIGNED_BYTE, normalize: true, buffer: WebGLBuffer, },
  890. * };
  891. *
  892. * @param {WebGLRenderingContext} gl The webgl rendering context.
  893. * @param {Object.<string, array|typedarray>} arrays The arrays
  894. * @param {Object.<string, string>} [opt_mapping] mapping from attribute name to array name.
  895. * if not specified defaults to "a_name" -> "name".
  896. * @return {Object.<string, module:webgl-utils.AttribInfo>} the attribs
  897. * @memberOf module:webgl-utils
  898. */
  899. function createAttribsFromArrays(gl, arrays, opt_mapping) {
  900. var mapping = opt_mapping || createMapping(arrays);
  901. var attribs = {};
  902. Object.keys(mapping).forEach(function(attribName) {
  903. var bufferName = mapping[attribName];
  904. var array = makeTypedArray(arrays[bufferName], bufferName);
  905. attribs[attribName] = {
  906. buffer: createBufferFromTypedArray(gl, array),
  907. numComponents: array.numComponents || guessNumComponentsFromName(bufferName),
  908. type: getGLTypeForTypedArray(gl, array),
  909. normalize: getNormalizationForTypedArray(array),
  910. };
  911. });
  912. return attribs;
  913. }
  914. /**
  915. * tries to get the number of elements from a set of arrays.
  916. */
  917. function getNumElementsFromNonIndexedArrays(arrays) {
  918. var key = Object.keys(arrays)[0];
  919. var array = arrays[key];
  920. if (isArrayBuffer(array)) {
  921. return array.numElements;
  922. } else {
  923. return array.data.length / array.numComponents;
  924. }
  925. }
  926. /**
  927. * @typedef {Object} BufferInfo
  928. * @property {number} numElements The number of elements to pass to `gl.drawArrays` or `gl.drawElements`.
  929. * @property {WebGLBuffer} [indices] The indices `ELEMENT_ARRAY_BUFFER` if any indices exist.
  930. * @property {Object.<string, module:webgl-utils.AttribInfo>} attribs The attribs approriate to call `setAttributes`
  931. * @memberOf module:webgl-utils
  932. */
  933. /**
  934. * Creates a BufferInfo from an object of arrays.
  935. *
  936. * This can be passed to {@link module:webgl-utils.setBuffersAndAttributes} and to
  937. * {@link module:webgl-utils:drawBufferInfo}.
  938. *
  939. * Given an object like
  940. *
  941. * var arrays = {
  942. * position: { numComponents: 3, data: [0, 0, 0, 10, 0, 0, 0, 10, 0, 10, 10, 0], },
  943. * texcoord: { numComponents: 2, data: [0, 0, 0, 1, 1, 0, 1, 1], },
  944. * normal: { numComponents: 3, data: [0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1], },
  945. * indices: { numComponents: 3, data: [0, 1, 2, 1, 2, 3], },
  946. * };
  947. *
  948. * Creates an BufferInfo like this
  949. *
  950. * bufferInfo = {
  951. * numElements: 4, // or whatever the number of elements is
  952. * indices: WebGLBuffer, // this property will not exist if there are no indices
  953. * attribs: {
  954. * a_position: { buffer: WebGLBuffer, numComponents: 3, },
  955. * a_normal: { buffer: WebGLBuffer, numComponents: 3, },
  956. * a_texcoord: { buffer: WebGLBuffer, numComponents: 2, },
  957. * },
  958. * };
  959. *
  960. * The properties of arrays can be JavaScript arrays in which case the number of components
  961. * will be guessed.
  962. *
  963. * var arrays = {
  964. * position: [0, 0, 0, 10, 0, 0, 0, 10, 0, 10, 10, 0],
  965. * texcoord: [0, 0, 0, 1, 1, 0, 1, 1],
  966. * normal: [0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1],
  967. * indices: [0, 1, 2, 1, 2, 3],
  968. * };
  969. *
  970. * They can also by TypedArrays
  971. *
  972. * var arrays = {
  973. * position: new Float32Array([0, 0, 0, 10, 0, 0, 0, 10, 0, 10, 10, 0]),
  974. * texcoord: new Float32Array([0, 0, 0, 1, 1, 0, 1, 1]),
  975. * normal: new Float32Array([0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1]),
  976. * indices: new Uint16Array([0, 1, 2, 1, 2, 3]),
  977. * };
  978. *
  979. * Or augmentedTypedArrays
  980. *
  981. * var positions = createAugmentedTypedArray(3, 4);
  982. * var texcoords = createAugmentedTypedArray(2, 4);
  983. * var normals = createAugmentedTypedArray(3, 4);
  984. * var indices = createAugmentedTypedArray(3, 2, Uint16Array);
  985. *
  986. * positions.push([0, 0, 0, 10, 0, 0, 0, 10, 0, 10, 10, 0]);
  987. * texcoords.push([0, 0, 0, 1, 1, 0, 1, 1]);
  988. * normals.push([0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1]);
  989. * indices.push([0, 1, 2, 1, 2, 3]);
  990. *
  991. * var arrays = {
  992. * position: positions,
  993. * texcoord: texcoords,
  994. * normal: normals,
  995. * indices: indices,
  996. * };
  997. *
  998. * For the last example it is equivalent to
  999. *
  1000. * var bufferInfo = {
  1001. * attribs: {
  1002. * a_position: { numComponents: 3, buffer: gl.createBuffer(), },
  1003. * a_texcoods: { numComponents: 2, buffer: gl.createBuffer(), },
  1004. * a_normals: { numComponents: 3, buffer: gl.createBuffer(), },
  1005. * },
  1006. * indices: gl.createBuffer(),
  1007. * numElements: 6,
  1008. * };
  1009. *
  1010. * gl.bindBuffer(gl.ARRAY_BUFFER, bufferInfo.attribs.a_position.buffer);
  1011. * gl.bufferData(gl.ARRAY_BUFFER, arrays.position, gl.STATIC_DRAW);
  1012. * gl.bindBuffer(gl.ARRAY_BUFFER, bufferInfo.attribs.a_texcoord.buffer);
  1013. * gl.bufferData(gl.ARRAY_BUFFER, arrays.texcoord, gl.STATIC_DRAW);
  1014. * gl.bindBuffer(gl.ARRAY_BUFFER, bufferInfo.attribs.a_normal.buffer);
  1015. * gl.bufferData(gl.ARRAY_BUFFER, arrays.normal, gl.STATIC_DRAW);
  1016. * gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, bufferInfo.indices);
  1017. * gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, arrays.indices, gl.STATIC_DRAW);
  1018. *
  1019. * @param {WebGLRenderingContext} gl A WebGLRenderingContext
  1020. * @param {Object.<string, array|object|typedarray>} arrays Your data
  1021. * @param {Object.<string, string>} [opt_mapping] an optional mapping of attribute to array name.
  1022. * If not passed in it's assumed the array names will be mapped to an attibute
  1023. * of the same name with "a_" prefixed to it. An other words.
  1024. *
  1025. * var arrays = {
  1026. * position: ...,
  1027. * texcoord: ...,
  1028. * normal: ...,
  1029. * indices: ...,
  1030. * };
  1031. *
  1032. * bufferInfo = createBufferInfoFromArrays(gl, arrays);
  1033. *
  1034. * Is the same as
  1035. *
  1036. * var arrays = {
  1037. * position: ...,
  1038. * texcoord: ...,
  1039. * normal: ...,
  1040. * indices: ...,
  1041. * };
  1042. *
  1043. * var mapping = {
  1044. * a_position: "position",
  1045. * a_texcoord: "texcoord",
  1046. * a_normal: "normal",
  1047. * };
  1048. *
  1049. * bufferInfo = createBufferInfoFromArrays(gl, arrays, mapping);
  1050. *
  1051. * @return {module:webgl-utils.BufferInfo} A BufferInfo
  1052. * @memberOf module:webgl-utils
  1053. */
  1054. function createBufferInfoFromArrays(gl, arrays, opt_mapping) {
  1055. var bufferInfo = {
  1056. attribs: createAttribsFromArrays(gl, arrays, opt_mapping),
  1057. };
  1058. var indices = arrays.indices;
  1059. if (indices) {
  1060. indices = makeTypedArray(indices, "indices");
  1061. bufferInfo.indices = createBufferFromTypedArray(gl, indices, gl.ELEMENT_ARRAY_BUFFER);
  1062. bufferInfo.numElements = indices.length;
  1063. } else {
  1064. bufferInfo.numElements = getNumElementsFromNonIndexedArrays(arrays);
  1065. }
  1066. return bufferInfo;
  1067. }
  1068. /**
  1069. * Creates buffers from typed arrays
  1070. *
  1071. * Given something like this
  1072. *
  1073. * var arrays = {
  1074. * positions: [1, 2, 3],
  1075. * normals: [0, 0, 1],
  1076. * }
  1077. *
  1078. * returns something like
  1079. *
  1080. * buffers = {
  1081. * positions: WebGLBuffer,
  1082. * normals: WebGLBuffer,
  1083. * }
  1084. *
  1085. * If the buffer is named 'indices' it will be made an ELEMENT_ARRAY_BUFFER.
  1086. *
  1087. * @param {WebGLRenderingContext} gl A WebGLRenderingContext.
  1088. * @param {Object<string, array|typedarray>} arrays
  1089. * @return {Object<string, WebGLBuffer>} returns an object with one WebGLBuffer per array
  1090. * @memberOf module:webgl-utils
  1091. */
  1092. function createBuffersFromArrays(gl, arrays) {
  1093. var buffers = { };
  1094. Object.keys(arrays).forEach(function(key) {
  1095. var type = key === "indices" ? gl.ELEMENT_ARRAY_BUFFER : gl.ARRAY_BUFFER;
  1096. var array = makeTypedArray(arrays[key], name);
  1097. buffers[key] = createBufferFromTypedArray(gl, array, type);
  1098. });
  1099. // hrm
  1100. if (arrays.indices) {
  1101. buffers.numElements = arrays.indices.length;
  1102. } else if (arrays.position) {
  1103. buffers.numElements = arrays.position.length / 3;
  1104. }
  1105. return buffers;
  1106. }
  1107. /**
  1108. * Calls `gl.drawElements` or `gl.drawArrays`, whichever is appropriate
  1109. *
  1110. * normally you'd call `gl.drawElements` or `gl.drawArrays` yourself
  1111. * but calling this means if you switch from indexed data to non-indexed
  1112. * data you don't have to remember to update your draw call.
  1113. *
  1114. * @param {WebGLRenderingContext} gl A WebGLRenderingContext
  1115. * @param {module:webgl-utils.BufferInfo} bufferInfo as returned from createBufferInfoFromArrays
  1116. * @param {enum} [primitiveType] eg (gl.TRIANGLES, gl.LINES, gl.POINTS, gl.TRIANGLE_STRIP, ...)
  1117. * @param {number} [count] An optional count. Defaults to bufferInfo.numElements
  1118. * @param {number} [offset] An optional offset. Defaults to 0.
  1119. * @memberOf module:webgl-utils
  1120. */
  1121. function drawBufferInfo(gl, bufferInfo, primitiveType, count, offset) {
  1122. var indices = bufferInfo.indices;
  1123. primitiveType = primitiveType === undefined ? gl.TRIANGLES : primitiveType;
  1124. var numElements = count === undefined ? bufferInfo.numElements : count;
  1125. offset = offset === undefined ? offset : 0;
  1126. if (indices) {
  1127. gl.drawElements(primitiveType, numElements, gl.UNSIGNED_SHORT, offset);
  1128. } else {
  1129. gl.drawArrays(primitiveType, offset, numElements);
  1130. }
  1131. }
  1132. /**
  1133. * @typedef {Object} DrawObject
  1134. * @property {module:webgl-utils.ProgramInfo} programInfo A ProgramInfo as returned from createProgramInfo
  1135. * @property {module:webgl-utils.BufferInfo} bufferInfo A BufferInfo as returned from createBufferInfoFromArrays
  1136. * @property {Object<string, ?>} uniforms The values for the uniforms
  1137. * @memberOf module:webgl-utils
  1138. */
  1139. /**
  1140. * Draws a list of objects
  1141. * @param {WebGLRenderingContext} gl A WebGLRenderingContext
  1142. * @param {DrawObject[]} objectsToDraw an array of objects to draw.
  1143. * @memberOf module:webgl-utils
  1144. */
  1145. function drawObjectList(gl, objectsToDraw) {
  1146. var lastUsedProgramInfo = null;
  1147. var lastUsedBufferInfo = null;
  1148. objectsToDraw.forEach(function(object) {
  1149. var programInfo = object.programInfo;
  1150. var bufferInfo = object.bufferInfo;
  1151. var bindBuffers = false;
  1152. if (programInfo !== lastUsedProgramInfo) {
  1153. lastUsedProgramInfo = programInfo;
  1154. gl.useProgram(programInfo.program);
  1155. bindBuffers = true;
  1156. }
  1157. // Setup all the needed attributes.
  1158. if (bindBuffers || bufferInfo !== lastUsedBufferInfo) {
  1159. lastUsedBufferInfo = bufferInfo;
  1160. setBuffersAndAttributes(gl, programInfo.attribSetters, bufferInfo);
  1161. }
  1162. // Set the uniforms.
  1163. setUniforms(programInfo.uniformSetters, object.uniforms);
  1164. // Draw
  1165. drawBufferInfo(gl, bufferInfo);
  1166. });
  1167. }
  1168. var isIE = /*@cc_on!@*/false || !!document.documentMode;
  1169. // Edge 20+
  1170. var isEdge = !isIE && !!window.StyleMedia;
  1171. if (isEdge) {
  1172. // Hack for Edge. Edge's WebGL implmentation is crap still and so they
  1173. // only respond to "experimental-webgl". I don't want to clutter the
  1174. // examples with that so his hack works around it
  1175. HTMLCanvasElement.prototype.getContext = function(origFn) {
  1176. return function() {
  1177. var args = arguments;
  1178. var type = args[0];
  1179. if (type === "webgl") {
  1180. args = [].slice.call(arguments);
  1181. args[0] = "experimental-webgl";
  1182. }
  1183. return origFn.apply(this, args);
  1184. };
  1185. }(HTMLCanvasElement.prototype.getContext);
  1186. }
  1187. return {
  1188. createAugmentedTypedArray: createAugmentedTypedArray,
  1189. createAttribsFromArrays: createAttribsFromArrays,
  1190. createBuffersFromArrays: createBuffersFromArrays,
  1191. createBufferInfoFromArrays: createBufferInfoFromArrays,
  1192. createAttributeSetters: createAttributeSetters,
  1193. createProgram: createProgram,
  1194. createProgramFromScripts: createProgramFromScripts,
  1195. createProgramFromSources: createProgramFromSources,
  1196. createProgramInfo: createProgramInfo,
  1197. createUniformSetters: createUniformSetters,
  1198. createVAOAndSetAttributes: createVAOAndSetAttributes,
  1199. createVAOFromBufferInfo: createVAOFromBufferInfo,
  1200. drawBufferInfo: drawBufferInfo,
  1201. drawObjectList: drawObjectList,
  1202. getExtensionWithKnownPrefixes: getExtensionWithKnownPrefixes,
  1203. resizeCanvasToDisplaySize: resizeCanvasToDisplaySize,
  1204. setAttributes: setAttributes,
  1205. setBuffersAndAttributes: setBuffersAndAttributes,
  1206. setUniforms: setUniforms,
  1207. };
  1208. }));