1 /****************************************************************************
  2  Copyright (c) 2008-2010 Ricardo Quesada
  3  Copyright (c) 2011-2012 cocos2d-x.org
  4  Copyright (c) 2013-2014 Chukong Technologies Inc.
  5  Copyright 2011 Jeff Lamarche
  6  Copyright 2012 Goffredo Marocchi
  7 
  8  http://www.cocos2d-x.org
  9 
 10  Permission is hereby granted, free of charge, to any person obtaining a copy
 11  of this software and associated documentation files (the "Software"), to deal
 12  in the Software without restriction, including without limitation the rights
 13  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 14  copies of the Software, and to permit persons to whom the Software is
 15  furnished to do so, subject to the following conditions:
 16 
 17  The above copyright notice and this permission notice shall be included in
 18  all copies or substantial portions of the Software.
 19 
 20  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 21  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 22  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 23  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 24  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 25  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 26  THE SOFTWARE.
 27  ****************************************************************************/
 28 
 29 cc.HashUniformEntry = function (value, location, hh) {
 30     this.value = value;
 31     this.location = location;
 32     this.hh = hh || {};
 33 };
 34 
 35 /**
 36  * Class that implements a WebGL program
 37  * @class
 38  * @extends cc.Class
 39  */
 40 cc.GLProgram = cc.Class.extend(/** @lends cc.GLProgram# */{
 41     _glContext: null,
 42     _programObj: null,
 43     _vertShader: null,
 44     _fragShader: null,
 45     _uniforms: null,
 46     _hashForUniforms: null,
 47     _usesTime: false,
 48 
 49     // Uniform cache
 50     _updateUniformLocation: function (location, data, bytes) {
 51         if (location == null)
 52             return false;
 53 
 54         var updated = true;
 55         var element = null;
 56         for (var i = 0; i < this._hashForUniforms.length; i++)
 57             if (this._hashForUniforms[i].location == location)
 58                 element = this._hashForUniforms[i];
 59 
 60         if (!element) {
 61             element = new cc.HashUniformEntry();
 62             // key
 63             element.location = location;
 64             // value
 65             element.value = data;
 66             this._hashForUniforms.push(element);
 67         } else {
 68             if (element.value == data)
 69                 updated = false;
 70             else
 71                 element.value = data;
 72         }
 73 
 74         return updated;
 75     },
 76 
 77     _description: function () {
 78         return "<CCGLProgram = " + this.toString() + " | Program = " + this._programObj.toString() + ", VertexShader = " +
 79             this._vertShader.toString() + ", FragmentShader = " + this._fragShader.toString() + ">";
 80     },
 81 
 82     _compileShader: function (shader, type, source) {
 83         if (!source || !shader)
 84             return false;
 85 
 86         var preStr = cc.GLProgram._isHighpSupported() ? "precision highp float;\n" : "precision mediump float;\n";
 87         source = preStr
 88             + "uniform mat4 CC_PMatrix;         \n"
 89             + "uniform mat4 CC_MVMatrix;        \n"
 90             + "uniform mat4 CC_MVPMatrix;       \n"
 91             + "uniform vec4 CC_Time;            \n"
 92             + "uniform vec4 CC_SinTime;         \n"
 93             + "uniform vec4 CC_CosTime;         \n"
 94             + "uniform vec4 CC_Random01;        \n"
 95             + "uniform sampler2D CC_Texture0;   \n"
 96             + "//CC INCLUDES END                \n" + source;
 97 
 98         this._glContext.shaderSource(shader, source);
 99         this._glContext.compileShader(shader);
100         var status = this._glContext.getShaderParameter(shader, this._glContext.COMPILE_STATUS);
101 
102         if (!status) {
103             cc.log("cocos2d: ERROR: Failed to compile shader:\n" + this._glContext.getShaderSource(shader));
104             if (type === this._glContext.VERTEX_SHADER)
105                 cc.log("cocos2d: \n" + this.vertexShaderLog());
106             else
107                 cc.log("cocos2d: \n" + this.fragmentShaderLog());
108         }
109         return ( status === true );
110     },
111 
112 	/**
113 	 * Create a cc.GLProgram object
114 	 * @param {String} vShaderFileName
115 	 * @param {String} fShaderFileName
116 	 * @returns {cc.GLProgram}
117 	 */
118     ctor: function (vShaderFileName, fShaderFileName, glContext) {
119         this._uniforms = [];
120         this._hashForUniforms = [];
121         this._glContext = glContext || cc._renderContext;
122 
123 		vShaderFileName && fShaderFileName && this.init(vShaderFileName, fShaderFileName);
124     },
125 
126     /**
127      * destroy program
128      */
129     destroyProgram: function () {
130         this._vertShader = null;
131         this._fragShader = null;
132         this._uniforms = null;
133         this._hashForUniforms = null;
134 
135         this._glContext.deleteProgram(this._programObj);
136     },
137 
138     /**
139      * Initializes the cc.GLProgram with a vertex and fragment with string
140      * @param {String} vertShaderStr
141      * @param {String} fragShaderStr
142      * @return {Boolean}
143      */
144     initWithVertexShaderByteArray: function (vertShaderStr, fragShaderStr) {
145         var locGL = this._glContext;
146         this._programObj = locGL.createProgram();
147         //cc.checkGLErrorDebug();
148 
149         this._vertShader = null;
150         this._fragShader = null;
151 
152         if (vertShaderStr) {
153             this._vertShader = locGL.createShader(locGL.VERTEX_SHADER);
154             if (!this._compileShader(this._vertShader, locGL.VERTEX_SHADER, vertShaderStr)) {
155                 cc.log("cocos2d: ERROR: Failed to compile vertex shader");
156             }
157         }
158 
159         // Create and compile fragment shader
160         if (fragShaderStr) {
161             this._fragShader = locGL.createShader(locGL.FRAGMENT_SHADER);
162             if (!this._compileShader(this._fragShader, locGL.FRAGMENT_SHADER, fragShaderStr)) {
163                 cc.log("cocos2d: ERROR: Failed to compile fragment shader");
164             }
165         }
166 
167         if (this._vertShader)
168             locGL.attachShader(this._programObj, this._vertShader);
169         cc.checkGLErrorDebug();
170 
171         if (this._fragShader)
172             locGL.attachShader(this._programObj, this._fragShader);
173         this._hashForUniforms.length = 0;
174 
175         cc.checkGLErrorDebug();
176         return true;
177     },
178 
179     /**
180      * Initializes the cc.GLProgram with a vertex and fragment with string
181      * @param {String} vertShaderStr
182      * @param {String} fragShaderStr
183      * @return {Boolean}
184      */
185     initWithString: function (vertShaderStr, fragShaderStr) {
186         return this.initWithVertexShaderByteArray(vertShaderStr, fragShaderStr);
187     },
188 
189     /**
190      * Initializes the CCGLProgram with a vertex and fragment with contents of filenames
191      * @param {String} vShaderFilename
192      * @param {String} fShaderFileName
193      * @return {Boolean}
194      */
195     initWithVertexShaderFilename: function (vShaderFilename, fShaderFileName) {
196         var vertexSource = cc.loader.getRes(vShaderFilename);
197         if(!vertexSource) throw new Error("Please load the resource firset : " + vShaderFilename);
198         var fragmentSource = cc.loader.getRes(fShaderFileName);
199         if(!fragmentSource) throw new Error("Please load the resource firset : " + fShaderFileName);
200         return this.initWithVertexShaderByteArray(vertexSource, fragmentSource);
201     },
202 
203     /**
204      * Initializes the CCGLProgram with a vertex and fragment with contents of filenames
205      * @param {String} vShaderFilename
206      * @param {String} fShaderFileName
207      * @return {Boolean}
208      */
209     init: function (vShaderFilename, fShaderFileName) {
210         return this.initWithVertexShaderFilename(vShaderFilename, fShaderFileName);
211     },
212 
213     /**
214      * It will add a new attribute to the shader
215      * @param {String} attributeName
216      * @param {Number} index
217      */
218     addAttribute: function (attributeName, index) {
219         this._glContext.bindAttribLocation(this._programObj, index, attributeName);
220     },
221 
222     /**
223      * links the glProgram
224      * @return {Boolean}
225      */
226     link: function () {
227         if(!this._programObj) {
228             cc.log("cc.GLProgram.link(): Cannot link invalid program");
229             return false;
230         }
231 
232         this._glContext.linkProgram(this._programObj);
233 
234         if (this._vertShader)
235             this._glContext.deleteShader(this._vertShader);
236         if (this._fragShader)
237             this._glContext.deleteShader(this._fragShader);
238 
239         this._vertShader = null;
240         this._fragShader = null;
241 
242         if (cc.game.config[cc.game.CONFIG_KEY.debugMode]) {
243             var status = this._glContext.getProgramParameter(this._programObj, this._glContext.LINK_STATUS);
244             if (!status) {
245                 cc.log("cocos2d: ERROR: Failed to link program: " + this._glContext.getProgramInfoLog(this._programObj));
246                 cc.glDeleteProgram(this._programObj);
247                 this._programObj = null;
248                 return false;
249             }
250         }
251 
252         return true;
253     },
254 
255     /**
256      * it will call glUseProgram()
257      */
258     use: function () {
259         cc.glUseProgram(this._programObj);
260     },
261 
262     /**
263      * It will create 4 uniforms:
264      *  cc.UNIFORM_PMATRIX
265      *  cc.UNIFORM_MVMATRIX
266      *  cc.UNIFORM_MVPMATRIX
267      *  cc.UNIFORM_SAMPLER
268      */
269     updateUniforms: function () {
270         this._uniforms[cc.UNIFORM_PMATRIX] = this._glContext.getUniformLocation(this._programObj, cc.UNIFORM_PMATRIX_S);
271         this._uniforms[cc.UNIFORM_MVMATRIX] = this._glContext.getUniformLocation(this._programObj, cc.UNIFORM_MVMATRIX_S);
272         this._uniforms[cc.UNIFORM_MVPMATRIX] = this._glContext.getUniformLocation(this._programObj, cc.UNIFORM_MVPMATRIX_S);
273         this._uniforms[cc.UNIFORM_TIME] = this._glContext.getUniformLocation(this._programObj, cc.UNIFORM_TIME_S);
274         this._uniforms[cc.UNIFORM_SINTIME] = this._glContext.getUniformLocation(this._programObj, cc.UNIFORM_SINTIME_S);
275         this._uniforms[cc.UNIFORM_COSTIME] = this._glContext.getUniformLocation(this._programObj, cc.UNIFORM_COSTIME_S);
276 
277         this._usesTime = (this._uniforms[cc.UNIFORM_TIME] != null || this._uniforms[cc.UNIFORM_SINTIME] != null || this._uniforms[cc.UNIFORM_COSTIME] != null);
278 
279         this._uniforms[cc.UNIFORM_RANDOM01] = this._glContext.getUniformLocation(this._programObj, cc.UNIFORM_RANDOM01_S);
280         this._uniforms[cc.UNIFORM_SAMPLER] = this._glContext.getUniformLocation(this._programObj, cc.UNIFORM_SAMPLER_S);
281 
282         this.use();
283         // Since sample most probably won't change, set it to 0 now.
284         this.setUniformLocationWith1i(this._uniforms[cc.UNIFORM_SAMPLER], 0);
285     },
286 
287     /**
288      * calls retrieves the named uniform location for this shader program.
289      * @param {String} name
290      * @returns {Number}
291      */
292     getUniformLocationForName:function(name){
293         if(!name)
294             throw new Error("cc.GLProgram.getUniformLocationForName(): uniform name should be non-null");
295         if(!this._programObj)
296             throw new Error("cc.GLProgram.getUniformLocationForName(): Invalid operation. Cannot get uniform location when program is not initialized");
297 
298         return this._glContext.getUniformLocation(this._programObj, name);
299     },
300 
301     /**
302      * get uniform MVP matrix
303      * @returns {WebGLUniformLocation}
304      */
305     getUniformMVPMatrix: function () {
306         return this._uniforms[cc.UNIFORM_MVPMATRIX];
307     },
308 
309     /**
310      * get uniform sampler
311      * @returns {WebGLUniformLocation}
312      */
313     getUniformSampler: function () {
314         return this._uniforms[cc.UNIFORM_SAMPLER];
315     },
316 
317     /**
318      * calls glUniform1i only if the values are different than the previous call for this same shader program.
319      * @param {WebGLUniformLocation} location
320      * @param {Number} i1
321      */
322     setUniformLocationWith1i: function (location, i1) {
323         var updated = this._updateUniformLocation(location, i1);
324         if (updated)
325             this._glContext.uniform1i(location, i1);
326     },
327 
328     /**
329      * calls glUniform2i only if the values are different than the previous call for this same shader program.
330      * @param {WebGLUniformLocation} location
331      * @param {Number} i1
332      * @param {Number} i2
333      */
334     setUniformLocationWith2i:function(location, i1,i2){
335         var intArray= [i1,i2];
336         var updated =  this._updateUniformLocation(location, intArray);
337 
338         if( updated )
339             this._glContext.uniform2i(location, i1, i2);
340     },
341 
342     /**
343      * calls glUniform3i only if the values are different than the previous call for this same shader program.
344      * @param {WebGLUniformLocation} location
345      * @param {Number} i1
346      * @param {Number} i2
347      * @param {Number} i3
348      */
349     setUniformLocationWith3i:function(location, i1, i2, i3){
350         var intArray = [i1,i2,i3];
351         var updated =  this._updateUniformLocation(location, intArray);
352 
353         if( updated )
354             this._glContext.uniform3i(location, i1, i2, i3);
355     },
356 
357     /**
358      * calls glUniform4i only if the values are different than the previous call for this same shader program.
359      * @param {WebGLUniformLocation} location
360      * @param {Number} i1
361      * @param {Number} i2
362      * @param {Number} i3
363      * @param {Number} i4
364      */
365     setUniformLocationWith4i:function(location, i1, i2, i3, i4){
366         var intArray = [i1,i2,i3,i4];
367         var updated =  this._updateUniformLocation(location, intArray);
368 
369         if( updated )
370             this._glContext.uniform4i(location, i1, i2, i3, i4);
371     },
372 
373     /**
374      * calls glUniform2iv only if the values are different than the previous call for this same shader program.
375      * @param {WebGLUniformLocation} location
376      * @param {Int32Array} intArray
377      * @param {Number} numberOfArrays
378      */
379     setUniformLocationWith2iv:function(location, intArray, numberOfArrays){
380         var updated =  this._updateUniformLocation(location, intArray);
381 
382         if( updated )
383             this._glContext.uniform2iv(location, intArray);
384     },
385 
386     /**
387      * calls glUniform3iv only if the values are different than the previous call for this same shader program.
388      * @param {WebGLUniformLocation} location
389      * @param {Int32Array} intArray
390      * @param {Number} numberOfArrays
391      */
392     setUniformLocationWith3iv:function(location, intArray, numberOfArrays){
393         var updated =  this._updateUniformLocation(location, intArray);
394 
395         if( updated )
396             this._glContext.uniform3iv(location, intArray);
397     },
398 
399     /**
400      * calls glUniform4iv only if the values are different than the previous call for this same shader program.
401      * @param {WebGLUniformLocation} location
402      * @param {Int32Array} intArray
403      * @param {Number} numberOfArrays
404      */
405     setUniformLocationWith4iv:function(location, intArray, numberOfArrays){
406         var updated =  this._updateUniformLocation(location, intArray);
407 
408         if( updated )
409             this._glContext.uniform4iv(location, intArray);
410     },
411 
412     /**
413      * calls glUniform1i only if the values are different than the previous call for this same shader program.
414      * @param {WebGLUniformLocation} location
415      * @param {Number} i1
416      */
417     setUniformLocationI32: function (location, i1) {
418         this.setUniformLocationWith1i(arguments[0], arguments[1]);
419     },
420 
421     /**
422      * calls glUniform1f only if the values are different than the previous call for this same shader program.
423      * @param {WebGLUniformLocation} location
424      * @param {Number} f1
425      */
426     setUniformLocationWith1f: function (location, f1) {
427         var updated = this._updateUniformLocation(location, f1);
428         if (updated)
429             this._glContext.uniform1f(location, f1);
430     },
431 
432     /**
433      * calls glUniform2f only if the values are different than the previous call for this same shader program.
434      * @param {WebGLUniformLocation} location
435      * @param {Number} f1
436      * @param {Number} f2
437      */
438     setUniformLocationWith2f: function (location, f1, f2) {
439         var floats = [f1, f2];
440         var updated = this._updateUniformLocation(location, floats);
441         if (updated)
442             this._glContext.uniform2f(location, f1, f2);
443     },
444 
445     /**
446      * calls glUniform3f only if the values are different than the previous call for this same shader program.
447      * @param {WebGLUniformLocation} location
448      * @param {Number} f1
449      * @param {Number} f2
450      * @param {Number} f3
451      */
452     setUniformLocationWith3f: function (location, f1, f2, f3) {
453         var floats = [f1, f2, f3];
454         var updated = this._updateUniformLocation(location, floats);
455         if (updated)
456             this._glContext.uniform3f(location, f1, f2, f3);
457     },
458 
459     /**
460      * calls glUniform4f only if the values are different than the previous call for this same shader program.
461      * @param {WebGLUniformLocation} location
462      * @param {Number} f1
463      * @param {Number} f2
464      * @param {Number} f3
465      * @param {Number} f4
466      */
467     setUniformLocationWith4f: function (location, f1, f2, f3, f4) {
468         var floats = [f1, f2, f3, f4];
469         var updated = this._updateUniformLocation(location, floats);
470         if (updated)
471             this._glContext.uniform4f(location, f1, f2, f3, f4);
472     },
473 
474     /**
475      * calls glUniform2fv only if the values are different than the previous call for this same shader program.
476      * @param {WebGLUniformLocation} location
477      * @param {Float32Array} floatArray
478      * @param {Number} numberOfArrays
479      */
480     setUniformLocationWith2fv: function (location, floatArray, numberOfArrays) {
481         var updated = this._updateUniformLocation(location, floatArray);
482         if (updated)
483             this._glContext.uniform2fv(location, floatArray);
484     },
485 
486     /**
487      * calls glUniform3fv only if the values are different than the previous call for this same shader program.
488      * @param {WebGLUniformLocation} location
489      * @param {Float32Array} floatArray
490      * @param {Number} numberOfArrays
491      */
492     setUniformLocationWith3fv: function (location, floatArray, numberOfArrays) {
493         var updated = this._updateUniformLocation(location, floatArray);
494         if (updated)
495             this._glContext.uniform3fv(location, floatArray);
496     },
497 
498     /**
499      * calls glUniform4fv only if the values are different than the previous call for this same shader program.
500      * @param {WebGLUniformLocation} location
501      * @param {Float32Array} floatArray
502      * @param {Number} numberOfArrays
503      */
504     setUniformLocationWith4fv: function (location, floatArray, numberOfArrays) {
505         var updated = this._updateUniformLocation(location, floatArray);
506         if (updated)
507             this._glContext.uniform4fv(location, floatArray);
508     },
509 
510     /**
511      * calls glUniformMatrix4fv only if the values are different than the previous call for this same shader program.
512      * @param {WebGLUniformLocation} location
513      * @param {Float32Array} matrixArray
514      * @param {Number} numberOfMatrices
515      */
516     setUniformLocationWithMatrix4fv: function (location, matrixArray, numberOfMatrices) {
517         var updated = this._updateUniformLocation(location, matrixArray);
518         if (updated)
519             this._glContext.uniformMatrix4fv(location, false, matrixArray);
520     },
521 
522     setUniformLocationF32: function () {
523         if (arguments.length < 2)
524             return;
525 
526         switch (arguments.length) {
527             case 2:
528                 this.setUniformLocationWith1f(arguments[0], arguments[1]);
529                 break;
530             case 3:
531                 this.setUniformLocationWith2f(arguments[0], arguments[1], arguments[2]);
532                 break;
533             case 4:
534                 this.setUniformLocationWith3f(arguments[0], arguments[1], arguments[2], arguments[3]);
535                 break;
536             case 5:
537                 this.setUniformLocationWith4f(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4]);
538                 break;
539         }
540     },
541 
542     /**
543      * will update the builtin uniforms if they are different than the previous call for this same shader program.
544      */
545     setUniformsForBuiltins: function () {
546         var matrixP = new cc.math.Matrix4();
547         var matrixMV = new cc.math.Matrix4();
548         var matrixMVP = new cc.math.Matrix4();
549 
550         cc.kmGLGetMatrix(cc.KM_GL_PROJECTION, matrixP);
551         cc.kmGLGetMatrix(cc.KM_GL_MODELVIEW, matrixMV);
552 
553         cc.kmMat4Multiply(matrixMVP, matrixP, matrixMV);
554 
555         this.setUniformLocationWithMatrix4fv(this._uniforms[cc.UNIFORM_PMATRIX], matrixP.mat, 1);
556         this.setUniformLocationWithMatrix4fv(this._uniforms[cc.UNIFORM_MVMATRIX], matrixMV.mat, 1);
557         this.setUniformLocationWithMatrix4fv(this._uniforms[cc.UNIFORM_MVPMATRIX], matrixMVP.mat, 1);
558 
559         if (this._usesTime) {
560             var director = cc.director;
561             // This doesn't give the most accurate global time value.
562             // Cocos2D doesn't store a high precision time value, so this will have to do.
563             // Getting Mach time per frame per shader using time could be extremely expensive.
564             var time = director.getTotalFrames() * director.getAnimationInterval();
565 
566             this.setUniformLocationWith4f(this._uniforms[cc.UNIFORM_TIME], time / 10.0, time, time * 2, time * 4);
567             this.setUniformLocationWith4f(this._uniforms[cc.UNIFORM_SINTIME], time / 8.0, time / 4.0, time / 2.0, Math.sin(time));
568             this.setUniformLocationWith4f(this._uniforms[cc.UNIFORM_COSTIME], time / 8.0, time / 4.0, time / 2.0, Math.cos(time));
569         }
570 
571         if (this._uniforms[cc.UNIFORM_RANDOM01] !== -1)
572             this.setUniformLocationWith4f(this._uniforms[cc.UNIFORM_RANDOM01], Math.random(), Math.random(), Math.random(), Math.random());
573     },
574 
575     _setUniformsForBuiltinsForRenderer: function (node) {
576         if(!node || !node._renderCmd)
577             return;
578 
579         var matrixP = new cc.math.Matrix4();
580         //var matrixMV = new cc.kmMat4();
581         var matrixMVP = new cc.math.Matrix4();
582 
583         cc.kmGLGetMatrix(cc.KM_GL_PROJECTION, matrixP);
584         //cc.kmGLGetMatrix(cc.KM_GL_MODELVIEW, node._stackMatrix);
585 
586         cc.kmMat4Multiply(matrixMVP, matrixP, node._renderCmd._stackMatrix);
587 
588         this.setUniformLocationWithMatrix4fv(this._uniforms[cc.UNIFORM_PMATRIX], matrixP.mat, 1);
589         this.setUniformLocationWithMatrix4fv(this._uniforms[cc.UNIFORM_MVMATRIX], node._renderCmd._stackMatrix.mat, 1);
590         this.setUniformLocationWithMatrix4fv(this._uniforms[cc.UNIFORM_MVPMATRIX], matrixMVP.mat, 1);
591 
592         if (this._usesTime) {
593             var director = cc.director;
594             // This doesn't give the most accurate global time value.
595             // Cocos2D doesn't store a high precision time value, so this will have to do.
596             // Getting Mach time per frame per shader using time could be extremely expensive.
597             var time = director.getTotalFrames() * director.getAnimationInterval();
598 
599             this.setUniformLocationWith4f(this._uniforms[cc.UNIFORM_TIME], time / 10.0, time, time * 2, time * 4);
600             this.setUniformLocationWith4f(this._uniforms[cc.UNIFORM_SINTIME], time / 8.0, time / 4.0, time / 2.0, Math.sin(time));
601             this.setUniformLocationWith4f(this._uniforms[cc.UNIFORM_COSTIME], time / 8.0, time / 4.0, time / 2.0, Math.cos(time));
602         }
603 
604         if (this._uniforms[cc.UNIFORM_RANDOM01] !== -1)
605             this.setUniformLocationWith4f(this._uniforms[cc.UNIFORM_RANDOM01], Math.random(), Math.random(), Math.random(), Math.random());
606     },
607 
608     /**
609      * will update the MVP matrix on the MVP uniform if it is different than the previous call for this same shader program.
610      */
611     setUniformForModelViewProjectionMatrix: function () {
612         this._glContext.uniformMatrix4fv(this._uniforms[cc.UNIFORM_MVPMATRIX], false,
613             cc.getMat4MultiplyValue(cc.projection_matrix_stack.top, cc.modelview_matrix_stack.top));
614     },
615 
616     setUniformForModelViewProjectionMatrixWithMat4: function (swapMat4) {
617         cc.kmMat4Multiply(swapMat4, cc.projection_matrix_stack.top, cc.modelview_matrix_stack.top);
618         this._glContext.uniformMatrix4fv(this._uniforms[cc.UNIFORM_MVPMATRIX], false, swapMat4.mat);
619     },
620 
621     setUniformForModelViewAndProjectionMatrixWithMat4: function () {
622         this._glContext.uniformMatrix4fv(this._uniforms[cc.UNIFORM_MVMATRIX], false, cc.modelview_matrix_stack.top.mat);
623         this._glContext.uniformMatrix4fv(this._uniforms[cc.UNIFORM_PMATRIX], false, cc.projection_matrix_stack.top.mat);
624     },
625 
626     _setUniformForMVPMatrixWithMat4: function(modelViewMatrix){
627         if(!modelViewMatrix)
628             throw new Error("modelView matrix is undefined.");
629         this._glContext.uniformMatrix4fv(this._uniforms[cc.UNIFORM_MVMATRIX], false, modelViewMatrix.mat);
630         this._glContext.uniformMatrix4fv(this._uniforms[cc.UNIFORM_PMATRIX], false, cc.projection_matrix_stack.top.mat);
631     },
632 
633     /**
634      * returns the vertexShader error log
635      * @return {String}
636      */
637     vertexShaderLog: function () {
638         return this._glContext.getShaderInfoLog(this._vertShader);
639     },
640 
641     /**
642      * returns the vertexShader error log
643      * @return {String}
644      */
645     getVertexShaderLog: function () {
646         return this._glContext.getShaderInfoLog(this._vertShader);
647     },
648 
649     /**
650      * returns the fragmentShader error log
651      * @returns {String}
652      */
653     getFragmentShaderLog: function () {
654         return this._glContext.getShaderInfoLog(this._vertShader);
655     },
656 
657     /**
658      * returns the fragmentShader error log
659      * @return {String}
660      */
661     fragmentShaderLog: function () {
662         return this._glContext.getShaderInfoLog(this._fragShader);
663     },
664 
665     /**
666      * returns the program error log
667      * @return {String}
668      */
669     programLog: function () {
670         return this._glContext.getProgramInfoLog(this._programObj);
671     },
672 
673     /**
674      * returns the program error log
675      * @return {String}
676      */
677     getProgramLog: function () {
678         return this._glContext.getProgramInfoLog(this._programObj);
679     },
680 
681     /**
682      *  reload all shaders, this function is designed for android  <br/>
683      *  when opengl context lost, so don't call it.
684      */
685     reset: function () {
686         this._vertShader = null;
687         this._fragShader = null;
688         this._uniforms.length = 0;
689 
690         // it is already deallocated by android
691         //ccGLDeleteProgram(m_uProgram);
692         this._glContext.deleteProgram(this._programObj);
693         this._programObj = null;
694 
695         // Purge uniform hash
696         for (var i = 0; i < this._hashForUniforms.length; i++) {
697             this._hashForUniforms[i].value = null;
698             this._hashForUniforms[i] = null;
699         }
700 
701         this._hashForUniforms.length = 0;
702     },
703 
704     /**
705      * get WebGLProgram object
706      * @return {WebGLProgram}
707      */
708     getProgram: function () {
709         return this._programObj;
710     },
711 
712     /**
713      * Currently JavaScript Bindings (JSB), in some cases, needs to use retain and release. This is a bug in JSB,
714      * and the ugly workaround is to use retain/release. So, these 2 methods were added to be compatible with JSB.
715      * This is a hack, and should be removed once JSB fixes the retain/release bug
716      */
717     retain: function () {
718     },
719     release: function () {
720     }
721 });
722 
723 /**
724  * Create a cc.GLProgram object
725  * @deprecated since v3.0, please use new cc.GLProgram(vShaderFileName, fShaderFileName) instead
726  * @param {String} vShaderFileName
727  * @param {String} fShaderFileName
728  * @returns {cc.GLProgram}
729  */
730 cc.GLProgram.create = function (vShaderFileName, fShaderFileName) {
731     return new cc.GLProgram(vShaderFileName, fShaderFileName);
732 };
733 
734 cc.GLProgram._highpSupported = null;
735 
736 cc.GLProgram._isHighpSupported = function(){
737     if(cc.GLProgram._highpSupported == null){
738         var ctx = cc._renderContext;
739         var highp = ctx.getShaderPrecisionFormat(ctx.FRAGMENT_SHADER, ctx.HIGH_FLOAT);
740         cc.GLProgram._highpSupported = highp.precision !== 0;
741     }
742     return cc.GLProgram._highpSupported;
743 };
744 
745 /**
746  * <p>
747  *     Sets the shader program for this node
748  *
749  *     Since v2.0, each rendering node must set its shader program.
750  *     It should be set in initialize phase.
751  * </p>
752  * @function
753  * @param {cc.Node} node
754  * @param {cc.GLProgram} program The shader program which fetches from CCShaderCache.
755  * @example
756  * cc.setGLProgram(node, cc.shaderCache.programForKey(cc.SHADER_POSITION_TEXTURECOLOR));
757  */
758 cc.setProgram = function (node, program) {
759     node.shaderProgram = program;
760 
761     var children = node.children;
762     if (!children)
763         return;
764 
765     for (var i = 0; i < children.length; i++)
766         cc.setProgram(children[i], program);
767 };
768