babylon.effect.ts 32 KB

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