hefeihvac_java/node_modules/claygl/src/gpu/GLProgram.js

338 lines
11 KiB
JavaScript

import vendor from '../core/vendor';
import Base from '../core/Base';
var SHADER_STATE_TO_ENABLE = 1;
var SHADER_STATE_KEEP_ENABLE = 2;
var SHADER_STATE_PENDING = 3;
// Enable attribute operation is global to all programs
// Here saved the list of all enabled attribute index
// http://www.mjbshaw.com/2013/03/webgl-fixing-invalidoperation.html
var enabledAttributeList = {};
// some util functions
function addLineNumbers(string) {
var chunks = string.split('\n');
for (var i = 0, il = chunks.length; i < il; i ++) {
// Chrome reports shader errors on lines
// starting counting from 1
chunks[i] = (i + 1) + ': ' + chunks[i];
}
return chunks.join('\n');
}
// Return true or error msg if error happened
function checkShaderErrorMsg(_gl, shader, shaderString) {
if (!_gl.getShaderParameter(shader, _gl.COMPILE_STATUS)) {
return [_gl.getShaderInfoLog(shader), addLineNumbers(shaderString)].join('\n');
}
}
var tmpFloat32Array16 = new vendor.Float32Array(16);
var GLProgram = Base.extend({
uniformSemantics: {},
attributes: {}
}, function () {
this._locations = {};
this._textureSlot = 0;
this._program = null;
}, {
bind: function (renderer) {
this._textureSlot = 0;
renderer.gl.useProgram(this._program);
},
hasUniform: function (symbol) {
var location = this._locations[symbol];
return location !== null && location !== undefined;
},
useTextureSlot: function (renderer, texture, slot) {
if (texture) {
renderer.gl.activeTexture(renderer.gl.TEXTURE0 + slot);
// Maybe texture is not loaded yet;
if (texture.isRenderable()) {
texture.bind(renderer);
}
else {
// Bind texture to null
texture.unbind(renderer);
}
}
},
currentTextureSlot: function () {
return this._textureSlot;
},
resetTextureSlot: function (slot) {
this._textureSlot = slot || 0;
},
takeCurrentTextureSlot: function (renderer, texture) {
var textureSlot = this._textureSlot;
this.useTextureSlot(renderer, texture, textureSlot);
this._textureSlot++;
return textureSlot;
},
setUniform: function (_gl, type, symbol, value) {
var locationMap = this._locations;
var location = locationMap[symbol];
// Uniform is not existed in the shader
if (location === null || location === undefined) {
return false;
}
switch (type) {
case 'm4':
if (!(value instanceof Float32Array)) {
// Use Float32Array is much faster than array when uniformMatrix4fv.
for (var i = 0; i < value.length; i++) {
tmpFloat32Array16[i] = value[i];
}
value = tmpFloat32Array16;
}
_gl.uniformMatrix4fv(location, false, value);
break;
case '2i':
_gl.uniform2i(location, value[0], value[1]);
break;
case '2f':
_gl.uniform2f(location, value[0], value[1]);
break;
case '3i':
_gl.uniform3i(location, value[0], value[1], value[2]);
break;
case '3f':
_gl.uniform3f(location, value[0], value[1], value[2]);
break;
case '4i':
_gl.uniform4i(location, value[0], value[1], value[2], value[3]);
break;
case '4f':
_gl.uniform4f(location, value[0], value[1], value[2], value[3]);
break;
case '1i':
_gl.uniform1i(location, value);
break;
case '1f':
_gl.uniform1f(location, value);
break;
case '1fv':
_gl.uniform1fv(location, value);
break;
case '1iv':
_gl.uniform1iv(location, value);
break;
case '2iv':
_gl.uniform2iv(location, value);
break;
case '2fv':
_gl.uniform2fv(location, value);
break;
case '3iv':
_gl.uniform3iv(location, value);
break;
case '3fv':
_gl.uniform3fv(location, value);
break;
case '4iv':
_gl.uniform4iv(location, value);
break;
case '4fv':
_gl.uniform4fv(location, value);
break;
case 'm2':
case 'm2v':
_gl.uniformMatrix2fv(location, false, value);
break;
case 'm3':
case 'm3v':
_gl.uniformMatrix3fv(location, false, value);
break;
case 'm4v':
// Raw value
if (Array.isArray(value) && Array.isArray(value[0])) {
var array = new vendor.Float32Array(value.length * 16);
var cursor = 0;
for (var i = 0; i < value.length; i++) {
var item = value[i];
for (var j = 0; j < 16; j++) {
array[cursor++] = item[j];
}
}
_gl.uniformMatrix4fv(location, false, array);
}
else { // ArrayBufferView
_gl.uniformMatrix4fv(location, false, value);
}
break;
}
return true;
},
setUniformOfSemantic: function (_gl, semantic, val) {
var semanticInfo = this.uniformSemantics[semantic];
if (semanticInfo) {
return this.setUniform(_gl, semanticInfo.type, semanticInfo.symbol, val);
}
return false;
},
// Used for creating VAO
// Enable the attributes passed in and disable the rest
// Example Usage:
// enableAttributes(renderer, ["position", "texcoords"])
enableAttributes: function (renderer, attribList, vao) {
var _gl = renderer.gl;
var program = this._program;
var locationMap = this._locations;
var enabledAttributeListInContext;
if (vao) {
enabledAttributeListInContext = vao.__enabledAttributeList;
}
else {
enabledAttributeListInContext = enabledAttributeList[renderer.__uid__];
}
if (!enabledAttributeListInContext) {
// In vertex array object context
// PENDING Each vao object needs to enable attributes again?
if (vao) {
enabledAttributeListInContext
= vao.__enabledAttributeList
= [];
}
else {
enabledAttributeListInContext
= enabledAttributeList[renderer.__uid__]
= [];
}
}
var locationList = [];
for (var i = 0; i < attribList.length; i++) {
var symbol = attribList[i];
if (!this.attributes[symbol]) {
locationList[i] = -1;
continue;
}
var location = locationMap[symbol];
if (location == null) {
location = _gl.getAttribLocation(program, symbol);
// Attrib location is a number from 0 to ...
if (location === -1) {
locationList[i] = -1;
continue;
}
locationMap[symbol] = location;
}
locationList[i] = location;
if (!enabledAttributeListInContext[location]) {
enabledAttributeListInContext[location] = SHADER_STATE_TO_ENABLE;
}
else {
enabledAttributeListInContext[location] = SHADER_STATE_KEEP_ENABLE;
}
}
for (var i = 0; i < enabledAttributeListInContext.length; i++) {
switch(enabledAttributeListInContext[i]){
case SHADER_STATE_TO_ENABLE:
_gl.enableVertexAttribArray(i);
enabledAttributeListInContext[i] = SHADER_STATE_PENDING;
break;
case SHADER_STATE_KEEP_ENABLE:
enabledAttributeListInContext[i] = SHADER_STATE_PENDING;
break;
// Expired
case SHADER_STATE_PENDING:
_gl.disableVertexAttribArray(i);
enabledAttributeListInContext[i] = 0;
break;
}
}
return locationList;
},
getAttribLocation: function (_gl, symbol) {
var locationMap = this._locations;
var location = locationMap[symbol];
if (location == null) {
location = _gl.getAttribLocation(this._program, symbol);
locationMap[symbol] = location;
}
return location;
},
buildProgram: function (_gl, shader, vertexShaderCode, fragmentShaderCode) {
var vertexShader = _gl.createShader(_gl.VERTEX_SHADER);
var program = _gl.createProgram();
_gl.shaderSource(vertexShader, vertexShaderCode);
_gl.compileShader(vertexShader);
var fragmentShader = _gl.createShader(_gl.FRAGMENT_SHADER);
_gl.shaderSource(fragmentShader, fragmentShaderCode);
_gl.compileShader(fragmentShader);
var msg = checkShaderErrorMsg(_gl, vertexShader, vertexShaderCode);
if (msg) {
return msg;
}
msg = checkShaderErrorMsg(_gl, fragmentShader, fragmentShaderCode);
if (msg) {
return msg;
}
_gl.attachShader(program, vertexShader);
_gl.attachShader(program, fragmentShader);
// Force the position bind to location 0;
if (shader.attributeSemantics['POSITION']) {
_gl.bindAttribLocation(program, 0, shader.attributeSemantics['POSITION'].symbol);
}
else {
// Else choose an attribute and bind to location 0;
var keys = Object.keys(this.attributes);
_gl.bindAttribLocation(program, 0, keys[0]);
}
_gl.linkProgram(program);
_gl.deleteShader(vertexShader);
_gl.deleteShader(fragmentShader);
this._program = program;
// Save code.
this.vertexCode = vertexShaderCode;
this.fragmentCode = fragmentShaderCode;
if (!_gl.getProgramParameter(program, _gl.LINK_STATUS)) {
return 'Could not link program\n' + _gl.getProgramInfoLog(program);
}
// Cache uniform locations
for (var i = 0; i < shader.uniforms.length; i++) {
var uniformSymbol = shader.uniforms[i];
this._locations[uniformSymbol] = _gl.getUniformLocation(program, uniformSymbol);
}
}
});
export default GLProgram;