babylon.effect.ts 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993
  1. module BABYLON {
  2. export class EffectFallbacks {
  3. private _defines = {};
  4. private _currentRank = 32;
  5. private _maxRank = -1;
  6. private _mesh: AbstractMesh;
  7. private _meshRank: number;
  8. public addFallback(rank: number, define: string): void {
  9. if (!this._defines[rank]) {
  10. if (rank < this._currentRank) {
  11. this._currentRank = rank;
  12. }
  13. if (rank > this._maxRank) {
  14. this._maxRank = rank;
  15. }
  16. this._defines[rank] = new Array<String>();
  17. }
  18. this._defines[rank].push(define);
  19. }
  20. public addCPUSkinningFallback(rank: number, mesh: BABYLON.AbstractMesh) {
  21. this._meshRank = rank;
  22. this._mesh = mesh;
  23. if (rank < this._currentRank) {
  24. this._currentRank = rank;
  25. }
  26. if (rank > this._maxRank) {
  27. this._maxRank = rank;
  28. }
  29. }
  30. public get isMoreFallbacks(): boolean {
  31. return this._currentRank <= this._maxRank;
  32. }
  33. public reduce(currentDefines: string): string {
  34. // First we try to switch to CPU skinning
  35. if (this._mesh && this._mesh.computeBonesUsingShaders && this._mesh.numBoneInfluencers > 0) {
  36. this._mesh.computeBonesUsingShaders = false;
  37. currentDefines = currentDefines.replace("#define NUM_BONE_INFLUENCERS " + this._mesh.numBoneInfluencers, "#define NUM_BONE_INFLUENCERS 0");
  38. Tools.Log("Falling back to CPU skinning for " + this._mesh.name);
  39. var scene = this._mesh.getScene();
  40. for (var index = 0; index < scene.meshes.length; index++) {
  41. var otherMesh = scene.meshes[index];
  42. if (otherMesh.material === this._mesh.material && otherMesh.computeBonesUsingShaders && otherMesh.numBoneInfluencers > 0) {
  43. otherMesh.computeBonesUsingShaders = false;
  44. }
  45. }
  46. }
  47. else {
  48. var currentFallbacks = this._defines[this._currentRank];
  49. if (currentFallbacks) {
  50. for (var index = 0; index < currentFallbacks.length; index++) {
  51. currentDefines = currentDefines.replace("#define " + currentFallbacks[index], "");
  52. }
  53. }
  54. this._currentRank++;
  55. }
  56. return currentDefines;
  57. }
  58. }
  59. export class EffectCreationOptions {
  60. public attributes: string[];
  61. public uniformsNames: string[];
  62. public uniformBuffersNames: string[];
  63. public samplers: string[];
  64. public defines: any;
  65. public fallbacks: EffectFallbacks;
  66. public onCompiled: (effect: Effect) => void;
  67. public onError: (effect: Effect, errors: string) => void;
  68. public indexParameters: any;
  69. public maxSimultaneousLights: number;
  70. }
  71. export class Effect {
  72. public name: any;
  73. public defines: string;
  74. public onCompiled: (effect: Effect) => void;
  75. public onError: (effect: Effect, errors: string) => void;
  76. public onBind: (effect: Effect) => void;
  77. public uniqueId = 0;
  78. private static _uniqueIdSeed = 0;
  79. private _engine: Engine;
  80. private _uniformBuffersNames: { [key: string]: number } = {};
  81. private _uniformsNames: string[];
  82. private _samplers: string[];
  83. private _isReady = false;
  84. private _compilationError = "";
  85. private _attributesNames: string[];
  86. private _attributes: number[];
  87. private _uniforms: WebGLUniformLocation[];
  88. public _key: string;
  89. private _indexParameters: any;
  90. private _fallbacks: EffectFallbacks;
  91. private _program: WebGLProgram;
  92. private _valueCache: { [key: string]: any } = {};
  93. private static _baseCache: { [key: number]: WebGLBuffer } = {};
  94. constructor(baseName: any, attributesNamesOrOptions: string[] | EffectCreationOptions, uniformsNamesOrEngine: string[] | Engine, samplers?: string[], engine?: Engine, defines?: string, fallbacks?: EffectFallbacks, onCompiled?: (effect: Effect) => void, onError?: (effect: Effect, errors: string) => void, indexParameters?: any) {
  95. this.name = baseName;
  96. if ((<EffectCreationOptions>attributesNamesOrOptions).attributes) {
  97. var options = <EffectCreationOptions>attributesNamesOrOptions;
  98. this._engine = <Engine>uniformsNamesOrEngine;
  99. this._attributesNames = options.attributes;
  100. this._uniformsNames = options.uniformsNames.concat(options.samplers);
  101. this._samplers = options.samplers;
  102. this.defines = options.defines;
  103. this.onError = options.onError;
  104. this.onCompiled = options.onCompiled;
  105. this._fallbacks = options.fallbacks;
  106. this._indexParameters = options.indexParameters;
  107. if (options.uniformBuffersNames) {
  108. for (var i = 0; i < options.uniformBuffersNames.length; i++) {
  109. this._uniformBuffersNames[options.uniformBuffersNames[i]] = i;
  110. }
  111. }
  112. } else {
  113. this._engine = engine;
  114. this.defines = defines;
  115. this._uniformsNames = (<string[]>uniformsNamesOrEngine).concat(samplers);
  116. this._samplers = samplers;
  117. this._attributesNames = (<string[]>attributesNamesOrOptions);
  118. this.onError = onError;
  119. this.onCompiled = onCompiled;
  120. this._indexParameters = indexParameters;
  121. this._fallbacks = fallbacks;
  122. }
  123. this.uniqueId = Effect._uniqueIdSeed++;
  124. var vertexSource;
  125. var fragmentSource;
  126. if (baseName.vertexElement) {
  127. vertexSource = document.getElementById(baseName.vertexElement);
  128. if (!vertexSource) {
  129. vertexSource = baseName.vertexElement;
  130. }
  131. } else {
  132. vertexSource = baseName.vertex || baseName;
  133. }
  134. if (baseName.fragmentElement) {
  135. fragmentSource = document.getElementById(baseName.fragmentElement);
  136. if (!fragmentSource) {
  137. fragmentSource = baseName.fragmentElement;
  138. }
  139. } else {
  140. fragmentSource = baseName.fragment || baseName;
  141. }
  142. this._loadVertexShader(vertexSource, vertexCode => {
  143. this._processIncludes(vertexCode, vertexCodeWithIncludes => {
  144. this._processShaderConversion(vertexCodeWithIncludes, false, migratedVertexCode => {
  145. this._loadFragmentShader(fragmentSource, (fragmentCode) => {
  146. this._processIncludes(fragmentCode, fragmentCodeWithIncludes => {
  147. this._processShaderConversion(fragmentCodeWithIncludes, true, migratedFragmentCode => {
  148. this._prepareEffect(migratedVertexCode, migratedFragmentCode, this._attributesNames, this.defines, this._fallbacks);
  149. });
  150. });
  151. });
  152. });
  153. });
  154. });
  155. }
  156. public get key(): string {
  157. return this._key;
  158. }
  159. // Properties
  160. public isReady(): boolean {
  161. return this._isReady;
  162. }
  163. public getProgram(): WebGLProgram {
  164. return this._program;
  165. }
  166. public getAttributesNames(): string[] {
  167. return this._attributesNames;
  168. }
  169. public getAttributeLocation(index: number): number {
  170. return this._attributes[index];
  171. }
  172. public getAttributeLocationByName(name: string): number {
  173. var index = this._attributesNames.indexOf(name);
  174. return this._attributes[index];
  175. }
  176. public getAttributesCount(): number {
  177. return this._attributes.length;
  178. }
  179. public getUniformIndex(uniformName: string): number {
  180. return this._uniformsNames.indexOf(uniformName);
  181. }
  182. public getUniform(uniformName: string): WebGLUniformLocation {
  183. return this._uniforms[this._uniformsNames.indexOf(uniformName)];
  184. }
  185. public getSamplers(): string[] {
  186. return this._samplers;
  187. }
  188. public getCompilationError(): string {
  189. return this._compilationError;
  190. }
  191. public getVertexShaderSource(): string {
  192. return this._evaluateDefinesOnString(this._engine.getVertexShaderSource(this._program));
  193. }
  194. public getFragmentShaderSource(): string {
  195. return this._evaluateDefinesOnString(this._engine.getFragmentShaderSource(this._program));
  196. }
  197. // Methods
  198. public _loadVertexShader(vertex: any, callback: (data: any) => void): void {
  199. // DOM element ?
  200. if (vertex instanceof HTMLElement) {
  201. var vertexCode = Tools.GetDOMTextContent(vertex);
  202. callback(vertexCode);
  203. return;
  204. }
  205. // Base64 encoded ?
  206. if (vertex.substr(0, 7) === "base64:") {
  207. var vertexBinary = window.atob(vertex.substr(7));
  208. callback(vertexBinary);
  209. return;
  210. }
  211. // Is in local store ?
  212. if (Effect.ShadersStore[vertex + "VertexShader"]) {
  213. callback(Effect.ShadersStore[vertex + "VertexShader"]);
  214. return;
  215. }
  216. var vertexShaderUrl;
  217. if (vertex[0] === "." || vertex[0] === "/" || vertex.indexOf("http") > -1) {
  218. vertexShaderUrl = vertex;
  219. } else {
  220. vertexShaderUrl = Engine.ShadersRepository + vertex;
  221. }
  222. // Vertex shader
  223. Tools.LoadFile(vertexShaderUrl + ".vertex.fx", callback);
  224. }
  225. public _loadFragmentShader(fragment: any, callback: (data: any) => void): void {
  226. // DOM element ?
  227. if (fragment instanceof HTMLElement) {
  228. var fragmentCode = Tools.GetDOMTextContent(fragment);
  229. callback(fragmentCode);
  230. return;
  231. }
  232. // Base64 encoded ?
  233. if (fragment.substr(0, 7) === "base64:") {
  234. var fragmentBinary = window.atob(fragment.substr(7));
  235. callback(fragmentBinary);
  236. return;
  237. }
  238. // Is in local store ?
  239. if (Effect.ShadersStore[fragment + "PixelShader"]) {
  240. callback(Effect.ShadersStore[fragment + "PixelShader"]);
  241. return;
  242. }
  243. if (Effect.ShadersStore[fragment + "FragmentShader"]) {
  244. callback(Effect.ShadersStore[fragment + "FragmentShader"]);
  245. return;
  246. }
  247. var fragmentShaderUrl;
  248. if (fragment[0] === "." || fragment[0] === "/" || fragment.indexOf("http") > -1) {
  249. fragmentShaderUrl = fragment;
  250. } else {
  251. fragmentShaderUrl = Engine.ShadersRepository + fragment;
  252. }
  253. // Fragment shader
  254. Tools.LoadFile(fragmentShaderUrl + ".fragment.fx", callback);
  255. }
  256. private _dumpShadersSource(vertexCode: string, fragmentCode: string, defines: string): void {
  257. // Rebuild shaders source code
  258. var shaderVersion = (this._engine.webGLVersion > 1) ? "#version 300 es\n" : "";
  259. var prefix = shaderVersion + (defines ? defines + "\n" : "");
  260. vertexCode = prefix + vertexCode;
  261. fragmentCode = prefix + fragmentCode;
  262. // Number lines of shaders source code
  263. var i = 2;
  264. var regex = /\n/gm;
  265. var formattedVertexCode = "\n1\t" + vertexCode.replace(regex, function() { return "\n" + (i++) + "\t"; });
  266. i = 2;
  267. var formattedFragmentCode = "\n1\t" + fragmentCode.replace(regex, function() { return "\n" + (i++) + "\t"; });
  268. // Dump shaders name and formatted source code
  269. if (this.name.vertexElement) {
  270. BABYLON.Tools.Error("Vertex shader: " + this.name.vertexElement + formattedVertexCode);
  271. BABYLON.Tools.Error("Fragment shader: " + this.name.fragmentElement + formattedFragmentCode);
  272. }
  273. else if (this.name.vertex) {
  274. BABYLON.Tools.Error("Vertex shader: " + this.name.vertex + formattedVertexCode);
  275. BABYLON.Tools.Error("Fragment shader: " + this.name.fragment + formattedFragmentCode);
  276. }
  277. else {
  278. BABYLON.Tools.Error("Vertex shader: " + this.name + formattedVertexCode);
  279. BABYLON.Tools.Error("Fragment shader: " + this.name + formattedFragmentCode);
  280. }
  281. };
  282. private _processShaderConversion(sourceCode: string, isFragment: boolean, callback: (data: any) => void): void {
  283. var preparedSourceCode = this._processPrecision(sourceCode);
  284. if (this._engine.webGLVersion == 1) {
  285. callback(preparedSourceCode);
  286. return;
  287. }
  288. // Already converted
  289. if (preparedSourceCode.indexOf("#version 3") !== -1) {
  290. callback(preparedSourceCode);
  291. return;
  292. }
  293. // Remove extensions
  294. // #extension GL_OES_standard_derivatives : enable
  295. // #extension GL_EXT_shader_texture_lod : enable
  296. // #extension GL_EXT_frag_depth : enable
  297. var regex = /#extension.+(GL_OES_standard_derivatives|GL_EXT_shader_texture_lod|GL_EXT_frag_depth).+enable/g;
  298. var result = preparedSourceCode.replace(regex, "");
  299. // Migrate to GLSL v300
  300. result = result.replace(/varying\s/g, isFragment ? "in " : "out ");
  301. result = result.replace(/attribute[ \t]/g, "in ");
  302. result = result.replace(/[ \t]attribute/g, " in");
  303. if (isFragment) {
  304. result = result.replace(/texture2DLodEXT\(/g, "textureLod(");
  305. result = result.replace(/textureCubeLodEXT\(/g, "textureLod(");
  306. result = result.replace(/texture2D\(/g, "texture(");
  307. result = result.replace(/textureCube\(/g, "texture(");
  308. result = result.replace(/gl_FragDepthEXT/g, "gl_FragDepth");
  309. result = result.replace(/gl_FragColor/g, "glFragColor");
  310. result = result.replace(/void\s+?main\(/g, "out vec4 glFragColor;\nvoid main(");
  311. }
  312. callback(result);
  313. }
  314. private _processIncludes(sourceCode: string, callback: (data: any) => void): void {
  315. var regex = /#include<(.+)>(\((.*)\))*(\[(.*)\])*/g;
  316. var match = regex.exec(sourceCode);
  317. var returnValue = new String(sourceCode);
  318. while (match != null) {
  319. var includeFile = match[1];
  320. // Uniform declaration
  321. if (includeFile.indexOf("__decl__") !== -1) {
  322. includeFile = includeFile.replace(/__decl__/, "");
  323. if (this._engine.webGLVersion != 1) {
  324. includeFile = includeFile.replace(/Vertex/, "Ubo");
  325. includeFile = includeFile.replace(/Fragment/, "Ubo");
  326. }
  327. includeFile = includeFile + "Declaration";
  328. }
  329. if (Effect.IncludesShadersStore[includeFile]) {
  330. // Substitution
  331. var includeContent = Effect.IncludesShadersStore[includeFile];
  332. if (match[2]) {
  333. var splits = match[3].split(",");
  334. for (var index = 0; index < splits.length; index += 2) {
  335. var source = new RegExp(splits[index], "g");
  336. var dest = splits[index + 1];
  337. includeContent = includeContent.replace(source, dest);
  338. }
  339. }
  340. if (match[4]) {
  341. var indexString = match[5];
  342. if (indexString.indexOf("..") !== -1) {
  343. var indexSplits = indexString.split("..");
  344. var minIndex = parseInt(indexSplits[0]);
  345. var maxIndex = parseInt(indexSplits[1]);
  346. var sourceIncludeContent = includeContent.slice(0);
  347. includeContent = "";
  348. if (isNaN(maxIndex)) {
  349. maxIndex = this._indexParameters[indexSplits[1]];
  350. }
  351. for (var i = minIndex; i < maxIndex; i++) {
  352. if (this._engine.webGLVersion === 1) {
  353. // Ubo replacement
  354. sourceIncludeContent = sourceIncludeContent.replace(/light\{X\}.(\w*)/g, (str: string, p1: string) => {
  355. return p1 + "{X}";
  356. });
  357. }
  358. includeContent += sourceIncludeContent.replace(/\{X\}/g, i) + "\n";
  359. }
  360. } else {
  361. if (this._engine.webGLVersion === 1) {
  362. // Ubo replacement
  363. includeContent = includeContent.replace(/light\{X\}.(\w*)/g, (str: string, p1: string) => {
  364. return p1 + "{X}";
  365. });
  366. }
  367. includeContent = includeContent.replace(/\{X\}/g, indexString);
  368. }
  369. }
  370. // Replace
  371. returnValue = returnValue.replace(match[0], includeContent);
  372. } else {
  373. var includeShaderUrl = Engine.ShadersRepository + "ShadersInclude/" + includeFile + ".fx";
  374. Tools.LoadFile(includeShaderUrl, (fileContent) => {
  375. Effect.IncludesShadersStore[includeFile] = fileContent;
  376. this._processIncludes(<string>returnValue, callback);
  377. });
  378. return;
  379. }
  380. match = regex.exec(sourceCode);
  381. }
  382. callback(returnValue);
  383. }
  384. private _processPrecision(source: string): string {
  385. if (source.indexOf("precision highp float") === -1) {
  386. if (!this._engine.getCaps().highPrecisionShaderSupported) {
  387. source = "precision mediump float;\n" + source;
  388. } else {
  389. source = "precision highp float;\n" + source;
  390. }
  391. } else {
  392. if (!this._engine.getCaps().highPrecisionShaderSupported) { // Moving highp to mediump
  393. source = source.replace("precision highp float", "precision mediump float");
  394. }
  395. }
  396. return source;
  397. }
  398. private _prepareEffect(vertexSourceCode: string, fragmentSourceCode: string, attributesNames: string[], defines: string, fallbacks?: EffectFallbacks): void {
  399. try {
  400. var engine = this._engine;
  401. this._program = engine.createShaderProgram(vertexSourceCode, fragmentSourceCode, defines);
  402. if (engine.webGLVersion > 1) {
  403. for (var name in this._uniformBuffersNames) {
  404. this.bindUniformBlock(name, this._uniformBuffersNames[name]);
  405. }
  406. }
  407. this._uniforms = engine.getUniforms(this._program, this._uniformsNames);
  408. this._attributes = engine.getAttributes(this._program, attributesNames);
  409. var index: number;
  410. for (index = 0; index < this._samplers.length; index++) {
  411. var sampler = this.getUniform(this._samplers[index]);
  412. if (sampler == null) {
  413. this._samplers.splice(index, 1);
  414. index--;
  415. }
  416. }
  417. engine.bindSamplers(this);
  418. this._compilationError = "";
  419. this._isReady = true;
  420. if (this.onCompiled) {
  421. this.onCompiled(this);
  422. }
  423. } catch (e) {
  424. this._compilationError = e.message;
  425. // Let's go through fallbacks then
  426. Tools.Error("Unable to compile effect:");
  427. BABYLON.Tools.Error("Uniforms: " + this._uniformsNames.map(function(uniform) {
  428. return " " + uniform;
  429. }));
  430. BABYLON.Tools.Error("Attributes: " + attributesNames.map(function(attribute) {
  431. return " " + attribute;
  432. }));
  433. this._dumpShadersSource(vertexSourceCode, fragmentSourceCode, defines);
  434. Tools.Error("Error: " + this._compilationError);
  435. if (fallbacks && fallbacks.isMoreFallbacks) {
  436. Tools.Error("Trying next fallback.");
  437. defines = fallbacks.reduce(defines);
  438. this._prepareEffect(vertexSourceCode, fragmentSourceCode, attributesNames, defines, fallbacks);
  439. } else { // Sorry we did everything we can
  440. if (this.onError) {
  441. this.onError(this, this._compilationError);
  442. }
  443. }
  444. }
  445. }
  446. public get isSupported(): boolean {
  447. return this._compilationError === "";
  448. }
  449. public _bindTexture(channel: string, texture: WebGLTexture): void {
  450. this._engine._bindTexture(this._samplers.indexOf(channel), texture);
  451. }
  452. public setTexture(channel: string, texture: BaseTexture): void {
  453. this._engine.setTexture(this._samplers.indexOf(channel), this.getUniform(channel), texture);
  454. }
  455. public setTextureArray(channel: string, textures: BaseTexture[]): void {
  456. if (this._samplers.indexOf(channel + "Ex") === -1) {
  457. var initialPos = this._samplers.indexOf(channel);
  458. for (var index = 1; index < textures.length; index++) {
  459. this._samplers.splice(initialPos + index, 0, channel + "Ex");
  460. }
  461. }
  462. this._engine.setTextureArray(this._samplers.indexOf(channel), this.getUniform(channel), textures);
  463. }
  464. public setTextureFromPostProcess(channel: string, postProcess: PostProcess): void {
  465. this._engine.setTextureFromPostProcess(this._samplers.indexOf(channel), postProcess);
  466. }
  467. public _cacheMatrix(uniformName: string, matrix: Matrix): boolean {
  468. var cache = this._valueCache[uniformName];
  469. var flag = matrix.updateFlag;
  470. if (cache !== undefined && cache === flag) {
  471. return false;
  472. }
  473. this._valueCache[uniformName] = flag;
  474. return true;
  475. }
  476. public _cacheFloat2(uniformName: string, x: number, y: number): boolean {
  477. var cache = this._valueCache[uniformName];
  478. if (!cache) {
  479. cache = [x, y];
  480. this._valueCache[uniformName] = cache;
  481. return true;
  482. }
  483. var changed = false;
  484. if (cache[0] !== x) {
  485. cache[0] = x;
  486. changed = true;
  487. }
  488. if (cache[1] !== y) {
  489. cache[1] = y;
  490. changed = true;
  491. }
  492. return changed;
  493. }
  494. public _cacheFloat3(uniformName: string, x: number, y: number, z: number): boolean {
  495. var cache = this._valueCache[uniformName];
  496. if (!cache) {
  497. cache = [x, y, z];
  498. this._valueCache[uniformName] = cache;
  499. return true;
  500. }
  501. var changed = false;
  502. if (cache[0] !== x) {
  503. cache[0] = x;
  504. changed = true;
  505. }
  506. if (cache[1] !== y) {
  507. cache[1] = y;
  508. changed = true;
  509. }
  510. if (cache[2] !== z) {
  511. cache[2] = z;
  512. changed = true;
  513. }
  514. return changed;
  515. }
  516. public _cacheFloat4(uniformName: string, x: number, y: number, z: number, w: number): boolean {
  517. var cache = this._valueCache[uniformName];
  518. if (!cache) {
  519. cache = [x, y, z, w];
  520. this._valueCache[uniformName] = cache;
  521. return true;
  522. }
  523. var changed = false;
  524. if (cache[0] !== x) {
  525. cache[0] = x;
  526. changed = true;
  527. }
  528. if (cache[1] !== y) {
  529. cache[1] = y;
  530. changed = true;
  531. }
  532. if (cache[2] !== z) {
  533. cache[2] = z;
  534. changed = true;
  535. }
  536. if (cache[3] !== w) {
  537. cache[3] = w;
  538. changed = true;
  539. }
  540. return changed;
  541. }
  542. public bindUniformBuffer(buffer: WebGLBuffer, name: string): void {
  543. if (Effect._baseCache[this._uniformBuffersNames[name]] === buffer) {
  544. return;
  545. }
  546. Effect._baseCache[this._uniformBuffersNames[name]] = buffer;
  547. this._engine.bindUniformBufferBase(buffer, this._uniformBuffersNames[name]);
  548. }
  549. public bindUniformBlock(blockName: string, index: number): void {
  550. this._engine.bindUniformBlock(this._program, blockName, index);
  551. }
  552. public setIntArray(uniformName: string, array: Int32Array): Effect {
  553. this._valueCache[uniformName] = null;
  554. this._engine.setIntArray(this.getUniform(uniformName), array);
  555. return this;
  556. }
  557. public setIntArray2(uniformName: string, array: Int32Array): Effect {
  558. this._valueCache[uniformName] = null;
  559. this._engine.setIntArray2(this.getUniform(uniformName), array);
  560. return this;
  561. }
  562. public setIntArray3(uniformName: string, array: Int32Array): Effect {
  563. this._valueCache[uniformName] = null;
  564. this._engine.setIntArray3(this.getUniform(uniformName), array);
  565. return this;
  566. }
  567. public setIntArray4(uniformName: string, array: Int32Array): Effect {
  568. this._valueCache[uniformName] = null;
  569. this._engine.setIntArray4(this.getUniform(uniformName), array);
  570. return this;
  571. }
  572. public setFloatArray(uniformName: string, array: Float32Array): Effect {
  573. this._valueCache[uniformName] = null;
  574. this._engine.setFloatArray(this.getUniform(uniformName), array);
  575. return this;
  576. }
  577. public setFloatArray2(uniformName: string, array: Float32Array): Effect {
  578. this._valueCache[uniformName] = null;
  579. this._engine.setFloatArray2(this.getUniform(uniformName), array);
  580. return this;
  581. }
  582. public setFloatArray3(uniformName: string, array: Float32Array): Effect {
  583. this._valueCache[uniformName] = null;
  584. this._engine.setFloatArray3(this.getUniform(uniformName), array);
  585. return this;
  586. }
  587. public setFloatArray4(uniformName: string, array: Float32Array): Effect {
  588. this._valueCache[uniformName] = null;
  589. this._engine.setFloatArray4(this.getUniform(uniformName), array);
  590. return this;
  591. }
  592. public setArray(uniformName: string, array: number[]): Effect {
  593. this._valueCache[uniformName] = null;
  594. this._engine.setArray(this.getUniform(uniformName), array);
  595. return this;
  596. }
  597. public setArray2(uniformName: string, array: number[]): Effect {
  598. this._valueCache[uniformName] = null;
  599. this._engine.setArray2(this.getUniform(uniformName), array);
  600. return this;
  601. }
  602. public setArray3(uniformName: string, array: number[]): Effect {
  603. this._valueCache[uniformName] = null;
  604. this._engine.setArray3(this.getUniform(uniformName), array);
  605. return this;
  606. }
  607. public setArray4(uniformName: string, array: number[]): Effect {
  608. this._valueCache[uniformName] = null;
  609. this._engine.setArray4(this.getUniform(uniformName), array);
  610. return this;
  611. }
  612. public setMatrices(uniformName: string, matrices: Float32Array): Effect {
  613. this._valueCache[uniformName] = null;
  614. this._engine.setMatrices(this.getUniform(uniformName), matrices);
  615. return this;
  616. }
  617. public setMatrix(uniformName: string, matrix: Matrix): Effect {
  618. if (this._cacheMatrix(uniformName, matrix)) {
  619. this._engine.setMatrix(this.getUniform(uniformName), matrix);
  620. }
  621. return this;
  622. }
  623. public setMatrix3x3(uniformName: string, matrix: Float32Array): Effect {
  624. this._valueCache[uniformName] = null;
  625. this._engine.setMatrix3x3(this.getUniform(uniformName), matrix);
  626. return this;
  627. }
  628. public setMatrix2x2(uniformName: string, matrix: Float32Array): Effect {
  629. this._valueCache[uniformName] = null;
  630. this._engine.setMatrix2x2(this.getUniform(uniformName), matrix);
  631. return this;
  632. }
  633. public setFloat(uniformName: string, value: number): Effect {
  634. var cache = this._valueCache[uniformName];
  635. if (cache !== undefined && cache === value)
  636. return this;
  637. this._valueCache[uniformName] = value;
  638. this._engine.setFloat(this.getUniform(uniformName), value);
  639. return this;
  640. }
  641. public setBool(uniformName: string, bool: boolean): Effect {
  642. var cache = this._valueCache[uniformName];
  643. if (cache !== undefined && cache === bool)
  644. return this;
  645. this._valueCache[uniformName] = bool;
  646. this._engine.setBool(this.getUniform(uniformName), bool ? 1 : 0);
  647. return this;
  648. }
  649. public setVector2(uniformName: string, vector2: Vector2): Effect {
  650. if (this._cacheFloat2(uniformName, vector2.x, vector2.y)) {
  651. this._engine.setFloat2(this.getUniform(uniformName), vector2.x, vector2.y);
  652. }
  653. return this;
  654. }
  655. public setFloat2(uniformName: string, x: number, y: number): Effect {
  656. if (this._cacheFloat2(uniformName, x, y)) {
  657. this._engine.setFloat2(this.getUniform(uniformName), x, y);
  658. }
  659. return this;
  660. }
  661. public setVector3(uniformName: string, vector3: Vector3): Effect {
  662. if (this._cacheFloat3(uniformName, vector3.x, vector3.y, vector3.z)) {
  663. this._engine.setFloat3(this.getUniform(uniformName), vector3.x, vector3.y, vector3.z);
  664. }
  665. return this;
  666. }
  667. public setFloat3(uniformName: string, x: number, y: number, z: number): Effect {
  668. if (this._cacheFloat3(uniformName, x, y, z)) {
  669. this._engine.setFloat3(this.getUniform(uniformName), x, y, z);
  670. }
  671. return this;
  672. }
  673. public setVector4(uniformName: string, vector4: Vector4): Effect {
  674. if (this._cacheFloat4(uniformName, vector4.x, vector4.y, vector4.z, vector4.w)) {
  675. this._engine.setFloat4(this.getUniform(uniformName), vector4.x, vector4.y, vector4.z, vector4.w);
  676. }
  677. return this;
  678. }
  679. public setFloat4(uniformName: string, x: number, y: number, z: number, w: number): Effect {
  680. if (this._cacheFloat4(uniformName, x, y, z, w)) {
  681. this._engine.setFloat4(this.getUniform(uniformName), x, y, z, w);
  682. }
  683. return this;
  684. }
  685. public setColor3(uniformName: string, color3: Color3): Effect {
  686. if (this._cacheFloat3(uniformName, color3.r, color3.g, color3.b)) {
  687. this._engine.setColor3(this.getUniform(uniformName), color3);
  688. }
  689. return this;
  690. }
  691. public setColor4(uniformName: string, color3: Color3, alpha: number): Effect {
  692. if (this._cacheFloat4(uniformName, color3.r, color3.g, color3.b, alpha)) {
  693. this._engine.setColor4(this.getUniform(uniformName), color3, alpha);
  694. }
  695. return this;
  696. }
  697. private _recombineShader(node: any): string {
  698. if (node.define) {
  699. if (node.condition) {
  700. var defineIndex = this.defines.indexOf("#define " + node.define);
  701. if (defineIndex === -1) {
  702. return null;
  703. }
  704. var nextComma = this.defines.indexOf("\n", defineIndex);
  705. var defineValue = this.defines.substr(defineIndex + 7, nextComma - defineIndex - 7).replace(node.define, "").trim();
  706. var condition = defineValue + node.condition;
  707. if (!eval(condition)) {
  708. return null;
  709. }
  710. }
  711. else if (node.ndef) {
  712. if (this.defines.indexOf("#define " + node.define) !== -1) {
  713. return null;
  714. }
  715. }
  716. else if (this.defines.indexOf("#define " + node.define) === -1) {
  717. return null;
  718. }
  719. }
  720. var result = "";
  721. for (var index = 0; index < node.children.length; index++) {
  722. var line = node.children[index];
  723. if (line.children) {
  724. var combined = this._recombineShader(line);
  725. if (combined !== null) {
  726. result += combined + "\r\n";
  727. }
  728. continue;
  729. }
  730. if (line.length > 0) {
  731. result += line + "\r\n";
  732. }
  733. }
  734. return result;
  735. }
  736. private _evaluateDefinesOnString(shaderString: string): string {
  737. var root = <any>{
  738. children: []
  739. };
  740. var currentNode = root;
  741. var lines = shaderString.split("\n");
  742. for (var index = 0; index < lines.length; index++) {
  743. var line = lines[index].trim();
  744. // #ifdef
  745. var pos = line.indexOf("#ifdef ");
  746. if (pos !== -1) {
  747. var define = line.substr(pos + 7);
  748. var newNode = {
  749. condition: null,
  750. ndef: false,
  751. define: define,
  752. children: [],
  753. parent: currentNode
  754. }
  755. currentNode.children.push(newNode);
  756. currentNode = newNode;
  757. continue;
  758. }
  759. // #ifndef
  760. var pos = line.indexOf("#ifndef ");
  761. if (pos !== -1) {
  762. var define = line.substr(pos + 8);
  763. newNode = {
  764. condition: null,
  765. define: define,
  766. ndef: true,
  767. children: [],
  768. parent: currentNode
  769. }
  770. currentNode.children.push(newNode);
  771. currentNode = newNode;
  772. continue;
  773. }
  774. // #if
  775. var pos = line.indexOf("#if ");
  776. if (pos !== -1) {
  777. var define = line.substr(pos + 4).trim();
  778. var conditionPos = define.indexOf(" ");
  779. newNode = {
  780. condition: define.substr(conditionPos + 1),
  781. define: define.substr(0, conditionPos),
  782. ndef: false,
  783. children: [],
  784. parent: currentNode
  785. }
  786. currentNode.children.push(newNode);
  787. currentNode = newNode;
  788. continue;
  789. }
  790. // #endif
  791. pos = line.indexOf("#endif");
  792. if (pos !== -1) {
  793. currentNode = currentNode.parent;
  794. continue;
  795. }
  796. currentNode.children.push(line);
  797. }
  798. // Recombine
  799. return this._recombineShader(root);
  800. }
  801. // Statics
  802. public static ShadersStore = {};
  803. public static IncludesShadersStore = {};
  804. }
  805. }