1 /* 2 Copyright (c) 2008 Seneca College 3 Licenced under the MIT License (http://www.c3dl.org/index.php/mit-license/) 4 */ 5 6 /** 7 @class WebGL context. 8 @augments c3dl.Renderer 9 */ 10 c3dl.WebGL = function() 11 { 12 var glCanvas3D = null; // GL Context; 13 this.texManager = null; 14 15 // overwrite the version set in the Renderer base class. 16 this.version = 2.0; 17 18 // overwrite the version set in the Renderer base class. 19 this.versionString = "WebGL"; 20 21 // program objects to render various visual objects. 22 this.geometryShader; 23 this.particleSystemShader; 24 this.pointShader; 25 this.pointSphereShader; 26 this.lineShader; 27 this.boundingSphereShader; 28 this.programsWithLights = []; 29 30 /// Maybe need to move these out somewhere else 31 this.pointVertBuffer = null; 32 this.pointColBuffer = null; 33 34 this.lineVertBuffer = null; 35 this.lineColBuffer = null; 36 37 // unique id of this renderer 38 this.ID = c3dl.getNextRendererID(); 39 this.STANDARD_PROGRAM_ID = null; 40 41 this.textureQueue = []; 42 43 // have the vbos to render point spheres been created 44 this.pointSphereRenderReady = false; 45 46 // Verts, Normals, etc will be added to this. 47 this.pointsphereVBOVert; 48 49 /** 50 Add a texture to the renderer. If the texture was added before 51 the renderer was initialized, the texture will be placed in a 52 queue and the texture will be created once the renderer has been 53 initialized. 54 55 The renderer will create an internal ID for the texture which can 56 be queried by calling getTextureID. This ID can be passed to commands 57 of the context in the rendering callback function in effects to make 58 that texture active. 59 60 @parma {String} path Texture path 61 */ 62 this.addTexture = function(path) 63 { 64 // 65 if(this.texManager == null) 66 { 67 this.textureQueue.push(path); 68 } 69 else 70 { 71 this.texManager.addTexture(path); 72 } 73 } 74 75 /** 76 Get the unique ID of this renderer. 77 78 @returns {int} unique ID of this renderer. 79 */ 80 this.getID = function() 81 { 82 return this.ID; 83 } 84 85 /** 86 Get the ID of the texture at 'texturePath'. If the texture could not 87 be found or the renderer has not yet been initialized, -1 will be 88 returned. 89 90 @param {String} texturePath 91 92 @returns {int} The ID of the texture, or -1 if the renderer has not 93 yet been initialized or if the texture was not found. 94 */ 95 this.getTextureID = function(texturePath) 96 { 97 if(this.texManager) 98 { 99 return this.texManager.getID(texturePath); 100 } 101 else 102 { 103 return -1; 104 } 105 } 106 107 /** 108 @private 109 Is the renderer ready? 110 111 @returns {boolean} True if the context is not null, otherwise false. 112 */ 113 this.isReady = function() 114 { 115 return glCanvas3D == null ? false : true; 116 } 117 118 /** 119 Get the OpenGL context. 120 121 @returns {Context} The GL Context. 122 */ 123 this.getGLContext = function() 124 { 125 return glCanvas3D; 126 } 127 128 /** 129 @private 130 Create a program which is composed of shaders. 131 132 Create a program object which is composed of compiled shader objects. 133 This program object can be installed as the current rendering state 134 by using gl.useProgram(). 135 136 @param {Array|String} vertexShaderSource The source code for the vertex shader. 137 @param {Array|String} fragmentShaderSource The source code for the fragment shader. 138 139 @return {c3dl.ProgramObject} ProgramObject or null . 140 */ 141 this.createProgram = function(vertexShader, fragmentShader) 142 { 143 // We don't check the parameters because the openGL functions will already 144 // be checking to make sure the source is valid when it compiles them. If 145 // they are invalid, error messages will be displayed on the page. 146 147 // make alias for shorter code. 148 var gl = glCanvas3D; 149 150 // createProgram creates a program object to which our shaders can be 151 // attached. We can later tell openGL which program to use by 152 // calling useProgram(). 153 var program = gl.createProgram(); 154 155 // it is possible createProgram failed. 156 if (program == null) 157 { 158 c3dl.debug.logError("failed to create shader program"); 159 return null; 160 } 161 162 var vertShader = gl.createShader(gl.VERTEX_SHADER); 163 gl.shaderSource(vertShader, vertexShader); 164 165 166 gl.compileShader(vertShader); 167 168 // The compilation status of each shader can be queried. 169 if (!gl.getShaderParameter(vertShader, gl.COMPILE_STATUS)) 170 { 171 c3dl.debug.logError("vert shader: " + gl.getShaderInfoLog(vertShader)); 172 gl.deleteShader(vertShader); 173 return null; 174 } 175 176 gl.attachShader(program, vertShader); 177 178 179 var vertShader = gl.createShader(gl.FRAGMENT_SHADER); 180 gl.shaderSource(vertShader, fragmentShader); 181 gl.compileShader(vertShader); 182 183 // The compilation status of each shader can be queried. 184 if (!gl.getShaderParameter(vertShader, gl.COMPILE_STATUS)) 185 { 186 c3dl.debug.logError("frag shader " +gl.getShaderInfoLog(vertShader)); 187 gl.deleteShader(vertShader); 188 return null; 189 } 190 gl.attachShader(program, vertShader); 191 192 // Linking is the final step which must be done to obtain a valid 193 // program object. Linking assigns variable locations for uniform variables, 194 // initialized user-defined uniform variables, resolves references 195 // between independently compiled shader objects, etc. 196 // 197 // The status of the link operation is stored as part of the program object's 198 // state which we will query. 199 // 200 gl.linkProgram(program); 201 202 // Check if the shaders were linked successfully. 203 if (gl.getProgramParameter(program, gl.LINK_STATUS) != 1) 204 { 205 c3dl.debug.logError(gl.getProgramInfoLog(program)); 206 gl.deleteProgram(program); 207 return null; 208 } 209 210 // create a program object, similar to an opengl program object. 211 var programObject = new c3dl.ProgramObject(); 212 programObject.rendererID = this.ID; 213 programObject.programID = program; 214 215 return programObject; 216 } 217 218 /** 219 @private 220 Clear the color and depth buffers. 221 222 Scene is responsible for calling this. 223 */ 224 this.clearBuffers = function() 225 { 226 glCanvas3D.clear(glCanvas3D.COLOR_BUFFER_BIT | glCanvas3D.DEPTH_BUFFER_BIT); 227 } 228 229 /** 230 @private 231 Swap the front and back buffers. Scene is responsible for calling this. 232 */ 233 this.swapBuffers = function() 234 { 235 glCanvas3D.clear(glCanvas3D.COLOR_BUFFER_BIT | glCanvas3D.DEPTH_BUFFER_BIT); 236 } 237 238 /** 239 @private 240 this is documented in the renderer class 241 */ 242 this.setClearColor = function(bgColor) 243 { 244 if( bgColor.length >= 3 ) 245 { 246 glCanvas3D.clearColor(bgColor[0],bgColor[1],bgColor[2],1.0); 247 } 248 } 249 250 /** 251 @private 252 253 implementes the 'virtual' function getMaxLineWidth from renderer. 254 255 Get the maximum line width supported which is implementation 256 dependent. 257 258 @returns {int} maximum line width supported. 259 */ 260 this.getMaxLineWidth = function() 261 { 262 // returns the range, first value represents minimum value supported. 263 // opengles guarantees support for width of 1. 264 265 // should this be checked once on init and then we don't have to keep querying? 266 var maxLineWidth = glCanvas3D.getParameter(glCanvas3D.ALIASED_LINE_WIDTH_RANGE); 267 268 return maxLineWidth[1]; 269 } 270 271 /** 272 @private 273 Set the shader values to zero so the light no longer affects the scene. 274 275 @param {int} lightID The light to clear must range from 0 to one less than c3dl.MAX_LIGHTS. 276 */ 277 this.clearLight = function(lightID) 278 { 279 if(lightID >= 0 && lightID < c3dl.MAX_LIGHTS) 280 { 281 for(var i = 0; i < this.programsWithLights.length; i++) 282 { 283 var PID = this.programsWithLights[i]; 284 285 // base string to shorten code below. 286 var base = "lights[" + lightID + "]."; 287 288 glCanvas3D.useProgram(PID); 289 this.setUniformf(PID, base + "position", [0,0,0]); 290 this.setUniformf(PID, base + "ambient", [0,0,0]); 291 this.setUniformf(PID, base + "diffuse", [0,0,0]); 292 this.setUniformf(PID, base + "specular", [0,0,0]); 293 this.setUniformf(PID, base + "spotDirection", [0,0,-1]); 294 this.setUniformf(PID, base + "spotCutoff", 180); 295 this.setUniformf(PID, base + "spotExponent", 0); 296 this.setUniformf(PID, base + "attenuation1", 1); 297 this.setUniformf(PID, base + "attenuation2", 0); 298 this.setUniformf(PID, base + "attenuation3", 0); 299 this.setUniformi(PID, base + "type", 0); 300 this.setUniformi(PID, base + "isOn", 0); 301 } 302 } 303 } 304 305 /** 306 @private 307 308 @param {Array} ambientLight Array of lights 309 */ 310 this.updateAmbientLight = function(ambientLight) 311 { 312 // the toon shader uses lights, but does not use 313 // the ambient light. We need to turn off debugger to 314 // suppress any errors. 315 var prevVal = c3dl.debug.getVisible(); 316 c3dl.debug.setVisible(false); 317 318 for(var i = 0; i < this.programsWithLights.length; i++) 319 { 320 glCanvas3D.useProgram(this.programsWithLights[i]); 321 this.setUniformf(this.programsWithLights[i], "ambientLightColor", ambientLight); 322 this.setUniformi(this.programsWithLights[i], "lightingOn", this.getLighting()); 323 } 324 325 // turn it back on if it was on before. 326 if(prevVal == true) 327 { 328 c3dl.debug.setVisible(true); 329 } 330 } 331 332 /** 333 @private 334 Update the light states in the shader with lightList. 335 336 @param {Array} lightList Array of lights 337 */ 338 this.updateLights = function(lightList) 339 { 340 // The list of all the program objects which have lights need to be updated 341 for(var progObjIter = 0; progObjIter < this.programsWithLights.length; progObjIter++) 342 { 343 var shader = this.programsWithLights[progObjIter]; 344 345 glCanvas3D.useProgram(shader); 346 347 // iterate over all the lights 348 for(var i = 0 ; i < lightList.length; i++) 349 { 350 // create a base string to shorten code below. 351 var base = "lights[" + i + "]."; 352 353 // we may have nulls in the array which represent places lights can be inserted 354 // so we have to check for these. 355 if(lightList[i] != null) 356 { 357 // if the light is off, that's the only uniform var that needs to be set. 358 if(lightList[i].isOn() == false) 359 { 360 this.setUniformi(shader, base + "isOn", lightList[i].isOn()); 361 } 362 else 363 { 364 if(lightList[i] instanceof c3dl.DirectionalLight) 365 { 366 // place the light in viewspace here instead of the shader, preventing placing the lights 367 // in viewspace for every vertex. 368 var dir = c3dl.multiplyMatrixByDirection(c3dl.peekMatrix(),lightList[i].getDirection()); 369 dir.push(0); 370 371 this.setUniformf(shader, base + "position", dir); 372 373 // this is used to distinguish a directional light from a spotlight. 374 this.setUniformf(shader, base + "spotCutoff", 180); 375 } 376 377 // check if its a spotlight first before positional light! 378 else if(lightList[i] instanceof c3dl.SpotLight) 379 { 380 var pos = lightList[i].getPosition(); 381 pos = c3dl.multiplyMatrixByVector(c3dl.peekMatrix(), pos); 382 pos.push(1); 383 384 var dir = lightList[i].getDirection(); 385 dir = c3dl.multiplyMatrixByDirection(c3dl.peekMatrix(), dir); 386 387 this.setUniformf(shader, base + "position", pos); 388 this.setUniformf(shader, base + "spotDirection", dir); 389 this.setUniformf(shader, base + "spotCutoff", lightList[i].getCutoff()); 390 this.setUniformf(shader, base + "spotExponent", lightList[i].getExponent()); 391 } 392 393 else if(lightList[i] instanceof c3dl.PositionalLight) 394 { 395 var pos = lightList[i].getPosition(); 396 //pos = c3dl.multiplyMatrixByVector(c3dl.getUniform("viewMatrix"), pos); 397 pos = c3dl.multiplyMatrixByVector(c3dl.peekMatrix(), pos); 398 pos.push(1); 399 400 this.setUniformf(shader, base + "position", pos); 401 this.setUniformf(shader, base + "spotCutoff", 180.0); 402 } 403 404 this.setUniformi(shader, base + "type", lightList[i].getType()); 405 this.setUniformi(shader, base + "isOn", lightList[i].isOn()); 406 this.setUniformf(shader, base + "ambient", lightList[i].getAmbient()); 407 this.setUniformf(shader, base + "diffuse", lightList[i].getDiffuse()); 408 this.setUniformf(shader, base + "specular", lightList[i].getSpecular()); 409 410 // lights are attenuated as long as they are not directional lights 411 if(!(lightList[i] instanceof c3dl.DirectionalLight)) 412 { 413 var attn = lightList[i].getAttenuation(); 414 this.setUniformf(shader, base + "attenuation1", attn[0]); 415 this.setUniformf(shader, base + "attenuation2", attn[1]); 416 this.setUniformf(shader, base + "attenuation3", attn[2]); 417 } 418 } 419 } 420 } 421 } 422 } 423 424 /* 425 426 */ 427 this.pointSphereRenderSetup = function() 428 { 429 // create the empty WebGL VBO's 430 this.pointSphereVBOVert = glCanvas3D.createBuffer(); 431 432 // bind to the VBO 433 glCanvas3D.bindBuffer(glCanvas3D.ARRAY_BUFFER,this.pointSphereVBOVert); 434 435 // set the data using the bounding sphere verts since that sphere has a size of 1 unit 436 glCanvas3D.bufferData(glCanvas3D.ARRAY_BUFFER, new WebGLFloatArray(c3dl.BOUNDING_SPHERE_VERTICES),glCanvas3D.STATIC_DRAW); 437 438 // next frame we'll be ready to render 439 this.pointSphereRenderReady = true; 440 } 441 442 /** 443 @private 444 445 Create a Renderer. 446 447 @param cvs 448 449 @returns {boolean} True if the context could be created, 450 otherwise false. 451 */ 452 this.createRenderer = function(cvs) 453 { 454 if (c3dl.debug.DUMMY) 455 { 456 glCanvas3D = {}; 457 glCanvas3D.__noSuchMethod__ = function(){return true;} 458 } 459 else 460 { 461 try 462 { 463 glCanvas3D = cvs.getContext('experimental-webgl'); 464 glCanvas3D.viewport(0, 0, cvs.width, cvs.height); 465 } 466 catch(err){} 467 } 468 469 return glCanvas3D ? true : false; 470 } 471 472 /** 473 @private 474 Enables depth testing, create necessary shaders, create the projection 475 matrix and set the lighting uniform. 476 477 Compiles and link the shaders. 478 479 @param {int} width of the canvas in pixels. 480 @param {int} height of the canvas in pixels. 481 */ 482 this.init = function(width, height) 483 { 484 if (glCanvas3D == null) 485 { 486 return false; 487 } 488 489 // set the context width and height. These are the base class 490 // members. 491 this.contextWidth = width; 492 this.contextHeight = height; 493 494 // enable the depth buffer, only needs to be done once, so do it here 495 glCanvas3D.enable(glCanvas3D.DEPTH_TEST); 496 497 // we need to define this ourselves since it is not present in canvas 3d 0.4.3 498 this.enable(c3dl.VERTEX_PROGRAM_POINT_SIZE); 499 500 // create the shader programs 501 //this.geometryShader = this.createProgram(c3dl.material_vs+c3dl.light_vs+c3dl.model_vs, c3dl.model_fs).getProgramID(); 502 this.particleSystemShader = this.createProgram(c3dl.psys_vs, c3dl.psys_fs).getProgramID(); 503 this.pointShader = this.createProgram(c3dl.point_vs, c3dl.point_fs).getProgramID(); 504 this.lineShader = this.createProgram(c3dl.line_vs, c3dl.line_fs).getProgramID(); 505 this.pointSphereShader = this.createProgram(c3dl.point_sphere_vs, c3dl.point_sphere_fs).getProgramID(); 506 this.boundingSphereShader = this.createProgram(c3dl.bounding_sphere_vs, c3dl.bounding_sphere_fs).getProgramID(); 507 508 // Template effects 509 510 // STANDARD 511 c3dl.effects.STD_EFFECT = new c3dl.EffectTemplate(); 512 c3dl.effects.STD_EFFECT.addVertexShader(c3dl.material_vs+c3dl.light_vs+c3dl.model_vs); 513 c3dl.effects.STD_EFFECT.addFragmentShader(c3dl.model_fs); 514 c3dl.effects.STD_EFFECT.setRenderingCallback(c3dl.std_callback); 515 c3dl.effects.STD_EFFECT.init(); 516 517 c3dl.effects.STANDARD = new c3dl.Effect(); 518 c3dl.effects.STANDARD.init(c3dl.effects.STD_EFFECT); 519 var prog = this.createProgram(c3dl.material_vs+c3dl.light_vs+c3dl.model_vs, c3dl.model_fs); 520 521 c3dl.effects.STANDARD.getEffectTemplate().addProgramObject(prog); 522 this.programsWithLights.push(c3dl.effects.STANDARD.getEffectTemplate().getProgramID(this.ID)); 523 this.STANDARD_PROGRAM_ID = prog.getProgramID(); 524 525 // need to create the solid color effect explicitly 526 // since effects are really only created if an object uses them. 527 // and since we need it for gooch and cartoon.... 528 /// 529 c3dl.effects.SOLID_COLOR_EFFECT_TEMP = new c3dl.EffectTemplate(); 530 c3dl.effects.SOLID_COLOR_EFFECT_TEMP.addVertexShader(c3dl.solid_color_vs); 531 c3dl.effects.SOLID_COLOR_EFFECT_TEMP.addFragmentShader(c3dl.solid_color_fs); 532 c3dl.effects.SOLID_COLOR_EFFECT_TEMP.setRenderingCallback(c3dl.solid_color_callback); 533 c3dl.effects.SOLID_COLOR_EFFECT_TEMP.init(); 534 535 c3dl.effects.SOLID_COLOR_EFFECT = new c3dl.Effect(); 536 c3dl.effects.SOLID_COLOR_EFFECT.init(c3dl.effects.SOLID_COLOR_EFFECT_TEMP); 537 var prog = this.createProgram(c3dl.solid_color_vs, c3dl.solid_color_fs); 538 539 c3dl.effects.SOLID_COLOR_EFFECT.getEffectTemplate().addProgramObject(prog); 540 this.SOLID_COLOR_EFFECT_ID = prog.getProgramID(); 541 542 // GREYSCALE 543 c3dl.effects.GREYSCALE = new c3dl.EffectTemplate(); 544 c3dl.effects.GREYSCALE.addVertexShader(c3dl.material_vs); 545 c3dl.effects.GREYSCALE.addVertexShader(c3dl.light_vs); 546 c3dl.effects.GREYSCALE.addVertexShader(c3dl.greyscale_vs); 547 c3dl.effects.GREYSCALE.addFragmentShader(c3dl.greyscale_fs); 548 c3dl.effects.GREYSCALE.setRenderingCallback(c3dl.greyscale_callback); 549 c3dl.effects.GREYSCALE.addParameter("color", Array, [0.3, 0.6, 0.1]); 550 c3dl.effects.GREYSCALE.init(); 551 552 // SOLID COLOR 553 c3dl.effects.SOLID_COLOR = new c3dl.EffectTemplate(); 554 c3dl.effects.SOLID_COLOR.addVertexShader(c3dl.solid_color_vs); 555 c3dl.effects.SOLID_COLOR.addFragmentShader(c3dl.solid_color_fs); 556 c3dl.effects.SOLID_COLOR.setRenderingCallback(c3dl.solid_color_callback); 557 c3dl.effects.SOLID_COLOR.addParameter("color", Array, [0.0, 0.0, 0.0]); 558 c3dl.effects.SOLID_COLOR.init(); 559 560 // SEPIA 561 c3dl.effects.SEPIA = new c3dl.EffectTemplate(); 562 c3dl.effects.SEPIA.addVertexShader(c3dl.material_vs); 563 c3dl.effects.SEPIA.addVertexShader(c3dl.light_vs); 564 c3dl.effects.SEPIA.addVertexShader(c3dl.sepia_vs); 565 c3dl.effects.SEPIA.addFragmentShader(c3dl.sepia_fs); 566 c3dl.effects.SEPIA.setRenderingCallback(c3dl.sepia_callback); 567 c3dl.effects.SEPIA.addParameter("color", Array, [1.2, 1.0, 0.8]); 568 c3dl.effects.SEPIA.init(); 569 570 // CARTOON 571 c3dl.effects.CARTOON = new c3dl.EffectTemplate(); 572 c3dl.effects.CARTOON.addVertexShader(c3dl.cartoon_vs); 573 c3dl.effects.CARTOON.addFragmentShader(c3dl.light_vs); 574 c3dl.effects.CARTOON.addFragmentShader(c3dl.cartoon_fs); 575 c3dl.effects.CARTOON.setRenderingCallback(c3dl.cartoon_callback); 576 c3dl.effects.CARTOON.addParameter("qMap", String); 577 c3dl.effects.CARTOON.addParameter("outline", Boolean, true); 578 c3dl.effects.CARTOON.init(); 579 580 // GOOCH 581 c3dl.effects.GOOCH = new c3dl.EffectTemplate(); 582 c3dl.effects.GOOCH.addVertexShader(c3dl.gooch_vs); 583 c3dl.effects.GOOCH.addFragmentShader(c3dl.light_vs); 584 c3dl.effects.GOOCH.addFragmentShader(c3dl.gooch_fs); 585 c3dl.effects.GOOCH.setRenderingCallback(c3dl.gooch_callback); 586 c3dl.effects.GOOCH.addParameter("coolColor", Array, [0,0,1] ); 587 c3dl.effects.GOOCH.addParameter("warmColor", Array, [0.5,0.5,0.0]); 588 c3dl.effects.GOOCH.addParameter("surfaceColor", Array, [0.1,0.1,0.1]); 589 c3dl.effects.GOOCH.addParameter("outline", Boolean, true); 590 c3dl.effects.GOOCH.init(); 591 592 this.texManager = new c3dl.TextureManager(glCanvas3D); 593 594 // iterate over all the textures the user added before the renderer 595 // was initialized and add them now. 596 for(var i in this.textureQueue) 597 { 598 if(this.textureQueue[i]) 599 { 600 this.texManager.addTexture(this.textureQueue[i]); 601 } 602 } 603 604 return true; 605 } 606 607 /** 608 @private 609 Render the bounding sphere 610 611 @param {c3dl.BoundingSphere} boundingSphere 612 */ 613 this.renderBoundingSphere = function(boundingSphere) 614 { 615 // create an short alias 616 var shader = this.boundingSphereShader; 617 glCanvas3D.useProgram(shader); 618 619 if( this.pointSphereRenderReady == false ) 620 { 621 // create VBO and set the data 622 this.pointSphereRenderSetup(); 623 } 624 else 625 { 626 c3dl.pushMatrix(); 627 628 var top = c3dl.peekMatrix(); 629 var mat = c3dl.makeIdentityMatrix(); 630 631 // set the bounding sphere's position 632 mat[12] = top[12] + boundingSphere.getPosition()[0]; 633 mat[13] = top[13] + boundingSphere.getPosition()[1]; 634 mat[14] = top[14] + boundingSphere.getPosition()[2]; 635 mat[0] = mat[5] = mat[10] = boundingSphere.getRadius(); 636 637 c3dl.matrixMode(c3dl.PROJECTION); 638 var proj = c3dl.peekMatrix(); 639 c3dl.matrixMode(c3dl.MODELVIEW); 640 641 // create a modelviewprojection matrix. By doing this, we can multiply 642 // 3 matrices together once per model instead of once per vertex. 643 var MVPMatrix = c3dl.multiplyMatrixByMatrix(proj, mat ); 644 this.setUniformMatrix(shader, "modelViewProjMatrix", MVPMatrix); 645 646 this.setVertexAttribArray(shader, "Vertex", 3, this.pointSphereVBOVert); 647 glCanvas3D.drawArrays(glCanvas3D.POINTS,0, c3dl.BOUNDING_SPHERE_VERTICES.length/3); 648 c3dl.popMatrix(); 649 } 650 } 651 652 /** 653 @private 654 Render a geometry 655 656 @param {c3dl.Geometry} obj 657 */ 658 this.renderGeometry = function(obj) 659 { 660 // get the object's effect 661 if(obj.getEffect()) 662 { 663 // 664 var effect = obj.getEffect().getEffectTemplate(); 665 666 var progObjID = effect.getProgramID(this.ID); 667 668 // If the effect's shaders have not yet been compiled for this renderer, 669 // compile them. 670 if( progObjID == -1) 671 { 672 var vertexShaders = effect.getVertexShaders(); 673 var fragmentShaders = effect.getFragmentShaders(); 674 675 // join all the shaders, but don't insert a delimiter, that 676 // would create a syntax error for the shaders. 677 var joinedVertexShaders = vertexShaders.join(''); 678 var joinedFragmentShaders = fragmentShaders.join(''); 679 680 var programObject = this.createProgram(joinedVertexShaders, joinedFragmentShaders); 681 682 // if the effect was successfully compiled, render it using the effect 683 if(programObject) 684 { 685 effect.addProgramObject(programObject); 686 687 // check if the user added the light vertex shader source. 688 // if they did, we'll need to update the light states every 689 // update for that shader. 690 for(var i = 0; i < vertexShaders.length; i++) 691 { 692 if(vertexShaders[i] == c3dl.light_vs) 693 { 694 this.programsWithLights.push(programObject.getProgramID()); 695 glCanvas3D.useProgram(programObject.getProgramID()); 696 this.setUniformi(programObject.getProgramID(), "lightingOn", true); 697 } 698 } 699 700 for(var i = 0; i < fragmentShaders.length; i++) 701 { 702 if(fragmentShaders[i] == c3dl.light_vs) 703 { 704 this.programsWithLights.push(programObject.getProgramID()); 705 glCanvas3D.useProgram(programObject.getProgramID()); 706 this.setUniformi(programObject.getProgramID(), "lightingOn", true); 707 } 708 } 709 } 710 else 711 { 712 c3dl.debug.logWarning("could not compile effect shader(s)."); 713 c3dl.debug.logInfo(joinedVertexShaders); 714 c3dl.debug.logInfo(joinedFragmentShaders); 715 } 716 } 717 // if the effect has already been compiled, go ahead and render the geometry. 718 else 719 { 720 var renderingObj = {}; 721 722 // user will need to query states of the context. 723 renderingObj["context"] = glCanvas3D; 724 renderingObj["getContext"] = function (){ return this.context;}; 725 726 // user will need to query states of the renderer. 727 // enventually the renderer will completely abtract the context and 728 // the context will not need to be passed in. But that requires all 729 // functions to be wrapped. 730 renderingObj["renderer"] = this; 731 renderingObj["getRenderer"] = function (){ return this.renderer;}; 732 733 // user will need to set the current program. 734 renderingObj['programObjectID'] = progObjID; 735 renderingObj['getProgramObjectID'] = function(){ return this.programObjectID;}; 736 737 // user will need the actual geometry to render. 738 renderingObj['geometry'] = obj; 739 renderingObj['getGeometry'] = function(){ return this.geometry;}; 740 741 var cb = effect.getRenderingCallback(); 742 cb(renderingObj); 743 } 744 745 } 746 747 // if the geometry didn't have an effect, we'll render it using 748 // the standard shader. 749 else 750 { 751 var renderingObj = {}; 752 753 // 754 renderingObj["context"] = glCanvas3D; 755 renderingObj["getContext"] = function (){ return this.context;}; 756 757 // 758 renderingObj["renderer"] = this; 759 renderingObj["getRenderer"] = function (){ return this.renderer;}; 760 761 // 762 renderingObj['programObjectID'] = this.STANDARD_PROGRAM_ID; 763 renderingObj['getProgramObjectID'] = function(){ return this.programObjectID;}; 764 765 // 766 renderingObj['geometry'] = obj; 767 renderingObj['getGeometry'] = function(){ return this.geometry;}; 768 769 c3dl.std_callback(renderingObj); 770 } 771 } 772 773 774 /** 775 @private 776 Render a particle system. 777 778 @param {c3dl.ParticleSystem} psys 779 */ 780 this.renderParticleSystem = function(psys) 781 { 782 // create shorter alias 783 var shader = this.particleSystemShader; 784 glCanvas3D.useProgram(shader); 785 786 var usingTexture = false; 787 var texturePath = psys.getTexture(); 788 var texID = this.texManager.getID(texturePath); 789 var texAttribLoc = glCanvas3D.getAttribLocation(shader, "Texture"); 790 791 // if the texture isn't loaded, but this collation element has one, 792 // queue one up 793 if(texID == -1 && texturePath) 794 { 795 this.texManager.addTexture(texturePath); 796 } 797 798 if(texID != -1 && texturePath && psys.getTexCoords()) 799 { 800 glCanvas3D.activeTexture(glCanvas3D.TEXTURE0); 801 glCanvas3D.bindTexture(glCanvas3D.TEXTURE_2D,texID); 802 this.setVertexAttribArray(shader, "Texture", 2, psys.getVBOTexCoords()); 803 usingTexture = true; 804 805 glCanvas3D.texParameteri( glCanvas3D.TEXTURE_2D, glCanvas3D.TEXTURE_WRAP_S, glCanvas3D.CLAMP_TO_EDGE); 806 glCanvas3D.texParameteri( glCanvas3D.TEXTURE_2D, glCanvas3D.TEXTURE_WRAP_T, glCanvas3D.CLAMP_TO_EDGE); 807 } 808 else 809 { 810 glCanvas3D.activeTexture(glCanvas3D.TEXTURE0); 811 glCanvas3D.disableVertexAttribArray(texAttribLoc); 812 //glCanvas3D.bindTexture(glCanvas3D.TEXTURE_2D,-1); 813 } 814 this.setUniformi(shader, "usingTexture", usingTexture); 815 this.setUniformMatrix(shader, "rot", [1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1]); 816 this.setVertexAttribArray(shader, "Vertex", 3, psys.getVBOVertices()); 817 818 for(var i = 0; i < psys.getNumParticles(); i++) 819 { 820 if(psys.getParticle(i).isAlive()) 821 { 822 var pSize = psys.getParticle(i).getSize(); 823 824 this.setUniformf(shader, "Color", psys.getParticle(i).getColor()); 825 826 c3dl.matrixMode(c3dl.PROJECTION); 827 var projectionMatrix = c3dl.peekMatrix(); 828 c3dl.matrixMode(c3dl.MODELVIEW); 829 var viewMatrix = c3dl.peekMatrix(); 830 831 // 832 var modelMatrix = psys.getParticle(i).getTransform(); 833 modelMatrix = c3dl.multiplyMatrixByMatrix(modelMatrix, [pSize,0,0,0, 0,pSize,0,0, 0,0,pSize,0, 0,0,0,1]); 834 835 // create a ModelViewProjection matrix. By doing this, we can multiply 836 // 3 matrices together once per particle instead of once per vertex 837 var modelViewProjMatrix = c3dl.multiplyMatrixByMatrix(viewMatrix,modelMatrix); 838 modelViewProjMatrix = c3dl.multiplyMatrixByMatrix(projectionMatrix, modelViewProjMatrix ); 839 this.setUniformMatrix(shader, "modelViewProjMatrix", modelViewProjMatrix); 840 841 glCanvas3D.drawArrays(glCanvas3D.TRIANGLE_FAN, i , 4); 842 } 843 } 844 } 845 846 /** 847 @private 848 Render a set of lines. 849 850 @param {Array} lines 851 */ 852 this.renderLines = function(lines) 853 { 854 if(lines.length > 0) 855 { 856 // create a short alias for this.lineShader 857 var shader = this.lineShader; 858 glCanvas3D.useProgram(shader); 859 860 // camera placed the view matrix at the bottom of the stack 861 var modelViewMatrix = c3dl.peekMatrix(); 862 c3dl.matrixMode(c3dl.PROJECTION); 863 var projectionMatrix = c3dl.peekMatrix(); 864 c3dl.matrixMode(c3dl.MODELVIEW); 865 866 // create a ModelViewProjection matrix. By doing this, we can multiply 867 // 3 matrices together once per model instead of once per vertex 868 var modelViewProjMatrix = c3dl.multiplyMatrixByMatrix(projectionMatrix, modelViewMatrix ); 869 this.setUniformMatrix(shader, "modelViewProjMatrix", modelViewProjMatrix); 870 871 872 // we need to render each line individually since each can have 873 // a different width. 874 glCanvas3D.lineWidth(4); 875 876 var coords = []; 877 var cols = []; 878 for(var i=0; i < lines.length; i++) 879 { 880 coords.push(lines[i].getCoordinates()[0]); 881 coords.push(lines[i].getCoordinates()[1]); 882 coords.push(lines[i].getCoordinates()[2]); 883 coords.push(lines[i].getCoordinates()[3]); 884 coords.push(lines[i].getCoordinates()[4]); 885 coords.push(lines[i].getCoordinates()[5]); 886 887 cols.push(lines[i].getColors()[0]); 888 cols.push(lines[i].getColors()[1]); 889 cols.push(lines[i].getColors()[2]); 890 cols.push(lines[i].getColors()[3]); 891 cols.push(lines[i].getColors()[4]); 892 cols.push(lines[i].getColors()[5]); 893 } 894 895 if(this.lineVertBuffer == null) 896 { 897 this.lineVertBuffer = {}; 898 this.lineVertBuffer.position = glCanvas3D.createBuffer(); 899 } 900 901 glCanvas3D.bindBuffer(glCanvas3D.ARRAY_BUFFER, this.lineVertBuffer.position); 902 glCanvas3D.bufferData(glCanvas3D.ARRAY_BUFFER, new WebGLFloatArray(coords), glCanvas3D.STREAM_DRAW); 903 this.setVertexAttribArray(shader, "Vertex", 3, this.lineVertBuffer.position ); 904 905 if(this.lineColBuffer == null) 906 { 907 this.lineColBuffer = {}; 908 this.lineColBuffer.position = glCanvas3D.createBuffer(); 909 } 910 911 glCanvas3D.bindBuffer(glCanvas3D.ARRAY_BUFFER, this.lineColBuffer.position); 912 glCanvas3D.bufferData(glCanvas3D.ARRAY_BUFFER, new WebGLFloatArray(cols), glCanvas3D.STREAM_DRAW); 913 this.setVertexAttribArray(shader, "Color", 3, this.lineColBuffer.position ); 914 915 glCanvas3D.drawArrays(glCanvas3D.LINES, 0, coords.length/3); 916 } 917 } 918 919 /** 920 @private 921 @param {Array} pointPositions 922 @param {Array} pointColors 923 @param {Array} attenuation 924 @param {bool} pointSmooth 925 @param {int} mode 926 @param {float} size 927 */ 928 this.renderPoints = function(pointPositions, pointColors, attenuation, pointSmooth, mode, size) 929 { 930 // trying to render an empty list will result in an opengl error 931 if(pointPositions.length > 0 && pointColors.length > 0) 932 { 933 if( mode == c3dl.POINT_MODE_POINT) 934 { 935 // create a shorter reference name. 936 var shader = this.pointShader; 937 glCanvas3D.useProgram(shader); 938 939 // camera placed the view matrix at the bottom of the stack 940 var viewMatrix = c3dl.peekMatrix(); 941 c3dl.matrixMode(c3dl.PROJECTION); 942 var projectionMatrix = c3dl.peekMatrix(); 943 c3dl.matrixMode(c3dl.MODELVIEW); 944 945 // if point smoothing is on, points will be rendered as circles, 946 // otherwise they will be rendered as squares. 947 pointSmooth ? this.enable(c3dl.POINT_SMOOTH) : this.disable(c3dl.POINT_SMOOTH); 948 949 // create a ModelViewProjection matrix. By doing this, we can multiply 950 // 3 matrices together once instead of once per point 951 var modelViewProjMatrix = c3dl.multiplyMatrixByMatrix(projectionMatrix, viewMatrix ); 952 this.setUniformMatrix(shader, "viewMatrix", viewMatrix); 953 this.setUniformMatrix(shader, "modelViewProjMatrix", modelViewProjMatrix); 954 this.setUniformf(shader, "attenuation", attenuation); 955 956 if(this.pointVertBuffer == null) 957 { 958 this.pointVertBuffer = {}; 959 this.pointVertBuffer.position = glCanvas3D.createBuffer(); 960 } 961 962 glCanvas3D.bindBuffer(glCanvas3D.ARRAY_BUFFER, this.pointVertBuffer.position); 963 glCanvas3D.bufferData(glCanvas3D.ARRAY_BUFFER, new WebGLFloatArray(pointPositions), glCanvas3D.STREAM_DRAW); 964 this.setVertexAttribArray(shader, "Vertex", 3, this.pointVertBuffer.position); 965 966 if(this.pointColBuffer == null) 967 { 968 this.pointColBuffer = {}; 969 this.pointColBuffer.position = glCanvas3D.createBuffer(); 970 } 971 972 glCanvas3D.bindBuffer(glCanvas3D.ARRAY_BUFFER, this.pointColBuffer.position); 973 glCanvas3D.bufferData(glCanvas3D.ARRAY_BUFFER, new WebGLFloatArray(pointColors), glCanvas3D.STREAM_DRAW); 974 this.setVertexAttribArray(shader, "Color", 3, this.pointColBuffer.position); 975 glCanvas3D.drawArrays(glCanvas3D.POINTS, 0, pointPositions.length/3); 976 } 977 else if(mode == c3dl.POINT_MODE_SPHERE) 978 { 979 // create an short alias 980 var shader = this.pointSphereShader; 981 glCanvas3D.useProgram(shader); 982 983 if( this.pointSphereRenderReady == false ) 984 { 985 // create vbo and set the data 986 this.pointSphereRenderSetup(); 987 } 988 else 989 { 990 c3dl.pushMatrix(); 991 992 for( var i=0 ; i < pointPositions.length; i+=3) 993 { 994 // 995 var mat = c3dl.makeIdentityMatrix(); 996 mat[12] = pointPositions[i]; 997 mat[13] = pointPositions[i+1]; 998 mat[14] = pointPositions[i+2]; 999 mat[0] = mat[5] = mat[10] = size; 1000 1001 mat = c3dl.multiplyMatrixByMatrix(c3dl.peekMatrix(), mat); 1002 1003 c3dl.matrixMode(c3dl.PROJECTION); 1004 var proj = c3dl.peekMatrix(); 1005 c3dl.matrixMode(c3dl.MODELVIEW); 1006 1007 // create a modelviewprojection matrix. By doing this, we can multiply 1008 // 3 matrices together once per model instead of once per vertex. 1009 var MVPMatrix = c3dl.multiplyMatrixByMatrix(proj, mat ); 1010 this.setUniformMatrix(shader, "modelViewProjMatrix", MVPMatrix); 1011 this.setUniformf(shader, "Color", [pointColors[i],pointColors[i+1],pointColors[i+2]]); 1012 1013 this.setVertexAttribArray(shader, "Vertex", 3, this.pointSphereVBOVert); 1014 glCanvas3D.drawArrays(glCanvas3D.TRIANGLES,0, c3dl.BOUNDING_SPHERE_VERTICES.length/3); 1015 c3dl.popMatrix(); 1016 } 1017 } 1018 } 1019 } 1020 } 1021 1022 /** 1023 @private 1024 1025 @param {int} shader 1026 @param {String} varName 1027 @param {int} size 1028 @param {Array} array 1029 */ 1030 /*this.setVertexAttribArray = function(shader, varName, size, array, vbo) 1031 { 1032 var attribLoc = glCanvas3D.getAttribLocation(shader, varName); 1033 1034 if(attribLoc != c3dl.SHADER_VAR_NOT_FOUND) 1035 { 1036 glCanvas3D.bindBuffer(glCanvas3D.ARRAY_BUFFER, vbo); 1037 glCanvas3D.vertexAttribPointer(attribLoc, size, glCanvas3D.FLOAT, false, 0, 0); 1038 glCanvas3D.enableVertexAttribArray(attribLoc); 1039 } 1040 else 1041 { 1042 c3dl.debug.logError("Attribute variable '" + varName + "' not found in shader with ID = " + shader); 1043 } 1044 }*/ 1045 1046 1047 1048 this.setVertexAttribArray = function(shader, varName, size, vbo) 1049 { 1050 var attribLoc = glCanvas3D.getAttribLocation(shader, varName); 1051 1052 if(attribLoc != c3dl.SHADER_VAR_NOT_FOUND) 1053 { 1054 glCanvas3D.bindBuffer(glCanvas3D.ARRAY_BUFFER, vbo); 1055 glCanvas3D.vertexAttribPointer(attribLoc, size, glCanvas3D.FLOAT, false, 0, 0); 1056 glCanvas3D.enableVertexAttribArray(attribLoc); 1057 } 1058 else 1059 { 1060 c3dl.debug.logError("Attribute variable '" + varName + "' not found in shader with ID = " + shader); 1061 } 1062 } 1063 1064 /** 1065 Sets the uniform matrix variable 'varName' to the value specified by 'value'. 1066 Before calling this function, you must ensure the program object with 1067 the variable name 'varName' has been installed as part of the current 1068 rendering state by calling useProgram().<br /> 1069 <br /> 1070 The size of the matrix must be 16 elements.<br /> 1071 <br /> 1072 On some systems, if the variable exists in the shader but isn't used, 1073 the compiler will remove it. An error message will be displayed if the 1074 non-existant variable is set.<br /> 1075 <br /> 1076 If the variable could not be found for any other reason, an error is 1077 displayed. 1078 1079 @param {int} programObjectID The shader where the variable resides. 1080 @param {String} varName The name of the matrix variable. 1081 @param {Array} matrix 16 element matrix. 1082 */ 1083 this.setUniformMatrix = function(programObjectID, varName, matrix) 1084 { 1085 var varLocation = glCanvas3D.getUniformLocation(programObjectID, varName); 1086 1087 // the variable won't be found if it was optimized out. 1088 if( varLocation != c3dl.SHADER_VAR_NOT_FOUND) 1089 { 1090 glCanvas3D.uniformMatrix4fv(varLocation, false, matrix); 1091 } 1092 else 1093 { 1094 c3dl.debug.logError("Uniform matrix variable '" + varName + "' not found in program object."); 1095 } 1096 } 1097 1098 /** 1099 Sets the uniform variable 'varName' to the value specified by 'value'. 1100 Before calling this function, you must ensure the program object with 1101 the variable name 'varName' has been installed as part of the current 1102 rendering state by calling useProgram().<br /> 1103 <br /> 1104 The size of the value will be queried and depending on its size, the 1105 appropriate OpenGL command will be called either uniform1f or 1106 uniform{1|2|3|4}fv.<br /> 1107 <br /> 1108 On some systems, if the variable exists in the shader but isn't used, 1109 the compiler will remove it. An error message will be displayed if the 1110 non-existant variable is set.<br /> 1111 <br /> 1112 If the variable could not be found for any other reason, an error is 1113 displayed. 1114 1115 @param {int} programObjectID The shader where the variable resides. 1116 @param {String} varName The name of the variable. 1117 @param {float|Array} value The value to assign the variable. 1118 */ 1119 this.setUniformf = function (shader, varName, value) 1120 { 1121 var varLocation = glCanvas3D.getUniformLocation(shader, varName); 1122 // the variable won't be found if it was optimized out. 1123 if( varLocation != c3dl.SHADER_VAR_NOT_FOUND) 1124 { 1125 if (value.length == 4){glCanvas3D.uniform4fv( varLocation, value);} 1126 else if (value.length == 3){glCanvas3D.uniform3fv( varLocation, value);} 1127 else if (value.length == 2){glCanvas3D.uniform2fv( varLocation, value);} 1128 else {glCanvas3D.uniform1f( varLocation, value);} 1129 } 1130 else 1131 { 1132 c3dl.debug.logError('Uniform variable "' + varName + '" not found in program object.'); 1133 } 1134 } 1135 1136 /** 1137 Sets the uniform variable 'varName' to the value specified by 'value'. 1138 Before calling this function, you must ensure the program object with 1139 the variable name 'varName' has been installed as part of the current 1140 rendering state by calling useProgram().<br /> 1141 <br /> 1142 The size of the value will be queried and depending on its size, the 1143 appropriate OpenGL command will be called either uniform1i or 1144 uniform{1|2|3|4}iv.<br /> 1145 <br /> 1146 On some systems, if the variable exists in the shader but isn't used, 1147 the compiler will remove it. An error message will be displayed if the 1148 non-existant variable is set.<br /> 1149 <br /> 1150 If the variable could not be found for any other reason, an error is 1151 displayed. 1152 1153 @param {int} programObjectID The shader where the variable resides. 1154 @param {String} varName The name of the variable. 1155 @param {int|Array} value The value to assign the variable. 1156 */ 1157 this.setUniformi = function (programObjectID, varName, value) 1158 { 1159 var varLocation = glCanvas3D.getUniformLocation(programObjectID, varName); 1160 1161 // the variable won't be found if it was optimized out. 1162 if( varLocation != c3dl.SHADER_VAR_NOT_FOUND) 1163 { 1164 if (value.length == 4){glCanvas3D.uniform4iv(varLocation, value);} 1165 else if (value.length == 3){glCanvas3D.uniform3iv(varLocation, value);} 1166 else if (value.length == 2){glCanvas3D.uniform2iv(varLocation, value);} 1167 else {glCanvas3D.uniform1i(varLocation, value);} 1168 } 1169 else 1170 { 1171 c3dl.debug.logError('Uniform variable "' + varName + '" not found in program object.'); 1172 } 1173 } 1174 1175 /** 1176 @private 1177 Enable an OpenGL server-side capability. 1178 1179 @param {int} capability 1180 */ 1181 this.enable = function(capability) 1182 { 1183 try{ 1184 // check if its defined 1185 if(capability) 1186 { 1187 glCanvas3D.enable(capability); 1188 } 1189 else 1190 { 1191 c3dl.debug.logWarning("Enable command passed undefined value."); 1192 } 1193 } 1194 catch(e) 1195 { 1196 c3dl.debug.logException("Exception name:" + e.name + "<br />" + 1197 "Exception msg: " + e.message + "<br />" + 1198 "Capability: " + capability); 1199 } 1200 } 1201 1202 /** 1203 @private 1204 1205 Disable a server-side OpenGL capability. This wraps the OpenGL ES 1206 'disable' command and adds a conditional to make sure the capability is 1207 supported before trying to change the state. If the capability is not 1208 supported and the state is change, the script could be prevented 1209 from running. 1210 1211 @param {int} capability OpenGL ES capability 1212 */ 1213 this.disable = function(capability) 1214 { 1215 if(capability) 1216 { 1217 glCanvas3D.disable(capability); 1218 } 1219 else 1220 { 1221 c3dl.debug.logWarning("disable command passed undefined value."); 1222 } 1223 } 1224 } 1225 1226 c3dl.WebGL.prototype = new c3dl.Renderer; 1227