681 lines
19 KiB
JavaScript
681 lines
19 KiB
JavaScript
|
|
/**
|
||
|
|
* Mainly do the parse and compile of shader string
|
||
|
|
* Support shader code chunk import and export
|
||
|
|
* Support shader semantics
|
||
|
|
* http://www.nvidia.com/object/using_sas.html
|
||
|
|
* https://github.com/KhronosGroup/collada2json/issues/45
|
||
|
|
*/
|
||
|
|
import util from './core/util';
|
||
|
|
import vendor from './core/vendor';
|
||
|
|
|
||
|
|
var uniformRegex = /uniform\s+(bool|float|int|vec2|vec3|vec4|ivec2|ivec3|ivec4|mat2|mat3|mat4|sampler2D|samplerCube)\s+([\s\S]*?);/g;
|
||
|
|
var attributeRegex = /attribute\s+(float|int|vec2|vec3|vec4)\s+([\s\S]*?);/g;
|
||
|
|
// Only parse number define.
|
||
|
|
var defineRegex = /#define\s+(\w+)?(\s+[\d-.]+)?\s*;?\s*\n/g;
|
||
|
|
|
||
|
|
var uniformTypeMap = {
|
||
|
|
'bool': '1i',
|
||
|
|
'int': '1i',
|
||
|
|
'sampler2D': 't',
|
||
|
|
'samplerCube': 't',
|
||
|
|
'float': '1f',
|
||
|
|
'vec2': '2f',
|
||
|
|
'vec3': '3f',
|
||
|
|
'vec4': '4f',
|
||
|
|
'ivec2': '2i',
|
||
|
|
'ivec3': '3i',
|
||
|
|
'ivec4': '4i',
|
||
|
|
'mat2': 'm2',
|
||
|
|
'mat3': 'm3',
|
||
|
|
'mat4': 'm4'
|
||
|
|
};
|
||
|
|
|
||
|
|
function createZeroArray(len) {
|
||
|
|
var arr = [];
|
||
|
|
for (var i = 0; i < len; i++) {
|
||
|
|
arr[i] = 0;
|
||
|
|
}
|
||
|
|
return arr;
|
||
|
|
}
|
||
|
|
|
||
|
|
var uniformValueConstructor = {
|
||
|
|
'bool': function () { return true; },
|
||
|
|
'int': function () { return 0; },
|
||
|
|
'float': function () { return 0; },
|
||
|
|
'sampler2D': function () { return null; },
|
||
|
|
'samplerCube': function () { return null; },
|
||
|
|
|
||
|
|
'vec2': function () { return createZeroArray(2); },
|
||
|
|
'vec3': function () { return createZeroArray(3); },
|
||
|
|
'vec4': function () { return createZeroArray(4); },
|
||
|
|
|
||
|
|
'ivec2': function () { return createZeroArray(2); },
|
||
|
|
'ivec3': function () { return createZeroArray(3); },
|
||
|
|
'ivec4': function () { return createZeroArray(4); },
|
||
|
|
|
||
|
|
'mat2': function () { return createZeroArray(4); },
|
||
|
|
'mat3': function () { return createZeroArray(9); },
|
||
|
|
'mat4': function () { return createZeroArray(16); },
|
||
|
|
|
||
|
|
'array': function () { return []; }
|
||
|
|
};
|
||
|
|
|
||
|
|
var attributeSemantics = [
|
||
|
|
'POSITION',
|
||
|
|
'NORMAL',
|
||
|
|
'BINORMAL',
|
||
|
|
'TANGENT',
|
||
|
|
'TEXCOORD',
|
||
|
|
'TEXCOORD_0',
|
||
|
|
'TEXCOORD_1',
|
||
|
|
'COLOR',
|
||
|
|
// Skinning
|
||
|
|
// https://github.com/KhronosGroup/glTF/blob/master/specification/README.md#semantics
|
||
|
|
'JOINT',
|
||
|
|
'WEIGHT'
|
||
|
|
];
|
||
|
|
var uniformSemantics = [
|
||
|
|
'SKIN_MATRIX',
|
||
|
|
// Information about viewport
|
||
|
|
'VIEWPORT_SIZE',
|
||
|
|
'VIEWPORT',
|
||
|
|
'DEVICEPIXELRATIO',
|
||
|
|
// Window size for window relative coordinate
|
||
|
|
// https://www.opengl.org/sdk/docs/man/html/gl_FragCoord.xhtml
|
||
|
|
'WINDOW_SIZE',
|
||
|
|
// Infomation about camera
|
||
|
|
'NEAR',
|
||
|
|
'FAR',
|
||
|
|
// Time
|
||
|
|
'TIME'
|
||
|
|
];
|
||
|
|
var matrixSemantics = [
|
||
|
|
'WORLD',
|
||
|
|
'VIEW',
|
||
|
|
'PROJECTION',
|
||
|
|
'WORLDVIEW',
|
||
|
|
'VIEWPROJECTION',
|
||
|
|
'WORLDVIEWPROJECTION',
|
||
|
|
'WORLDINVERSE',
|
||
|
|
'VIEWINVERSE',
|
||
|
|
'PROJECTIONINVERSE',
|
||
|
|
'WORLDVIEWINVERSE',
|
||
|
|
'VIEWPROJECTIONINVERSE',
|
||
|
|
'WORLDVIEWPROJECTIONINVERSE',
|
||
|
|
'WORLDTRANSPOSE',
|
||
|
|
'VIEWTRANSPOSE',
|
||
|
|
'PROJECTIONTRANSPOSE',
|
||
|
|
'WORLDVIEWTRANSPOSE',
|
||
|
|
'VIEWPROJECTIONTRANSPOSE',
|
||
|
|
'WORLDVIEWPROJECTIONTRANSPOSE',
|
||
|
|
'WORLDINVERSETRANSPOSE',
|
||
|
|
'VIEWINVERSETRANSPOSE',
|
||
|
|
'PROJECTIONINVERSETRANSPOSE',
|
||
|
|
'WORLDVIEWINVERSETRANSPOSE',
|
||
|
|
'VIEWPROJECTIONINVERSETRANSPOSE',
|
||
|
|
'WORLDVIEWPROJECTIONINVERSETRANSPOSE'
|
||
|
|
];
|
||
|
|
|
||
|
|
var attributeSizeMap = {
|
||
|
|
// WebGL does not support integer attributes
|
||
|
|
'vec4': 4,
|
||
|
|
'vec3': 3,
|
||
|
|
'vec2': 2,
|
||
|
|
'float': 1
|
||
|
|
};
|
||
|
|
|
||
|
|
|
||
|
|
var shaderIDCache = {};
|
||
|
|
var shaderCodeCache = {};
|
||
|
|
|
||
|
|
function getShaderID(vertex, fragment) {
|
||
|
|
var key = 'vertex:' + vertex + 'fragment:' + fragment;
|
||
|
|
if (shaderIDCache[key]) {
|
||
|
|
return shaderIDCache[key];
|
||
|
|
}
|
||
|
|
var id = util.genGUID();
|
||
|
|
shaderIDCache[key] = id;
|
||
|
|
|
||
|
|
shaderCodeCache[id] = {
|
||
|
|
vertex: vertex,
|
||
|
|
fragment: fragment
|
||
|
|
};
|
||
|
|
|
||
|
|
return id;
|
||
|
|
}
|
||
|
|
|
||
|
|
function removeComment(code) {
|
||
|
|
return code.replace(/[ \t]*\/\/.*\n/g, '' ) // remove //
|
||
|
|
.replace(/[ \t]*\/\*[\s\S]*?\*\//g, '' ); // remove /* */
|
||
|
|
}
|
||
|
|
|
||
|
|
function logSyntaxError() {
|
||
|
|
console.error('Wrong uniform/attributes syntax');
|
||
|
|
}
|
||
|
|
|
||
|
|
function parseDeclarations(type, line) {
|
||
|
|
var speratorsRegexp = /[,=\(\):]/;
|
||
|
|
var tokens = line
|
||
|
|
// Convert `symbol: [1,2,3]` to `symbol: vec3(1,2,3)`
|
||
|
|
.replace(/:\s*\[\s*(.*)\s*\]/g, '=' + type + '($1)')
|
||
|
|
.replace(/\s+/g, '')
|
||
|
|
.split(/(?=[,=\(\):])/g);
|
||
|
|
|
||
|
|
var newTokens = [];
|
||
|
|
for (var i = 0; i < tokens.length; i++) {
|
||
|
|
if (tokens[i].match(speratorsRegexp)) {
|
||
|
|
newTokens.push(
|
||
|
|
tokens[i].charAt(0),
|
||
|
|
tokens[i].slice(1)
|
||
|
|
);
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
newTokens.push(tokens[i]);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
tokens = newTokens;
|
||
|
|
|
||
|
|
var TYPE_SYMBOL = 0;
|
||
|
|
var TYPE_ASSIGN = 1;
|
||
|
|
var TYPE_VEC = 2;
|
||
|
|
var TYPE_ARR = 3;
|
||
|
|
var TYPE_SEMANTIC = 4;
|
||
|
|
var TYPE_NORMAL = 5;
|
||
|
|
|
||
|
|
var opType = TYPE_SYMBOL;
|
||
|
|
var declarations = {};
|
||
|
|
var declarationValue = null;
|
||
|
|
var currentDeclaration;
|
||
|
|
|
||
|
|
addSymbol(tokens[0]);
|
||
|
|
|
||
|
|
function addSymbol(symbol) {
|
||
|
|
if (!symbol) {
|
||
|
|
logSyntaxError();
|
||
|
|
}
|
||
|
|
var arrResult = symbol.match(/\[(.*?)\]/);
|
||
|
|
currentDeclaration = symbol.replace(/\[(.*?)\]/, '');
|
||
|
|
declarations[currentDeclaration] = {};
|
||
|
|
if (arrResult) {
|
||
|
|
declarations[currentDeclaration].isArray = true;
|
||
|
|
declarations[currentDeclaration].arraySize = arrResult[1];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
for (var i = 1; i < tokens.length; i++) {
|
||
|
|
var token = tokens[i];
|
||
|
|
if (!token) { // Empty token;
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
if (token === '=') {
|
||
|
|
if (opType !== TYPE_SYMBOL
|
||
|
|
&& opType !== TYPE_ARR) {
|
||
|
|
logSyntaxError();
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
opType = TYPE_ASSIGN;
|
||
|
|
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
else if (token === ':') {
|
||
|
|
opType = TYPE_SEMANTIC;
|
||
|
|
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
else if (token === ',') {
|
||
|
|
if (opType === TYPE_VEC) {
|
||
|
|
if (!(declarationValue instanceof Array)) {
|
||
|
|
logSyntaxError();
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
declarationValue.push(+tokens[++i]);
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
opType = TYPE_NORMAL;
|
||
|
|
}
|
||
|
|
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
else if (token === ')') {
|
||
|
|
declarations[currentDeclaration].value = new vendor.Float32Array(declarationValue);
|
||
|
|
declarationValue = null;
|
||
|
|
opType = TYPE_NORMAL;
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
else if (token === '(') {
|
||
|
|
if (opType !== TYPE_VEC) {
|
||
|
|
logSyntaxError();
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
if (!(declarationValue instanceof Array)) {
|
||
|
|
logSyntaxError();
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
declarationValue.push(+tokens[++i]);
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
else if (token.indexOf('vec') >= 0) {
|
||
|
|
if (opType !== TYPE_ASSIGN
|
||
|
|
// Compatitable with old syntax `symbol: [1,2,3]`
|
||
|
|
&& opType !== TYPE_SEMANTIC) {
|
||
|
|
logSyntaxError();
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
opType = TYPE_VEC;
|
||
|
|
declarationValue = [];
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
else if (opType === TYPE_ASSIGN) {
|
||
|
|
if (type === 'bool') {
|
||
|
|
declarations[currentDeclaration].value = token === 'true';
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
declarations[currentDeclaration].value = parseFloat(token);
|
||
|
|
}
|
||
|
|
declarationValue = null;
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
else if (opType === TYPE_SEMANTIC) {
|
||
|
|
var semantic = token;
|
||
|
|
if (attributeSemantics.indexOf(semantic) >= 0
|
||
|
|
|| uniformSemantics.indexOf(semantic) >= 0
|
||
|
|
|| matrixSemantics.indexOf(semantic) >= 0
|
||
|
|
) {
|
||
|
|
declarations[currentDeclaration].semantic = semantic;
|
||
|
|
}
|
||
|
|
else if (semantic === 'ignore' || semantic === 'unconfigurable') {
|
||
|
|
declarations[currentDeclaration].ignore = true;
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
// Try to parse as a default tvalue.
|
||
|
|
if (type === 'bool') {
|
||
|
|
declarations[currentDeclaration].value = semantic === 'true';
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
declarations[currentDeclaration].value = parseFloat(semantic);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
// treat as symbol.
|
||
|
|
addSymbol(token);
|
||
|
|
opType = TYPE_SYMBOL;
|
||
|
|
}
|
||
|
|
|
||
|
|
return declarations;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @constructor
|
||
|
|
* @extends clay.core.Base
|
||
|
|
* @alias clay.Shader
|
||
|
|
* @param {string} vertex
|
||
|
|
* @param {string} fragment
|
||
|
|
* @example
|
||
|
|
* // Create a phong shader
|
||
|
|
* var shader = new clay.Shader(
|
||
|
|
* clay.Shader.source('clay.standard.vertex'),
|
||
|
|
* clay.Shader.source('clay.standard.fragment')
|
||
|
|
* );
|
||
|
|
*/
|
||
|
|
function Shader(vertex, fragment) {
|
||
|
|
// First argument can be { vertex, fragment }
|
||
|
|
if (typeof vertex === 'object') {
|
||
|
|
fragment = vertex.fragment;
|
||
|
|
vertex = vertex.vertex;
|
||
|
|
}
|
||
|
|
|
||
|
|
vertex = removeComment(vertex);
|
||
|
|
fragment = removeComment(fragment);
|
||
|
|
|
||
|
|
this._shaderID = getShaderID(vertex, fragment);
|
||
|
|
|
||
|
|
this._vertexCode = Shader.parseImport(vertex);
|
||
|
|
this._fragmentCode = Shader.parseImport(fragment);
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @readOnly
|
||
|
|
*/
|
||
|
|
this.attributeSemantics = {};
|
||
|
|
/**
|
||
|
|
* @readOnly
|
||
|
|
*/
|
||
|
|
this.matrixSemantics = {};
|
||
|
|
/**
|
||
|
|
* @readOnly
|
||
|
|
*/
|
||
|
|
this.uniformSemantics = {};
|
||
|
|
/**
|
||
|
|
* @readOnly
|
||
|
|
*/
|
||
|
|
this.matrixSemanticKeys = [];
|
||
|
|
/**
|
||
|
|
* @readOnly
|
||
|
|
*/
|
||
|
|
this.uniformTemplates = {};
|
||
|
|
/**
|
||
|
|
* @readOnly
|
||
|
|
*/
|
||
|
|
this.attributes = {};
|
||
|
|
/**
|
||
|
|
* @readOnly
|
||
|
|
*/
|
||
|
|
this.textures = {};
|
||
|
|
/**
|
||
|
|
* @readOnly
|
||
|
|
*/
|
||
|
|
this.vertexDefines = {};
|
||
|
|
/**
|
||
|
|
* @readOnly
|
||
|
|
*/
|
||
|
|
this.fragmentDefines = {};
|
||
|
|
|
||
|
|
this._parseAttributes();
|
||
|
|
this._parseUniforms();
|
||
|
|
this._parseDefines();
|
||
|
|
}
|
||
|
|
|
||
|
|
Shader.prototype = {
|
||
|
|
|
||
|
|
constructor: Shader,
|
||
|
|
|
||
|
|
// Create a new uniform instance for material
|
||
|
|
createUniforms: function () {
|
||
|
|
var uniforms = {};
|
||
|
|
|
||
|
|
for (var symbol in this.uniformTemplates){
|
||
|
|
var uniformTpl = this.uniformTemplates[symbol];
|
||
|
|
uniforms[symbol] = {
|
||
|
|
type: uniformTpl.type,
|
||
|
|
value: uniformTpl.value()
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
return uniforms;
|
||
|
|
},
|
||
|
|
|
||
|
|
_parseImport: function () {
|
||
|
|
this._vertexCode = Shader.parseImport(this.vertex);
|
||
|
|
this._fragmentCode = Shader.parseImport(this.fragment);
|
||
|
|
},
|
||
|
|
|
||
|
|
_addSemanticUniform: function (symbol, uniformType, semantic) {
|
||
|
|
// This case is only for SKIN_MATRIX
|
||
|
|
// TODO
|
||
|
|
if (attributeSemantics.indexOf(semantic) >= 0) {
|
||
|
|
this.attributeSemantics[semantic] = {
|
||
|
|
symbol: symbol,
|
||
|
|
type: uniformType
|
||
|
|
};
|
||
|
|
}
|
||
|
|
else if (matrixSemantics.indexOf(semantic) >= 0) {
|
||
|
|
var isTranspose = false;
|
||
|
|
var semanticNoTranspose = semantic;
|
||
|
|
if (semantic.match(/TRANSPOSE$/)) {
|
||
|
|
isTranspose = true;
|
||
|
|
semanticNoTranspose = semantic.slice(0, -9);
|
||
|
|
}
|
||
|
|
this.matrixSemantics[semantic] = {
|
||
|
|
symbol: symbol,
|
||
|
|
type: uniformType,
|
||
|
|
isTranspose: isTranspose,
|
||
|
|
semanticNoTranspose: semanticNoTranspose
|
||
|
|
};
|
||
|
|
}
|
||
|
|
else if (uniformSemantics.indexOf(semantic) >= 0) {
|
||
|
|
this.uniformSemantics[semantic] = {
|
||
|
|
symbol: symbol,
|
||
|
|
type: uniformType
|
||
|
|
};
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
_addMaterialUniform: function (symbol, type, uniformType, defaultValueFunc, isArray, materialUniforms) {
|
||
|
|
materialUniforms[symbol] = {
|
||
|
|
type: uniformType,
|
||
|
|
value: isArray ? uniformValueConstructor['array'] : (defaultValueFunc || uniformValueConstructor[type]),
|
||
|
|
semantic: null
|
||
|
|
};
|
||
|
|
},
|
||
|
|
|
||
|
|
_parseUniforms: function () {
|
||
|
|
var uniforms = {};
|
||
|
|
var self = this;
|
||
|
|
var shaderType = 'vertex';
|
||
|
|
this._uniformList = [];
|
||
|
|
|
||
|
|
this._vertexCode = this._vertexCode.replace(uniformRegex, _uniformParser);
|
||
|
|
shaderType = 'fragment';
|
||
|
|
this._fragmentCode = this._fragmentCode.replace(uniformRegex, _uniformParser);
|
||
|
|
|
||
|
|
self.matrixSemanticKeys = Object.keys(this.matrixSemantics);
|
||
|
|
|
||
|
|
function makeDefaultValueFunc(value) {
|
||
|
|
return value != null ? function () { return value; } : null;
|
||
|
|
}
|
||
|
|
|
||
|
|
function _uniformParser(str, type, content) {
|
||
|
|
var declaredUniforms = parseDeclarations(type, content);
|
||
|
|
var uniformMainStr = [];
|
||
|
|
for (var symbol in declaredUniforms) {
|
||
|
|
|
||
|
|
var uniformInfo = declaredUniforms[symbol];
|
||
|
|
var semantic = uniformInfo.semantic;
|
||
|
|
var tmpStr = symbol;
|
||
|
|
var uniformType = uniformTypeMap[type];
|
||
|
|
var defaultValueFunc = makeDefaultValueFunc(declaredUniforms[symbol].value);
|
||
|
|
if (declaredUniforms[symbol].isArray) {
|
||
|
|
tmpStr += '[' + declaredUniforms[symbol].arraySize + ']';
|
||
|
|
uniformType += 'v';
|
||
|
|
}
|
||
|
|
|
||
|
|
uniformMainStr.push(tmpStr);
|
||
|
|
|
||
|
|
self._uniformList.push(symbol);
|
||
|
|
|
||
|
|
if (!uniformInfo.ignore) {
|
||
|
|
if (type === 'sampler2D' || type === 'samplerCube') {
|
||
|
|
// Texture is default disabled
|
||
|
|
self.textures[symbol] = {
|
||
|
|
shaderType: shaderType,
|
||
|
|
type: type
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
if (semantic) {
|
||
|
|
// TODO Should not declare multiple symbols if have semantic.
|
||
|
|
self._addSemanticUniform(symbol, uniformType, semantic);
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
self._addMaterialUniform(
|
||
|
|
symbol, type, uniformType, defaultValueFunc,
|
||
|
|
declaredUniforms[symbol].isArray, uniforms
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return uniformMainStr.length > 0
|
||
|
|
? 'uniform ' + type + ' ' + uniformMainStr.join(',') + ';\n' : '';
|
||
|
|
}
|
||
|
|
|
||
|
|
this.uniformTemplates = uniforms;
|
||
|
|
},
|
||
|
|
|
||
|
|
_parseAttributes: function () {
|
||
|
|
var attributes = {};
|
||
|
|
var self = this;
|
||
|
|
this._vertexCode = this._vertexCode.replace(attributeRegex, _attributeParser);
|
||
|
|
|
||
|
|
function _attributeParser(str, type, content) {
|
||
|
|
var declaredAttributes = parseDeclarations(type, content);
|
||
|
|
|
||
|
|
var size = attributeSizeMap[type] || 1;
|
||
|
|
var attributeMainStr = [];
|
||
|
|
for (var symbol in declaredAttributes) {
|
||
|
|
var semantic = declaredAttributes[symbol].semantic;
|
||
|
|
attributes[symbol] = {
|
||
|
|
// TODO Can only be float
|
||
|
|
type: 'float',
|
||
|
|
size: size,
|
||
|
|
semantic: semantic || null
|
||
|
|
};
|
||
|
|
// TODO Should not declare multiple symbols if have semantic.
|
||
|
|
if (semantic) {
|
||
|
|
if (attributeSemantics.indexOf(semantic) < 0) {
|
||
|
|
throw new Error('Unkown semantic "' + semantic + '"');
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
self.attributeSemantics[semantic] = {
|
||
|
|
symbol: symbol,
|
||
|
|
type: type
|
||
|
|
};
|
||
|
|
}
|
||
|
|
}
|
||
|
|
attributeMainStr.push(symbol);
|
||
|
|
}
|
||
|
|
|
||
|
|
return 'attribute ' + type + ' ' + attributeMainStr.join(',') + ';\n';
|
||
|
|
}
|
||
|
|
|
||
|
|
this.attributes = attributes;
|
||
|
|
},
|
||
|
|
|
||
|
|
_parseDefines: function () {
|
||
|
|
var self = this;
|
||
|
|
var shaderType = 'vertex';
|
||
|
|
this._vertexCode = this._vertexCode.replace(defineRegex, _defineParser);
|
||
|
|
shaderType = 'fragment';
|
||
|
|
this._fragmentCode = this._fragmentCode.replace(defineRegex, _defineParser);
|
||
|
|
|
||
|
|
function _defineParser(str, symbol, value) {
|
||
|
|
var defines = shaderType === 'vertex' ? self.vertexDefines : self.fragmentDefines;
|
||
|
|
if (!defines[symbol]) { // Haven't been defined by user
|
||
|
|
if (value === 'false') {
|
||
|
|
defines[symbol] = false;
|
||
|
|
}
|
||
|
|
else if (value === 'true') {
|
||
|
|
defines[symbol] = true;
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
defines[symbol] = value
|
||
|
|
// If can parse to float
|
||
|
|
? (isNaN(parseFloat(value)) ? value.trim() : parseFloat(value))
|
||
|
|
: null;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return '';
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Clone a new shader
|
||
|
|
* @return {clay.Shader}
|
||
|
|
*/
|
||
|
|
clone: function () {
|
||
|
|
var code = shaderCodeCache[this._shaderID];
|
||
|
|
var shader = new Shader(code.vertex, code.fragment);
|
||
|
|
return shader;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
if (Object.defineProperty) {
|
||
|
|
Object.defineProperty(Shader.prototype, 'shaderID', {
|
||
|
|
get: function () {
|
||
|
|
return this._shaderID;
|
||
|
|
}
|
||
|
|
});
|
||
|
|
Object.defineProperty(Shader.prototype, 'vertex', {
|
||
|
|
get: function () {
|
||
|
|
return this._vertexCode;
|
||
|
|
}
|
||
|
|
});
|
||
|
|
Object.defineProperty(Shader.prototype, 'fragment', {
|
||
|
|
get: function () {
|
||
|
|
return this._fragmentCode;
|
||
|
|
}
|
||
|
|
});
|
||
|
|
Object.defineProperty(Shader.prototype, 'uniforms', {
|
||
|
|
get: function () {
|
||
|
|
return this._uniformList;
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
var importRegex = /(@import)\s*([0-9a-zA-Z_\-\.]*)/g;
|
||
|
|
Shader.parseImport = function (shaderStr) {
|
||
|
|
shaderStr = shaderStr.replace(importRegex, function (str, importSymbol, importName) {
|
||
|
|
var str = Shader.source(importName);
|
||
|
|
if (str) {
|
||
|
|
// Recursively parse
|
||
|
|
return Shader.parseImport(str);
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
console.error('Shader chunk "' + importName + '" not existed in library');
|
||
|
|
return '';
|
||
|
|
}
|
||
|
|
});
|
||
|
|
return shaderStr;
|
||
|
|
};
|
||
|
|
|
||
|
|
var exportRegex = /(@export)\s*([0-9a-zA-Z_\-\.]*)\s*\n([\s\S]*?)@end/g;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Import shader source
|
||
|
|
* @param {string} shaderStr
|
||
|
|
* @memberOf clay.Shader
|
||
|
|
*/
|
||
|
|
Shader['import'] = function (shaderStr) {
|
||
|
|
shaderStr.replace(exportRegex, function (str, exportSymbol, exportName, code) {
|
||
|
|
var code = code.replace(/(^[\s\t\xa0\u3000]+)|([\u3000\xa0\s\t]+\x24)/g, '');
|
||
|
|
if (code) {
|
||
|
|
var parts = exportName.split('.');
|
||
|
|
var obj = Shader.codes;
|
||
|
|
var i = 0;
|
||
|
|
var key;
|
||
|
|
while (i < parts.length - 1) {
|
||
|
|
key = parts[i++];
|
||
|
|
if (!obj[key]) {
|
||
|
|
obj[key] = {};
|
||
|
|
}
|
||
|
|
obj = obj[key];
|
||
|
|
}
|
||
|
|
key = parts[i];
|
||
|
|
obj[key] = code;
|
||
|
|
}
|
||
|
|
return code;
|
||
|
|
});
|
||
|
|
};
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Library to store all the loaded shader codes
|
||
|
|
* @type {Object}
|
||
|
|
* @readOnly
|
||
|
|
* @memberOf clay.Shader
|
||
|
|
*/
|
||
|
|
Shader.codes = {};
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get shader source
|
||
|
|
* @param {string} name
|
||
|
|
* @return {string}
|
||
|
|
*/
|
||
|
|
Shader.source = function (name) {
|
||
|
|
var parts = name.split('.');
|
||
|
|
var obj = Shader.codes;
|
||
|
|
var i = 0;
|
||
|
|
while (obj && i < parts.length) {
|
||
|
|
var key = parts[i++];
|
||
|
|
obj = obj[key];
|
||
|
|
}
|
||
|
|
if (typeof obj !== 'string') {
|
||
|
|
// FIXME Use default instead
|
||
|
|
console.error('Shader "' + name + '" not existed in library');
|
||
|
|
return '';
|
||
|
|
}
|
||
|
|
return obj;
|
||
|
|
};
|
||
|
|
|
||
|
|
export default Shader;
|