macton-gl-utils.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663
  1. jQuery.glCheckError = function( gl, error_log )
  2. {
  3. var error = gl.getError();
  4. if (error != gl.NO_ERROR)
  5. {
  6. var error_str = "GL Error: " + error + " " + gl.enum_strings[error];
  7. if ( typeof error_log === 'function' )
  8. {
  9. error_log( error_str );
  10. }
  11. throw error_str;
  12. }
  13. }
  14. // jQuery.glProgram = function( gl, config, complete )
  15. //
  16. // Example config:
  17. //
  18. // var g_BumpReflectProgram =
  19. // {
  20. // VertexProgramURL: '/shaders/bump_reflect.vs',
  21. // FragmentProgramURL: '/shaders/bump_reflect.fs',
  22. // ErrorLog: error_log,
  23. // };
  24. jQuery.glProgram = function( gl, config, complete )
  25. {
  26. this.Program = null;
  27. this.UniformLocations = { };
  28. this.AttributeIndices = [];
  29. this.Uniforms = [];
  30. this.UniformTypes = { };
  31. this.Attributes = [];
  32. this.AttributeTypes = { };
  33. this.ErrorLog = config.error_log;
  34. this.BoundTextureCount = 0;
  35. this.Use = function()
  36. {
  37. gl.useProgram( this.Program );
  38. this.BoundTextureCount = 0;
  39. }
  40. this.BindModel = function( model, bindings )
  41. {
  42. var attribute_ndx = this.AttributeIndices;
  43. gl.bindBuffer(gl.ARRAY_BUFFER, model.VertexBuffer );
  44. for ( var model_stream_name in bindings )
  45. {
  46. var program_stream_name = bindings[ model_stream_name ];
  47. var program_attribute = this.AttributeIndices[ program_stream_name ];
  48. var model_stream_offset = model.VertexStreamBufferOffsets[ model_stream_name ];
  49. var model_stream_stride = model.VertexStreamBufferStrides[ model_stream_name ];
  50. var model_stream_type = model.VertexStreamBufferGLTypes[ model_stream_name ];
  51. gl.vertexAttribPointer( program_attribute, model_stream_stride, model_stream_type, false, 0, model_stream_offset );
  52. gl.enableVertexAttribArray( program_attribute );
  53. }
  54. gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, model.IndexBuffer );
  55. $.glCheckError( gl, this.ErrorLog );
  56. this.Model = model;
  57. }
  58. this.BindUniform = function( uniform_name, value )
  59. {
  60. var uniform_type = this.UniformTypes[ uniform_name ];
  61. switch (uniform_type)
  62. {
  63. case 'mat4':
  64. {
  65. gl.uniformMatrix4fv( this.UniformLocations[ uniform_name ], gl.FALSE, new Float32Array( value ) );
  66. }
  67. break;
  68. case 'sampler2D':
  69. {
  70. gl.activeTexture( gl[ 'TEXTURE' + this.BoundTextureCount ] );
  71. if ( value instanceof jQuery.glTexture )
  72. {
  73. gl.bindTexture( gl.TEXTURE_2D, value.Texture );
  74. }
  75. else
  76. {
  77. gl.bindTexture( gl.TEXTURE_2D, value );
  78. }
  79. gl.uniform1i( this.UniformLocations[ uniform_name ], this.BoundTextureCount );
  80. this.BoundTextureCount++;
  81. }
  82. break;
  83. case 'samplerCube':
  84. {
  85. gl.activeTexture( gl[ 'TEXTURE' + this.BoundTextureCount ] );
  86. gl.bindTexture( gl.TEXTURE_CUBE_MAP, value );
  87. gl.uniform1i( this.UniformLocations[ uniform_name ], this.BoundTextureCount );
  88. this.BoundTextureCount++;
  89. }
  90. break;
  91. }
  92. }
  93. this.DrawModel = function()
  94. {
  95. var model = this.Model; // Model must be bound with BindModel first.
  96. gl.drawElements( gl.TRIANGLES, model.IndexCount, model.IndexStreamGLType, 0 );
  97. }
  98. this.CreateBestVertexBindings = function( model )
  99. {
  100. var bindings = new Object();
  101. for (var i=0;i<model.VertexStreamNames.length;i++)
  102. {
  103. var vertex_stream_name = model.VertexStreamNames[i];
  104. var best_dist = 1000;
  105. for (var j=0;j<this.Attributes.length;j++)
  106. {
  107. var attribute_name = this.Attributes[j];
  108. var dist = vertex_stream_name.LD( attribute_name );
  109. if ( dist < best_dist )
  110. {
  111. bindings[ vertex_stream_name ] = attribute_name;
  112. best_dist = dist;
  113. }
  114. }
  115. }
  116. return (bindings);
  117. }
  118. var vertex_shader = null;
  119. var fragment_shader = null;
  120. var CompileShader = function( type, source )
  121. {
  122. var shader = gl.createShader(type);
  123. if (shader == null)
  124. {
  125. return null;
  126. }
  127. gl.shaderSource(shader, source);
  128. gl.compileShader(shader);
  129. if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS))
  130. {
  131. var infoLog = gl.getShaderInfoLog(shader);
  132. output("Error compiling shader:\n" + infoLog);
  133. gl.deleteShader(shader);
  134. return null;
  135. }
  136. return shader;
  137. }
  138. var TryErrorLog = function( msg )
  139. {
  140. if ( typeof config.ErrorLog !== 'function' ) return;
  141. config.ErrorLog( msg );
  142. }
  143. var TryBuildProgram = function()
  144. {
  145. if ( vertex_shader == null ) return;
  146. if ( fragment_shader == null ) return;
  147. this.Program = gl.createProgram();
  148. if (this.Program == null)
  149. {
  150. TryErrorLog("Could not create GL program");
  151. return;
  152. }
  153. gl.attachShader( this.Program, vertex_shader );
  154. gl.attachShader( this.Program, fragment_shader );
  155. for (var i=0;i<this.Attributes.length;i++)
  156. {
  157. var attribute_name = this.Attributes[i];
  158. this.AttributeIndices[ attribute_name ] = i;
  159. gl.bindAttribLocation( this.Program, i, attribute_name );
  160. }
  161. gl.linkProgram( this.Program );
  162. var linked = gl.getProgramParameter( this.Program, gl.LINK_STATUS );
  163. if (!linked)
  164. {
  165. var infoLog = gl.getProgramInfoLog(this.Program);
  166. TryErrorLog("GL program link failed: " + infoLog);
  167. gl.deleteProgram( this.Program );
  168. this.Program = null;
  169. return;
  170. }
  171. for (var i=0;i<this.Uniforms.length;i++)
  172. {
  173. var uniform_name = this.Uniforms[i];
  174. this.UniformLocations[ uniform_name ] = gl.getUniformLocation( this.Program, uniform_name );
  175. }
  176. $.glCheckError( gl, config.error_log );
  177. if ( typeof complete === 'function' ) complete( this );
  178. }
  179. var AddUniforms = function( source )
  180. {
  181. var uniform_match = /uniform\s+(\w+)\s+(\w+)\s*;/g
  182. var uniforms = source.match( uniform_match );
  183. for (var i=0;i<uniforms.length;i++)
  184. {
  185. var uniform = uniforms[i].split( uniform_match );
  186. var uniform_type = uniform[1];
  187. var uniform_name = uniform[2];
  188. this.Uniforms.push( uniform_name );
  189. this.UniformTypes[ uniform_name ] = uniform_type;
  190. }
  191. }
  192. var AddAttributes = function( source )
  193. {
  194. var attribute_match = /attribute\s+(\w+)\s+(\w+)\s*;/g
  195. var attributes = source.match( attribute_match );
  196. for (var i=0;i<attributes.length;i++)
  197. {
  198. var attribute = attributes[i].split( attribute_match );
  199. var attribute_type = attribute[1];
  200. var attribute_name = attribute[2];
  201. this.Attributes.push( attribute_name );
  202. this.AttributeTypes[ attribute_name ] = attribute_type;
  203. }
  204. }
  205. var LoadVertexShader = function( source )
  206. {
  207. vertex_shader = CompileShader.apply( this, [ gl.VERTEX_SHADER, source ] );
  208. AddUniforms.apply( this, [ source ] );
  209. AddAttributes.apply( this, [ source ] );
  210. TryBuildProgram.apply( this );
  211. }
  212. var LoadFragmentShader = function( source )
  213. {
  214. fragment_shader = CompileShader.apply( this, [ gl.FRAGMENT_SHADER, source ] );
  215. AddUniforms.apply( this, [ source ] );
  216. TryBuildProgram.apply( this );
  217. }
  218. var that = this;
  219. this.get = function( url, fn )
  220. {
  221. var request = new XMLHttpRequest();
  222. request.open( "GET", url, true );
  223. request.send( null );
  224. fn( request.responseText );
  225. }
  226. this.get( config.VertexProgramURL, function( source ) { LoadVertexShader.apply( that, arguments ); } );
  227. this.get( config.FragmentProgramURL, function( source ) { LoadFragmentShader.apply( that, arguments ); } );
  228. // $.get( config.VertexProgramURL, function( source ) { LoadVertexShader.apply( that, arguments ); } );
  229. // $.get( config.FragmentProgramURL, function( source ) { LoadFragmentShader.apply( that, arguments ); } );
  230. }
  231. // jQuery.glModel = function( gl, model_url, complete )
  232. //
  233. // Model JSON format:
  234. // {
  235. // VertexStreams: [
  236. // {
  237. // Name: "position",
  238. // Type: "float",
  239. // Stride: 3,
  240. // Stream: [ 1.0, 1.0, 1.0, ... ]
  241. // }
  242. // ,...
  243. // ]
  244. // Indices:
  245. // {
  246. // Type: "uint16_t",
  247. // Stream: [ 0, 1, 2, 2, 3, 4, ... ]
  248. // }
  249. // }
  250. // }
  251. jQuery.glModel = function( gl, model_url, complete )
  252. {
  253. this.VertexStreamNames = [];
  254. this.VertexStreamBuffers = [];
  255. this.VertexStreamBufferOffsets = [];
  256. this.VertexStreamBufferStrides = [];
  257. this.VertexStreamBufferGLTypes = [];
  258. this.VertexBuffer = null;
  259. this.IndexStreamBuffer = null;
  260. this.IndexStreamGLType = null;
  261. this.IndexBuffer = null;
  262. this.IndexCount = 0;
  263. var LoadModel = function( source )
  264. {
  265. var CreateArrayByStdType = function( type, source )
  266. {
  267. if (type == 'float')
  268. {
  269. return new Float32Array( source );
  270. }
  271. else if (type == 'uint16_t')
  272. {
  273. return new Uint16Array( source );
  274. }
  275. else if (type == 'uint32_t')
  276. {
  277. return new Uint32Array( source );
  278. }
  279. else if (type == 'uint8_t')
  280. {
  281. return new Uint8Array( source );
  282. }
  283. }
  284. var GLTypeByStdType = function( type )
  285. {
  286. if (type == 'float')
  287. {
  288. return gl.FLOAT;
  289. }
  290. else if (type == 'uint16_t')
  291. {
  292. return gl.UNSIGNED_SHORT;
  293. }
  294. else if (type == 'uint32_t')
  295. {
  296. return gl.UNSIGNED_INT;
  297. }
  298. else if (type == 'uint8_t')
  299. {
  300. return gl.UNSIGNED_BYTE;
  301. }
  302. }
  303. var vertex_stream_count = source.VertexStreams.length;
  304. var vertex_buffer_size = 0;
  305. for (var i=0;i<vertex_stream_count;i++)
  306. {
  307. var stream_source = source.VertexStreams[i];
  308. var stream_name = stream_source.Name;
  309. var stream_buffer = CreateArrayByStdType( stream_source.Type, stream_source.Stream );
  310. var stream_stride = stream_source.Stride;
  311. var stream_type = stream_source.Type;
  312. var stream_gl_type = GLTypeByStdType( stream_type );
  313. this.VertexStreamBuffers[i] = stream_buffer;
  314. this.VertexStreamNames[i] = stream_name;
  315. this.VertexStreamBufferOffsets[ stream_name ] = vertex_buffer_size;
  316. this.VertexStreamBufferStrides[ stream_name ] = stream_stride;
  317. this.VertexStreamBufferGLTypes[ stream_name ] = stream_gl_type;
  318. if ( stream_buffer )
  319. {
  320. vertex_buffer_size += stream_buffer.byteLength;
  321. }
  322. }
  323. this.VertexBuffer = gl.createBuffer();
  324. gl.bindBuffer( gl.ARRAY_BUFFER, this.VertexBuffer );
  325. gl.bufferData( gl.ARRAY_BUFFER, vertex_buffer_size, gl.STATIC_DRAW );
  326. for (var i=0;i<vertex_stream_count;i++)
  327. {
  328. var stream_source = source.VertexStreams[i];
  329. var stream_name = stream_source.Name;
  330. gl.bufferSubData( gl.ARRAY_BUFFER, this.VertexStreamBufferOffsets[ stream_name ], this.VertexStreamBuffers[i] );
  331. }
  332. this.IndexBuffer = gl.createBuffer();
  333. this.IndexStreamBuffer = CreateArrayByStdType( source.Indices.Type, source.Indices.Stream );
  334. this.IndexStreamGLType = GLTypeByStdType( source.Indices.Type );
  335. this.IndexCount = source.Indices.Stream.length;
  336. gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, this.IndexBuffer );
  337. gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, this.IndexStreamBuffer, gl.STATIC_DRAW );
  338. if ( typeof complete === 'function' ) complete( this );
  339. }
  340. var that = this;
  341. $.ajax({
  342. url: model_url,
  343. dataType: 'json',
  344. mimeType: 'application/json',
  345. success: function( source ) { LoadModel.apply( that, arguments ); }
  346. });
  347. //$.getJSON( model_url, function( source ) { LoadModel.apply( that, arguments ); } );
  348. }
  349. jQuery.glModel2 = function( gl, model, complete )
  350. {
  351. this.VertexStreamNames = [];
  352. this.VertexStreamBuffers = [];
  353. this.VertexStreamBufferOffsets = [];
  354. this.VertexStreamBufferStrides = [];
  355. this.VertexStreamBufferGLTypes = [];
  356. this.VertexBuffer = null;
  357. this.IndexStreamBuffer = null;
  358. this.IndexStreamGLType = null;
  359. this.IndexBuffer = null;
  360. this.IndexCount = 0;
  361. var LoadModel = function( source )
  362. {
  363. var CreateArrayByStdType = function( type, source )
  364. {
  365. if (type == 'float')
  366. {
  367. return new Float32Array( source );
  368. }
  369. else if (type == 'uint16_t')
  370. {
  371. return new Uint16Array( source );
  372. }
  373. else if (type == 'uint32_t')
  374. {
  375. return new Uint32Array( source );
  376. }
  377. else if (type == 'uint8_t')
  378. {
  379. return new Uint8Array( source );
  380. }
  381. }
  382. var GLTypeByStdType = function( type )
  383. {
  384. if (type == 'float')
  385. {
  386. return gl.FLOAT;
  387. }
  388. else if (type == 'uint16_t')
  389. {
  390. return gl.UNSIGNED_SHORT;
  391. }
  392. else if (type == 'uint32_t')
  393. {
  394. return gl.UNSIGNED_INT;
  395. }
  396. else if (type == 'uint8_t')
  397. {
  398. return gl.UNSIGNED_BYTE;
  399. }
  400. }
  401. var vertex_stream_count = source.VertexStreams.length;
  402. var vertex_buffer_size = 0;
  403. for (var i=0;i<vertex_stream_count;i++)
  404. {
  405. var stream_source = source.VertexStreams[i];
  406. var stream_name = stream_source.Name;
  407. var stream_buffer = CreateArrayByStdType( stream_source.Type, stream_source.Stream );
  408. var stream_stride = stream_source.Stride;
  409. var stream_type = stream_source.Type;
  410. var stream_gl_type = GLTypeByStdType( stream_type );
  411. this.VertexStreamBuffers[i] = stream_buffer;
  412. this.VertexStreamNames[i] = stream_name;
  413. this.VertexStreamBufferOffsets[ stream_name ] = vertex_buffer_size;
  414. this.VertexStreamBufferStrides[ stream_name ] = stream_stride;
  415. this.VertexStreamBufferGLTypes[ stream_name ] = stream_gl_type;
  416. if ( stream_buffer )
  417. {
  418. vertex_buffer_size += stream_buffer.byteLength;
  419. }
  420. }
  421. this.VertexBuffer = gl.createBuffer();
  422. gl.bindBuffer( gl.ARRAY_BUFFER, this.VertexBuffer );
  423. gl.bufferData( gl.ARRAY_BUFFER, vertex_buffer_size, gl.STATIC_DRAW );
  424. for (var i=0;i<vertex_stream_count;i++)
  425. {
  426. var stream_source = source.VertexStreams[i];
  427. var stream_name = stream_source.Name;
  428. gl.bufferSubData( gl.ARRAY_BUFFER, this.VertexStreamBufferOffsets[ stream_name ], this.VertexStreamBuffers[i] );
  429. }
  430. this.IndexBuffer = gl.createBuffer();
  431. this.IndexStreamBuffer = CreateArrayByStdType( source.Indices.Type, source.Indices.Stream );
  432. this.IndexStreamGLType = GLTypeByStdType( source.Indices.Type );
  433. this.IndexCount = source.Indices.Stream.length;
  434. gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, this.IndexBuffer );
  435. gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, this.IndexStreamBuffer, gl.STATIC_DRAW );
  436. if ( typeof complete === 'function' ) complete( this );
  437. }
  438. var that = this;
  439. /*
  440. $.ajax({
  441. url: model_url,
  442. dataType: 'json',
  443. mimeType: 'application/json',
  444. success: function( source ) { LoadModel.apply( that, arguments ); }
  445. });
  446. */
  447. LoadModel.apply( this, [ model ] );
  448. }
  449. // jQuery.glTexture = function( gl, config, complete )
  450. //
  451. // Example config:
  452. //
  453. // var g_BumpTextureConfig =
  454. // {
  455. // Type: 'TEXTURE_2D',
  456. // ImageURL: './images/bump.jpg',
  457. // TexParameters:
  458. // {
  459. // TEXTURE_MIN_FILTER: 'LINEAR',
  460. // TEXTURE_MAG_FILTER: 'LINEAR',
  461. // TEXTURE_WRAP_S: 'REPEAT',
  462. // TEXTURE_WRAP_T: 'REPEAT'
  463. // },
  464. // PixelStoreParameters:
  465. // {
  466. // UNPACK_FLIP_Y_WEBGL: true
  467. // }
  468. // };
  469. jQuery.glTexture = function( gl, config, complete )
  470. {
  471. this.Texture = gl.createTexture();
  472. this.ErrorLog = config.ErrorLog;
  473. var texture_type = gl[ config.Type ];
  474. var images_remaining;
  475. var that = this;
  476. if ( this.Texture == null )
  477. {
  478. return null;
  479. }
  480. gl.bindTexture( texture_type, this.Texture );
  481. for ( var pname in config.TexParameters )
  482. {
  483. var pvalue = config.TexParameters[ pname ];
  484. var parameter_name = gl[ pname ];
  485. var parameter_value = gl[ pvalue ];
  486. gl.texParameteri( texture_type, parameter_name, parameter_value );
  487. $.glCheckError( gl, this.ErrorLog );
  488. }
  489. var ImageLoaded = function( image, image_type, mip )
  490. {
  491. gl.bindTexture( texture_type, this.Texture );
  492. $.glCheckError( gl, this.ErrorLog );
  493. for ( var pname in config.PixelStoreParameters )
  494. {
  495. var parameter_name = gl[ pname ];
  496. var parameter_value = config.PixelStoreParameters[ pname ];
  497. gl.pixelStorei( parameter_name, parameter_value );
  498. $.glCheckError( gl, this.ErrorLog );
  499. }
  500. gl.texImage2D( image_type, mip, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image );
  501. $.glCheckError( gl, this.ErrorLog );
  502. if ( texture_type === gl.TEXTURE_CUBE_MAP && image_type === gl.TEXTURE_CUBE_MAP_NEGATIVE_Z )
  503. gl.generateMipmap( texture_type );
  504. if ( texture_type === gl.TEXTURE_2D && config.MipURL == undefined )
  505. gl.generateMipmap( texture_type );
  506. $.glCheckError( gl, this.ErrorLog );
  507. images_remaining--;
  508. if ( images_remaining == 0 )
  509. {
  510. if ( typeof complete === 'function' ) complete( this );
  511. }
  512. }
  513. var LoadImage = function( src, image_type, mip )
  514. {
  515. var image = new Image();
  516. image.onload = function() { ImageLoaded.apply( that, [ image, image_type, mip ] ); };
  517. image.src = src;
  518. }
  519. if ( typeof config.ImageURL == 'string' )
  520. {
  521. images_remaining = 1;
  522. if ( config.MipURL !== undefined )
  523. {
  524. images_remaining += config.MipURL.length;
  525. }
  526. LoadImage( config.ImageURL, gl[ config.Type ], 0 );
  527. if ( config.MipURL !== undefined )
  528. {
  529. for (var i=0;i<config.MipURL.length;i++)
  530. {
  531. LoadImage( config.MipURL[i], gl[ config.Type ], i + 1 );
  532. }
  533. }
  534. }
  535. else
  536. {
  537. images_remaining = config.ImageURL.length;
  538. for (var i=0;i<config.ImageURL.length;i++)
  539. {
  540. LoadImage( config.ImageURL[i], gl[ config.ImageType[i] ], 0 );
  541. }
  542. }
  543. }