babylon.effect.ts 57 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470
  1. module BABYLON {
  2. /**
  3. * EffectFallbacks can be used to add fallbacks (properties to disable) to certain properties when desired to improve performance.
  4. * (Eg. Start at high quality with reflection and fog, if fps is low, remove reflection, if still low remove fog)
  5. */
  6. export class EffectFallbacks {
  7. private _defines: { [key: string]: Array<String> } = {};
  8. private _currentRank = 32;
  9. private _maxRank = -1;
  10. private _mesh: Nullable<AbstractMesh>;
  11. /**
  12. * Removes the fallback from the bound mesh.
  13. */
  14. public unBindMesh() {
  15. this._mesh = null;
  16. }
  17. /**
  18. * Adds a fallback on the specified property.
  19. * @param rank The rank of the fallback (Lower ranks will be fallbacked to first)
  20. * @param define The name of the define in the shader
  21. */
  22. public addFallback(rank: number, define: string): void {
  23. if (!this._defines[rank]) {
  24. if (rank < this._currentRank) {
  25. this._currentRank = rank;
  26. }
  27. if (rank > this._maxRank) {
  28. this._maxRank = rank;
  29. }
  30. this._defines[rank] = new Array<String>();
  31. }
  32. this._defines[rank].push(define);
  33. }
  34. /**
  35. * Sets the mesh to use CPU skinning when needing to fallback.
  36. * @param rank The rank of the fallback (Lower ranks will be fallbacked to first)
  37. * @param mesh The mesh to use the fallbacks.
  38. */
  39. public addCPUSkinningFallback(rank: number, mesh: AbstractMesh) {
  40. this._mesh = mesh;
  41. if (rank < this._currentRank) {
  42. this._currentRank = rank;
  43. }
  44. if (rank > this._maxRank) {
  45. this._maxRank = rank;
  46. }
  47. }
  48. /**
  49. * Checks to see if more fallbacks are still availible.
  50. */
  51. public get isMoreFallbacks(): boolean {
  52. return this._currentRank <= this._maxRank;
  53. }
  54. /**
  55. * Removes the defines that shoould be removed when falling back.
  56. * @param currentDefines defines the current define statements for the shader.
  57. * @param effect defines the current effect we try to compile
  58. * @returns The resulting defines with defines of the current rank removed.
  59. */
  60. public reduce(currentDefines: string, effect: Effect): string {
  61. // First we try to switch to CPU skinning
  62. if (this._mesh && this._mesh.computeBonesUsingShaders && this._mesh.numBoneInfluencers > 0 && this._mesh.material) {
  63. this._mesh.computeBonesUsingShaders = false;
  64. currentDefines = currentDefines.replace("#define NUM_BONE_INFLUENCERS " + this._mesh.numBoneInfluencers, "#define NUM_BONE_INFLUENCERS 0");
  65. var scene = this._mesh.getScene();
  66. for (var index = 0; index < scene.meshes.length; index++) {
  67. var otherMesh = scene.meshes[index];
  68. if (!otherMesh.material) {
  69. continue;
  70. }
  71. if (!otherMesh.computeBonesUsingShaders || otherMesh.numBoneInfluencers === 0) {
  72. continue;
  73. }
  74. if (otherMesh.material.getEffect() === effect) {
  75. otherMesh.computeBonesUsingShaders = false;
  76. } else {
  77. for (var subMesh of otherMesh.subMeshes) {
  78. let subMeshEffect = subMesh.effect;
  79. if (subMeshEffect === effect) {
  80. otherMesh.computeBonesUsingShaders = false;
  81. break;
  82. }
  83. }
  84. }
  85. }
  86. }
  87. else {
  88. var currentFallbacks = this._defines[this._currentRank];
  89. if (currentFallbacks) {
  90. for (var index = 0; index < currentFallbacks.length; index++) {
  91. currentDefines = currentDefines.replace("#define " + currentFallbacks[index], "");
  92. }
  93. }
  94. this._currentRank++;
  95. }
  96. return currentDefines;
  97. }
  98. }
  99. /**
  100. * Options to be used when creating an effect.
  101. */
  102. export class EffectCreationOptions {
  103. /**
  104. * Atrributes that will be used in the shader.
  105. */
  106. public attributes: string[];
  107. /**
  108. * Uniform varible names that will be set in the shader.
  109. */
  110. public uniformsNames: string[];
  111. /**
  112. * Uniform buffer varible names that will be set in the shader.
  113. */
  114. public uniformBuffersNames: string[];
  115. /**
  116. * Sampler texture variable names that will be set in the shader.
  117. */
  118. public samplers: string[];
  119. /**
  120. * Define statements that will be set in the shader.
  121. */
  122. public defines: any;
  123. /**
  124. * Possible fallbacks for this effect to improve performance when needed.
  125. */
  126. public fallbacks: Nullable<EffectFallbacks>;
  127. /**
  128. * Callback that will be called when the shader is compiled.
  129. */
  130. public onCompiled: Nullable<(effect: Effect) => void>;
  131. /**
  132. * Callback that will be called if an error occurs during shader compilation.
  133. */
  134. public onError: Nullable<(effect: Effect, errors: string) => void>;
  135. /**
  136. * Parameters to be used with Babylons include syntax to iterate over an array (eg. {lights: 10})
  137. */
  138. public indexParameters: any;
  139. /**
  140. * Max number of lights that can be used in the shader.
  141. */
  142. public maxSimultaneousLights: number;
  143. /**
  144. * See https://developer.mozilla.org/en-US/docs/Web/API/WebGL2RenderingContext/transformFeedbackVaryings
  145. */
  146. public transformFeedbackVaryings: Nullable<string[]>;
  147. }
  148. /**
  149. * Effect containing vertex and fragment shader that can be executed on an object.
  150. */
  151. export class Effect {
  152. /**
  153. * Name of the effect.
  154. */
  155. public name: any;
  156. /**
  157. * String container all the define statements that should be set on the shader.
  158. */
  159. public defines: string;
  160. /**
  161. * Callback that will be called when the shader is compiled.
  162. */
  163. public onCompiled: Nullable<(effect: Effect) => void>;
  164. /**
  165. * Callback that will be called if an error occurs during shader compilation.
  166. */
  167. public onError: Nullable<(effect: Effect, errors: string) => void>;
  168. /**
  169. * Callback that will be called when effect is bound.
  170. */
  171. public onBind: Nullable<(effect: Effect) => void>;
  172. /**
  173. * Unique ID of the effect.
  174. */
  175. public uniqueId = 0;
  176. /**
  177. * Observable that will be called when the shader is compiled.
  178. */
  179. public onCompileObservable = new Observable<Effect>();
  180. /**
  181. * Observable that will be called if an error occurs during shader compilation.
  182. */
  183. public onErrorObservable = new Observable<Effect>();
  184. /**
  185. * Observable that will be called when effect is bound.
  186. */
  187. public onBindObservable = new Observable<Effect>();
  188. private static _uniqueIdSeed = 0;
  189. private _engine: Engine;
  190. private _uniformBuffersNames: { [key: string]: number } = {};
  191. private _uniformsNames: string[];
  192. private _samplers: string[];
  193. private _isReady = false;
  194. private _compilationError = "";
  195. private _attributesNames: string[];
  196. private _attributes: number[];
  197. private _uniforms: Nullable<WebGLUniformLocation>[];
  198. /**
  199. * Key for the effect.
  200. */
  201. public _key: string;
  202. private _indexParameters: any;
  203. private _fallbacks: Nullable<EffectFallbacks>;
  204. private _vertexSourceCode: string;
  205. private _fragmentSourceCode: string;
  206. private _vertexSourceCodeOverride: string;
  207. private _fragmentSourceCodeOverride: string;
  208. private _transformFeedbackVaryings: Nullable<string[]>;
  209. /**
  210. * Compiled shader to webGL program.
  211. */
  212. public _program: WebGLProgram;
  213. private _valueCache: { [key: string]: any };
  214. private static _baseCache: { [key: number]: WebGLBuffer } = {};
  215. /**
  216. * Instantiates an effect.
  217. * An effect can be used to create/manage/execute vertex and fragment shaders.
  218. * @param baseName Name of the effect.
  219. * @param attributesNamesOrOptions List of attribute names that will be passed to the shader or set of all options to create the effect.
  220. * @param uniformsNamesOrEngine List of uniform variable names that will be passed to the shader or the engine that will be used to render effect.
  221. * @param samplers List of sampler variables that will be passed to the shader.
  222. * @param engine Engine to be used to render the effect
  223. * @param defines Define statements to be added to the shader.
  224. * @param fallbacks Possible fallbacks for this effect to improve performance when needed.
  225. * @param onCompiled Callback that will be called when the shader is compiled.
  226. * @param onError Callback that will be called if an error occurs during shader compilation.
  227. * @param indexParameters Parameters to be used with Babylons include syntax to iterate over an array (eg. {lights: 10})
  228. */
  229. constructor(baseName: any, attributesNamesOrOptions: string[] | EffectCreationOptions, uniformsNamesOrEngine: string[] | Engine, samplers: Nullable<string[]> = null, engine?: Engine, defines: Nullable<string> = null,
  230. fallbacks: Nullable<EffectFallbacks> = null, onCompiled: Nullable<(effect: Effect) => void> = null, onError: Nullable<(effect: Effect, errors: string) => void> = null, indexParameters?: any) {
  231. this.name = baseName;
  232. if ((<EffectCreationOptions>attributesNamesOrOptions).attributes) {
  233. var options = <EffectCreationOptions>attributesNamesOrOptions;
  234. this._engine = <Engine>uniformsNamesOrEngine;
  235. this._attributesNames = options.attributes;
  236. this._uniformsNames = options.uniformsNames.concat(options.samplers);
  237. this._samplers = options.samplers;
  238. this.defines = options.defines;
  239. this.onError = options.onError;
  240. this.onCompiled = options.onCompiled;
  241. this._fallbacks = options.fallbacks;
  242. this._indexParameters = options.indexParameters;
  243. this._transformFeedbackVaryings = options.transformFeedbackVaryings;
  244. if (options.uniformBuffersNames) {
  245. for (var i = 0; i < options.uniformBuffersNames.length; i++) {
  246. this._uniformBuffersNames[options.uniformBuffersNames[i]] = i;
  247. }
  248. }
  249. } else {
  250. this._engine = <Engine>engine;
  251. this.defines = <string>defines;
  252. this._uniformsNames = (<string[]>uniformsNamesOrEngine).concat(<string[]>samplers);
  253. this._samplers = <string[]>samplers;
  254. this._attributesNames = (<string[]>attributesNamesOrOptions);
  255. this.onError = onError;
  256. this.onCompiled = onCompiled;
  257. this._indexParameters = indexParameters;
  258. this._fallbacks = fallbacks;
  259. }
  260. this.uniqueId = Effect._uniqueIdSeed++;
  261. if (this._getFromCache(baseName)) {
  262. this._prepareEffect();
  263. return;
  264. }
  265. var vertexSource: any;
  266. var fragmentSource: any;
  267. if (baseName.vertexElement) {
  268. vertexSource = document.getElementById(baseName.vertexElement);
  269. if (!vertexSource) {
  270. vertexSource = baseName.vertexElement;
  271. }
  272. } else {
  273. vertexSource = baseName.vertex || baseName;
  274. }
  275. if (baseName.fragmentElement) {
  276. fragmentSource = document.getElementById(baseName.fragmentElement);
  277. if (!fragmentSource) {
  278. fragmentSource = baseName.fragmentElement;
  279. }
  280. } else {
  281. fragmentSource = baseName.fragment || baseName;
  282. }
  283. let finalVertexCode: string;
  284. this._loadVertexShaderAsync(vertexSource)
  285. .then((vertexCode) => {
  286. return this._processIncludesAsync(vertexCode);
  287. })
  288. .then((vertexCodeWithIncludes) => {
  289. finalVertexCode = this._processShaderConversion(vertexCodeWithIncludes, false);
  290. return this._loadFragmentShaderAsync(fragmentSource);
  291. })
  292. .then((fragmentCode) => {
  293. return this._processIncludesAsync(fragmentCode);
  294. })
  295. .then((fragmentCodeWithIncludes) => {
  296. let migratedFragmentCode = this._processShaderConversion(fragmentCodeWithIncludes, true);
  297. if (baseName) {
  298. var vertex = baseName.vertexElement || baseName.vertex || baseName;
  299. var fragment = baseName.fragmentElement || baseName.fragment || baseName;
  300. this._vertexSourceCode = "#define SHADER_NAME vertex:" + vertex + "\n" + finalVertexCode;
  301. this._fragmentSourceCode = "#define SHADER_NAME fragment:" + fragment + "\n" + migratedFragmentCode;
  302. } else {
  303. this._vertexSourceCode = finalVertexCode;
  304. this._fragmentSourceCode = migratedFragmentCode;
  305. }
  306. this._setInCache(baseName);
  307. this._prepareEffect();
  308. });
  309. }
  310. private static _sourceCache: { [baseName: string]: { vertex: string, fragment: string } } = { };
  311. private _getSourceCacheKey(baseName: string): string {
  312. let cacheKey: string = baseName;
  313. if (this._indexParameters) {
  314. for (let key in this._indexParameters) {
  315. if (this._indexParameters.hasOwnProperty(key)) {
  316. cacheKey += "|";
  317. cacheKey += key
  318. cacheKey += "_";
  319. cacheKey += this._indexParameters[key];
  320. }
  321. }
  322. }
  323. return cacheKey;
  324. }
  325. private _getFromCache(baseName: string): boolean {
  326. if (typeof baseName !== "string") {
  327. return false;
  328. }
  329. let cacheKey = this._getSourceCacheKey(baseName);
  330. let sources = Effect._sourceCache[cacheKey];
  331. if (!sources) {
  332. return false;
  333. }
  334. this._vertexSourceCode = sources.vertex;
  335. this._fragmentSourceCode = sources.fragment;
  336. return true;
  337. }
  338. private _setInCache(baseName: string): void {
  339. if (typeof baseName !== "string") {
  340. return;
  341. }
  342. let cacheKey = this._getSourceCacheKey(baseName);
  343. Effect._sourceCache[cacheKey] = {
  344. vertex: this._vertexSourceCode,
  345. fragment: this._fragmentSourceCode
  346. };
  347. }
  348. /**
  349. * Unique key for this effect
  350. */
  351. public get key(): string {
  352. return this._key;
  353. }
  354. /**
  355. * If the effect has been compiled and prepared.
  356. * @returns if the effect is compiled and prepared.
  357. */
  358. public isReady(): boolean {
  359. return this._isReady;
  360. }
  361. /**
  362. * The engine the effect was initialized with.
  363. * @returns the engine.
  364. */
  365. public getEngine(): Engine {
  366. return this._engine;
  367. }
  368. /**
  369. * The compiled webGL program for the effect
  370. * @returns the webGL program.
  371. */
  372. public getProgram(): WebGLProgram {
  373. return this._program;
  374. }
  375. /**
  376. * The set of names of attribute variables for the shader.
  377. * @returns An array of attribute names.
  378. */
  379. public getAttributesNames(): string[] {
  380. return this._attributesNames;
  381. }
  382. /**
  383. * Returns the attribute at the given index.
  384. * @param index The index of the attribute.
  385. * @returns The location of the attribute.
  386. */
  387. public getAttributeLocation(index: number): number {
  388. return this._attributes[index];
  389. }
  390. /**
  391. * Returns the attribute based on the name of the variable.
  392. * @param name of the attribute to look up.
  393. * @returns the attribute location.
  394. */
  395. public getAttributeLocationByName(name: string): number {
  396. var index = this._attributesNames.indexOf(name);
  397. return this._attributes[index];
  398. }
  399. /**
  400. * The number of attributes.
  401. * @returns the numnber of attributes.
  402. */
  403. public getAttributesCount(): number {
  404. return this._attributes.length;
  405. }
  406. /**
  407. * Gets the index of a uniform variable.
  408. * @param uniformName of the uniform to look up.
  409. * @returns the index.
  410. */
  411. public getUniformIndex(uniformName: string): number {
  412. return this._uniformsNames.indexOf(uniformName);
  413. }
  414. /**
  415. * Returns the attribute based on the name of the variable.
  416. * @param uniformName of the uniform to look up.
  417. * @returns the location of the uniform.
  418. */
  419. public getUniform(uniformName: string): Nullable<WebGLUniformLocation> {
  420. return this._uniforms[this._uniformsNames.indexOf(uniformName)];
  421. }
  422. /**
  423. * Returns an array of sampler variable names
  424. * @returns The array of sampler variable neames.
  425. */
  426. public getSamplers(): string[] {
  427. return this._samplers;
  428. }
  429. /**
  430. * The error from the last compilation.
  431. * @returns the error string.
  432. */
  433. public getCompilationError(): string {
  434. return this._compilationError;
  435. }
  436. /**
  437. * Adds a callback to the onCompiled observable and call the callback imediatly if already ready.
  438. * @param func The callback to be used.
  439. */
  440. public executeWhenCompiled(func: (effect: Effect) => void): void {
  441. if (this.isReady()) {
  442. func(this);
  443. return;
  444. }
  445. this.onCompileObservable.add((effect) => {
  446. func(effect);
  447. });
  448. }
  449. /** @ignore */
  450. public _loadVertexShaderAsync(vertex: any): Promise<any> {
  451. if (Tools.IsWindowObjectExist()) {
  452. // DOM element ?
  453. if (vertex instanceof HTMLElement) {
  454. var vertexCode = Tools.GetDOMTextContent(vertex);
  455. return Promise.resolve(vertexCode);
  456. }
  457. }
  458. // Base64 encoded ?
  459. if (vertex.substr(0, 7) === "base64:") {
  460. var vertexBinary = window.atob(vertex.substr(7));
  461. return Promise.resolve(vertexBinary);
  462. }
  463. // Is in local store ?
  464. if (Effect.ShadersStore[vertex + "VertexShader"]) {
  465. return Promise.resolve(Effect.ShadersStore[vertex + "VertexShader"]);
  466. }
  467. var vertexShaderUrl;
  468. if (vertex[0] === "." || vertex[0] === "/" || vertex.indexOf("http") > -1) {
  469. vertexShaderUrl = vertex;
  470. } else {
  471. vertexShaderUrl = Engine.ShadersRepository + vertex;
  472. }
  473. // Vertex shader
  474. return this._engine._loadFileAsync(vertexShaderUrl + ".vertex.fx");
  475. }
  476. /** @ignore */
  477. public _loadFragmentShaderAsync(fragment: any): Promise<any> {
  478. if (Tools.IsWindowObjectExist()) {
  479. // DOM element ?
  480. if (fragment instanceof HTMLElement) {
  481. var fragmentCode = Tools.GetDOMTextContent(fragment);
  482. return Promise.resolve(fragmentCode);
  483. }
  484. }
  485. // Base64 encoded ?
  486. if (fragment.substr(0, 7) === "base64:") {
  487. var fragmentBinary = window.atob(fragment.substr(7));
  488. return Promise.resolve(fragmentBinary);
  489. }
  490. // Is in local store ?
  491. if (Effect.ShadersStore[fragment + "PixelShader"]) {
  492. return Promise.resolve(Effect.ShadersStore[fragment + "PixelShader"]);
  493. }
  494. if (Effect.ShadersStore[fragment + "FragmentShader"]) {
  495. return Promise.resolve(Effect.ShadersStore[fragment + "FragmentShader"]);
  496. }
  497. var fragmentShaderUrl;
  498. if (fragment[0] === "." || fragment[0] === "/" || fragment.indexOf("http") > -1) {
  499. fragmentShaderUrl = fragment;
  500. } else {
  501. fragmentShaderUrl = Engine.ShadersRepository + fragment;
  502. }
  503. // Fragment shader
  504. return this._engine._loadFileAsync(fragmentShaderUrl + ".fragment.fx");
  505. }
  506. private _dumpShadersSource(vertexCode: string, fragmentCode: string, defines: string): void {
  507. // Rebuild shaders source code
  508. var shaderVersion = (this._engine.webGLVersion > 1) ? "#version 300 es\n" : "";
  509. var prefix = shaderVersion + (defines ? defines + "\n" : "");
  510. vertexCode = prefix + vertexCode;
  511. fragmentCode = prefix + fragmentCode;
  512. // Number lines of shaders source code
  513. var i = 2;
  514. var regex = /\n/gm;
  515. var formattedVertexCode = "\n1\t" + vertexCode.replace(regex, function () { return "\n" + (i++) + "\t"; });
  516. i = 2;
  517. var formattedFragmentCode = "\n1\t" + fragmentCode.replace(regex, function () { return "\n" + (i++) + "\t"; });
  518. // Dump shaders name and formatted source code
  519. if (this.name.vertexElement) {
  520. Tools.Error("Vertex shader: " + this.name.vertexElement + formattedVertexCode);
  521. Tools.Error("Fragment shader: " + this.name.fragmentElement + formattedFragmentCode);
  522. }
  523. else if (this.name.vertex) {
  524. Tools.Error("Vertex shader: " + this.name.vertex + formattedVertexCode);
  525. Tools.Error("Fragment shader: " + this.name.fragment + formattedFragmentCode);
  526. }
  527. else {
  528. Tools.Error("Vertex shader: " + this.name + formattedVertexCode);
  529. Tools.Error("Fragment shader: " + this.name + formattedFragmentCode);
  530. }
  531. };
  532. private _processShaderConversion(sourceCode: string, isFragment: boolean): any {
  533. var preparedSourceCode = this._processPrecision(sourceCode);
  534. if (this._engine.webGLVersion == 1) {
  535. return preparedSourceCode;
  536. }
  537. // Already converted
  538. if (preparedSourceCode.indexOf("#version 3") !== -1) {
  539. return preparedSourceCode.replace("#version 300 es", "");
  540. }
  541. var hasDrawBuffersExtension = preparedSourceCode.search(/#extension.+GL_EXT_draw_buffers.+require/) !== -1;
  542. // Remove extensions
  543. // #extension GL_OES_standard_derivatives : enable
  544. // #extension GL_EXT_shader_texture_lod : enable
  545. // #extension GL_EXT_frag_depth : enable
  546. // #extension GL_EXT_draw_buffers : require
  547. var regex = /#extension.+(GL_OES_standard_derivatives|GL_EXT_shader_texture_lod|GL_EXT_frag_depth|GL_EXT_draw_buffers).+(enable|require)/g;
  548. var result = preparedSourceCode.replace(regex, "");
  549. // Migrate to GLSL v300
  550. result = result.replace(/varying(?![\n\r])\s/g, isFragment ? "in " : "out ");
  551. result = result.replace(/attribute[ \t]/g, "in ");
  552. result = result.replace(/[ \t]attribute/g, " in");
  553. if (isFragment) {
  554. result = result.replace(/texture2DLodEXT\s*\(/g, "textureLod(");
  555. result = result.replace(/textureCubeLodEXT\s*\(/g, "textureLod(");
  556. result = result.replace(/texture2D\s*\(/g, "texture(");
  557. result = result.replace(/textureCube\s*\(/g, "texture(");
  558. result = result.replace(/gl_FragDepthEXT/g, "gl_FragDepth");
  559. result = result.replace(/gl_FragColor/g, "glFragColor");
  560. result = result.replace(/gl_FragData/g, "glFragData");
  561. result = result.replace(/void\s+?main\s*\(/g, (hasDrawBuffersExtension ? "" : "out vec4 glFragColor;\n") + "void main(");
  562. }
  563. return result;
  564. }
  565. private _processIncludesAsync(sourceCode: string): Promise<any> {
  566. return new Promise((resolve, reject) => {
  567. var regex = /#include<(.+)>(\((.*)\))*(\[(.*)\])*/g;
  568. var match = regex.exec(sourceCode);
  569. var returnValue = sourceCode;
  570. while (match != null) {
  571. var includeFile = match[1];
  572. // Uniform declaration
  573. if (includeFile.indexOf("__decl__") !== -1) {
  574. includeFile = includeFile.replace(/__decl__/, "");
  575. if (this._engine.supportsUniformBuffers) {
  576. includeFile = includeFile.replace(/Vertex/, "Ubo");
  577. includeFile = includeFile.replace(/Fragment/, "Ubo");
  578. }
  579. includeFile = includeFile + "Declaration";
  580. }
  581. if (Effect.IncludesShadersStore[includeFile]) {
  582. // Substitution
  583. var includeContent = Effect.IncludesShadersStore[includeFile];
  584. if (match[2]) {
  585. var splits = match[3].split(",");
  586. for (var index = 0; index < splits.length; index += 2) {
  587. var source = new RegExp(splits[index], "g");
  588. var dest = splits[index + 1];
  589. includeContent = includeContent.replace(source, dest);
  590. }
  591. }
  592. if (match[4]) {
  593. var indexString = match[5];
  594. if (indexString.indexOf("..") !== -1) {
  595. var indexSplits = indexString.split("..");
  596. var minIndex = parseInt(indexSplits[0]);
  597. var maxIndex = parseInt(indexSplits[1]);
  598. var sourceIncludeContent = includeContent.slice(0);
  599. includeContent = "";
  600. if (isNaN(maxIndex)) {
  601. maxIndex = this._indexParameters[indexSplits[1]];
  602. }
  603. for (var i = minIndex; i < maxIndex; i++) {
  604. if (!this._engine.supportsUniformBuffers) {
  605. // Ubo replacement
  606. sourceIncludeContent = sourceIncludeContent.replace(/light\{X\}.(\w*)/g, (str: string, p1: string) => {
  607. return p1 + "{X}";
  608. });
  609. }
  610. includeContent += sourceIncludeContent.replace(/\{X\}/g, i.toString()) + "\n";
  611. }
  612. } else {
  613. if (!this._engine.supportsUniformBuffers) {
  614. // Ubo replacement
  615. includeContent = includeContent.replace(/light\{X\}.(\w*)/g, (str: string, p1: string) => {
  616. return p1 + "{X}";
  617. });
  618. }
  619. includeContent = includeContent.replace(/\{X\}/g, indexString);
  620. }
  621. }
  622. // Replace
  623. returnValue = returnValue.replace(match[0], includeContent);
  624. } else {
  625. var includeShaderUrl = Engine.ShadersRepository + "ShadersInclude/" + includeFile + ".fx";
  626. this._engine._loadFileAsync(includeShaderUrl)
  627. .then((fileContent) => {
  628. Effect.IncludesShadersStore[includeFile] = fileContent as string;
  629. return this._processIncludesAsync(returnValue);
  630. })
  631. .then((returnValue) => {
  632. resolve(returnValue);
  633. });
  634. return;
  635. }
  636. match = regex.exec(sourceCode);
  637. }
  638. resolve(returnValue);
  639. });
  640. }
  641. private _processPrecision(source: string): string {
  642. if (source.indexOf("precision highp float") === -1) {
  643. if (!this._engine.getCaps().highPrecisionShaderSupported) {
  644. source = "precision mediump float;\n" + source;
  645. } else {
  646. source = "precision highp float;\n" + source;
  647. }
  648. } else {
  649. if (!this._engine.getCaps().highPrecisionShaderSupported) { // Moving highp to mediump
  650. source = source.replace("precision highp float", "precision mediump float");
  651. }
  652. }
  653. return source;
  654. }
  655. /**
  656. * Recompiles the webGL program
  657. * @param vertexSourceCode The source code for the vertex shader.
  658. * @param fragmentSourceCode The source code for the fragment shader.
  659. * @param onCompiled Callback called when completed.
  660. * @param onError Callback called on error.
  661. */
  662. public _rebuildProgram(vertexSourceCode: string, fragmentSourceCode: string, onCompiled: (program: WebGLProgram) => void, onError: (message: string) => void) {
  663. this._isReady = false;
  664. this._vertexSourceCodeOverride = vertexSourceCode;
  665. this._fragmentSourceCodeOverride = fragmentSourceCode;
  666. this.onError = (effect, error) => {
  667. if (onError) {
  668. onError(error);
  669. }
  670. };
  671. this.onCompiled = () => {
  672. var scenes = this.getEngine().scenes;
  673. for (var i = 0; i < scenes.length; i++) {
  674. scenes[i].markAllMaterialsAsDirty(Material.TextureDirtyFlag);
  675. }
  676. if (onCompiled) {
  677. onCompiled(this._program);
  678. }
  679. };
  680. this._fallbacks = null;
  681. this._prepareEffect();
  682. }
  683. /**
  684. * Gets the uniform locations of the the specified variable names
  685. * @param names THe names of the variables to lookup.
  686. * @returns Array of locations in the same order as variable names.
  687. */
  688. public getSpecificUniformLocations(names: string[]): Nullable<WebGLUniformLocation>[] {
  689. let engine = this._engine;
  690. return engine.getUniforms(this._program, names);
  691. }
  692. /**
  693. * Prepares the effect
  694. */
  695. public _prepareEffect() {
  696. let attributesNames = this._attributesNames;
  697. let defines = this.defines;
  698. let fallbacks = this._fallbacks;
  699. this._valueCache = {};
  700. var previousProgram = this._program;
  701. try {
  702. let engine = this._engine;
  703. if (this._vertexSourceCodeOverride && this._fragmentSourceCodeOverride) {
  704. this._program = engine.createRawShaderProgram(this._vertexSourceCodeOverride, this._fragmentSourceCodeOverride, undefined, this._transformFeedbackVaryings);
  705. }
  706. else {
  707. this._program = engine.createShaderProgram(this._vertexSourceCode, this._fragmentSourceCode, defines, undefined, this._transformFeedbackVaryings);
  708. }
  709. this._program.__SPECTOR_rebuildProgram = this._rebuildProgram.bind(this);
  710. if (engine.supportsUniformBuffers) {
  711. for (var name in this._uniformBuffersNames) {
  712. this.bindUniformBlock(name, this._uniformBuffersNames[name]);
  713. }
  714. }
  715. this._uniforms = engine.getUniforms(this._program, this._uniformsNames);
  716. this._attributes = engine.getAttributes(this._program, attributesNames);
  717. var index: number;
  718. for (index = 0; index < this._samplers.length; index++) {
  719. var sampler = this.getUniform(this._samplers[index]);
  720. if (sampler == null) {
  721. this._samplers.splice(index, 1);
  722. index--;
  723. }
  724. }
  725. engine.bindSamplers(this);
  726. this._compilationError = "";
  727. this._isReady = true;
  728. if (this.onCompiled) {
  729. this.onCompiled(this);
  730. }
  731. this.onCompileObservable.notifyObservers(this);
  732. this.onCompileObservable.clear();
  733. // Unbind mesh reference in fallbacks
  734. if (this._fallbacks) {
  735. this._fallbacks.unBindMesh();
  736. }
  737. if (previousProgram) {
  738. this.getEngine()._deleteProgram(previousProgram);
  739. }
  740. } catch (e) {
  741. this._compilationError = e.message;
  742. // Let's go through fallbacks then
  743. Tools.Error("Unable to compile effect:");
  744. Tools.Error("Uniforms: " + this._uniformsNames.map(function (uniform) {
  745. return " " + uniform;
  746. }));
  747. Tools.Error("Attributes: " + attributesNames.map(function (attribute) {
  748. return " " + attribute;
  749. }));
  750. this._dumpShadersSource(this._vertexSourceCode, this._fragmentSourceCode, defines);
  751. Tools.Error("Error: " + this._compilationError);
  752. if (previousProgram) {
  753. this._program = previousProgram;
  754. this._isReady = true;
  755. if (this.onError) {
  756. this.onError(this, this._compilationError);
  757. }
  758. this.onErrorObservable.notifyObservers(this);
  759. }
  760. if (fallbacks && fallbacks.isMoreFallbacks) {
  761. Tools.Error("Trying next fallback.");
  762. this.defines = fallbacks.reduce(this.defines, this);
  763. this._prepareEffect();
  764. } else { // Sorry we did everything we can
  765. if (this.onError) {
  766. this.onError(this, this._compilationError);
  767. }
  768. this.onErrorObservable.notifyObservers(this);
  769. this.onErrorObservable.clear();
  770. // Unbind mesh reference in fallbacks
  771. if (this._fallbacks) {
  772. this._fallbacks.unBindMesh();
  773. }
  774. }
  775. }
  776. }
  777. /**
  778. * Checks if the effect is supported. (Must be called after compilation)
  779. */
  780. public get isSupported(): boolean {
  781. return this._compilationError === "";
  782. }
  783. /**
  784. * Binds a texture to the engine to be used as output of the shader.
  785. * @param channel Name of the output variable.
  786. * @param texture Texture to bind.
  787. */
  788. public _bindTexture(channel: string, texture: InternalTexture): void {
  789. this._engine._bindTexture(this._samplers.indexOf(channel), texture);
  790. }
  791. /**
  792. * Sets a texture on the engine to be used in the shader.
  793. * @param channel Name of the sampler variable.
  794. * @param texture Texture to set.
  795. */
  796. public setTexture(channel: string, texture: Nullable<BaseTexture>): void {
  797. this._engine.setTexture(this._samplers.indexOf(channel), this.getUniform(channel), texture);
  798. }
  799. /**
  800. * Sets an array of textures on the engine to be used in the shader.
  801. * @param channel Name of the variable.
  802. * @param textures Textures to set.
  803. */
  804. public setTextureArray(channel: string, textures: BaseTexture[]): void {
  805. if (this._samplers.indexOf(channel + "Ex") === -1) {
  806. var initialPos = this._samplers.indexOf(channel);
  807. for (var index = 1; index < textures.length; index++) {
  808. this._samplers.splice(initialPos + index, 0, channel + "Ex");
  809. }
  810. }
  811. this._engine.setTextureArray(this._samplers.indexOf(channel), this.getUniform(channel), textures);
  812. }
  813. /**
  814. * Sets a texture to be the input of the specified post process. (To use the output, pass in the next post process in the pipeline)
  815. * @param channel Name of the sampler variable.
  816. * @param postProcess Post process to get the input texture from.
  817. */
  818. public setTextureFromPostProcess(channel: string, postProcess: Nullable<PostProcess>): void {
  819. this._engine.setTextureFromPostProcess(this._samplers.indexOf(channel), postProcess);
  820. }
  821. /** @ignore */
  822. public _cacheMatrix(uniformName: string, matrix: Matrix): boolean {
  823. var cache = this._valueCache[uniformName];
  824. var flag = matrix.updateFlag;
  825. if (cache !== undefined && cache === flag) {
  826. return false;
  827. }
  828. this._valueCache[uniformName] = flag;
  829. return true;
  830. }
  831. /** @ignore */
  832. public _cacheFloat2(uniformName: string, x: number, y: number): boolean {
  833. var cache = this._valueCache[uniformName];
  834. if (!cache) {
  835. cache = [x, y];
  836. this._valueCache[uniformName] = cache;
  837. return true;
  838. }
  839. var changed = false;
  840. if (cache[0] !== x) {
  841. cache[0] = x;
  842. changed = true;
  843. }
  844. if (cache[1] !== y) {
  845. cache[1] = y;
  846. changed = true;
  847. }
  848. return changed;
  849. }
  850. /** @ignore */
  851. public _cacheFloat3(uniformName: string, x: number, y: number, z: number): boolean {
  852. var cache = this._valueCache[uniformName];
  853. if (!cache) {
  854. cache = [x, y, z];
  855. this._valueCache[uniformName] = cache;
  856. return true;
  857. }
  858. var changed = false;
  859. if (cache[0] !== x) {
  860. cache[0] = x;
  861. changed = true;
  862. }
  863. if (cache[1] !== y) {
  864. cache[1] = y;
  865. changed = true;
  866. }
  867. if (cache[2] !== z) {
  868. cache[2] = z;
  869. changed = true;
  870. }
  871. return changed;
  872. }
  873. /** @ignore */
  874. public _cacheFloat4(uniformName: string, x: number, y: number, z: number, w: number): boolean {
  875. var cache = this._valueCache[uniformName];
  876. if (!cache) {
  877. cache = [x, y, z, w];
  878. this._valueCache[uniformName] = cache;
  879. return true;
  880. }
  881. var changed = false;
  882. if (cache[0] !== x) {
  883. cache[0] = x;
  884. changed = true;
  885. }
  886. if (cache[1] !== y) {
  887. cache[1] = y;
  888. changed = true;
  889. }
  890. if (cache[2] !== z) {
  891. cache[2] = z;
  892. changed = true;
  893. }
  894. if (cache[3] !== w) {
  895. cache[3] = w;
  896. changed = true;
  897. }
  898. return changed;
  899. }
  900. /**
  901. * Binds a buffer to a uniform.
  902. * @param buffer Buffer to bind.
  903. * @param name Name of the uniform variable to bind to.
  904. */
  905. public bindUniformBuffer(buffer: WebGLBuffer, name: string): void {
  906. let bufferName = this._uniformBuffersNames[name];
  907. if (bufferName === undefined || Effect._baseCache[bufferName] === buffer) {
  908. return;
  909. }
  910. Effect._baseCache[bufferName] = buffer;
  911. this._engine.bindUniformBufferBase(buffer, bufferName);
  912. }
  913. /**
  914. * Binds block to a uniform.
  915. * @param blockName Name of the block to bind.
  916. * @param index Index to bind.
  917. */
  918. public bindUniformBlock(blockName: string, index: number): void {
  919. this._engine.bindUniformBlock(this._program, blockName, index);
  920. }
  921. /**
  922. * Sets an interger value on a uniform variable.
  923. * @param uniformName Name of the variable.
  924. * @param value Value to be set.
  925. * @returns this effect.
  926. */
  927. public setInt(uniformName: string, value: number): Effect {
  928. var cache = this._valueCache[uniformName];
  929. if (cache !== undefined && cache === value)
  930. return this;
  931. this._valueCache[uniformName] = value;
  932. this._engine.setInt(this.getUniform(uniformName), value);
  933. return this;
  934. }
  935. /**
  936. * Sets an int array on a uniform variable.
  937. * @param uniformName Name of the variable.
  938. * @param array array to be set.
  939. * @returns this effect.
  940. */
  941. public setIntArray(uniformName: string, array: Int32Array): Effect {
  942. this._valueCache[uniformName] = null;
  943. this._engine.setIntArray(this.getUniform(uniformName), array);
  944. return this;
  945. }
  946. /**
  947. * Sets an int array 2 on a uniform variable. (Array is specified as single array eg. [1,2,3,4] will result in [[1,2],[3,4]] in the shader)
  948. * @param uniformName Name of the variable.
  949. * @param array array to be set.
  950. * @returns this effect.
  951. */
  952. public setIntArray2(uniformName: string, array: Int32Array): Effect {
  953. this._valueCache[uniformName] = null;
  954. this._engine.setIntArray2(this.getUniform(uniformName), array);
  955. return this;
  956. }
  957. /**
  958. * Sets an int array 3 on a uniform variable. (Array is specified as single array eg. [1,2,3,4,5,6] will result in [[1,2,3],[4,5,6]] in the shader)
  959. * @param uniformName Name of the variable.
  960. * @param array array to be set.
  961. * @returns this effect.
  962. */
  963. public setIntArray3(uniformName: string, array: Int32Array): Effect {
  964. this._valueCache[uniformName] = null;
  965. this._engine.setIntArray3(this.getUniform(uniformName), array);
  966. return this;
  967. }
  968. /**
  969. * Sets an int array 4 on a uniform variable. (Array is specified as single array eg. [1,2,3,4,5,6,7,8] will result in [[1,2,3,4],[5,6,7,8]] in the shader)
  970. * @param uniformName Name of the variable.
  971. * @param array array to be set.
  972. * @returns this effect.
  973. */
  974. public setIntArray4(uniformName: string, array: Int32Array): Effect {
  975. this._valueCache[uniformName] = null;
  976. this._engine.setIntArray4(this.getUniform(uniformName), array);
  977. return this;
  978. }
  979. /**
  980. * Sets an float array on a uniform variable.
  981. * @param uniformName Name of the variable.
  982. * @param array array to be set.
  983. * @returns this effect.
  984. */
  985. public setFloatArray(uniformName: string, array: Float32Array): Effect {
  986. this._valueCache[uniformName] = null;
  987. this._engine.setFloatArray(this.getUniform(uniformName), array);
  988. return this;
  989. }
  990. /**
  991. * Sets an float array 2 on a uniform variable. (Array is specified as single array eg. [1,2,3,4] will result in [[1,2],[3,4]] in the shader)
  992. * @param uniformName Name of the variable.
  993. * @param array array to be set.
  994. * @returns this effect.
  995. */
  996. public setFloatArray2(uniformName: string, array: Float32Array): Effect {
  997. this._valueCache[uniformName] = null;
  998. this._engine.setFloatArray2(this.getUniform(uniformName), array);
  999. return this;
  1000. }
  1001. /**
  1002. * Sets an float array 3 on a uniform variable. (Array is specified as single array eg. [1,2,3,4,5,6] will result in [[1,2,3],[4,5,6]] in the shader)
  1003. * @param uniformName Name of the variable.
  1004. * @param array array to be set.
  1005. * @returns this effect.
  1006. */
  1007. public setFloatArray3(uniformName: string, array: Float32Array): Effect {
  1008. this._valueCache[uniformName] = null;
  1009. this._engine.setFloatArray3(this.getUniform(uniformName), array);
  1010. return this;
  1011. }
  1012. /**
  1013. * Sets an float array 4 on a uniform variable. (Array is specified as single array eg. [1,2,3,4,5,6,7,8] will result in [[1,2,3,4],[5,6,7,8]] in the shader)
  1014. * @param uniformName Name of the variable.
  1015. * @param array array to be set.
  1016. * @returns this effect.
  1017. */
  1018. public setFloatArray4(uniformName: string, array: Float32Array): Effect {
  1019. this._valueCache[uniformName] = null;
  1020. this._engine.setFloatArray4(this.getUniform(uniformName), array);
  1021. return this;
  1022. }
  1023. /**
  1024. * Sets an array on a uniform variable.
  1025. * @param uniformName Name of the variable.
  1026. * @param array array to be set.
  1027. * @returns this effect.
  1028. */
  1029. public setArray(uniformName: string, array: number[]): Effect {
  1030. this._valueCache[uniformName] = null;
  1031. this._engine.setArray(this.getUniform(uniformName), array);
  1032. return this;
  1033. }
  1034. /**
  1035. * Sets an array 2 on a uniform variable. (Array is specified as single array eg. [1,2,3,4] will result in [[1,2],[3,4]] in the shader)
  1036. * @param uniformName Name of the variable.
  1037. * @param array array to be set.
  1038. * @returns this effect.
  1039. */
  1040. public setArray2(uniformName: string, array: number[]): Effect {
  1041. this._valueCache[uniformName] = null;
  1042. this._engine.setArray2(this.getUniform(uniformName), array);
  1043. return this;
  1044. }
  1045. /**
  1046. * Sets an array 3 on a uniform variable. (Array is specified as single array eg. [1,2,3,4,5,6] will result in [[1,2,3],[4,5,6]] in the shader)
  1047. * @param uniformName Name of the variable.
  1048. * @param array array to be set.
  1049. * @returns this effect.
  1050. */
  1051. public setArray3(uniformName: string, array: number[]): Effect {
  1052. this._valueCache[uniformName] = null;
  1053. this._engine.setArray3(this.getUniform(uniformName), array);
  1054. return this;
  1055. }
  1056. /**
  1057. * Sets an array 4 on a uniform variable. (Array is specified as single array eg. [1,2,3,4,5,6,7,8] will result in [[1,2,3,4],[5,6,7,8]] in the shader)
  1058. * @param uniformName Name of the variable.
  1059. * @param array array to be set.
  1060. * @returns this effect.
  1061. */
  1062. public setArray4(uniformName: string, array: number[]): Effect {
  1063. this._valueCache[uniformName] = null;
  1064. this._engine.setArray4(this.getUniform(uniformName), array);
  1065. return this;
  1066. }
  1067. /**
  1068. * Sets matrices on a uniform variable.
  1069. * @param uniformName Name of the variable.
  1070. * @param matrices matrices to be set.
  1071. * @returns this effect.
  1072. */
  1073. public setMatrices(uniformName: string, matrices: Float32Array): Effect {
  1074. if (!matrices) {
  1075. return this;
  1076. }
  1077. this._valueCache[uniformName] = null;
  1078. this._engine.setMatrices(this.getUniform(uniformName), matrices);
  1079. return this;
  1080. }
  1081. /**
  1082. * Sets matrix on a uniform variable.
  1083. * @param uniformName Name of the variable.
  1084. * @param matrix matrix to be set.
  1085. * @returns this effect.
  1086. */
  1087. public setMatrix(uniformName: string, matrix: Matrix): Effect {
  1088. if (this._cacheMatrix(uniformName, matrix)) {
  1089. this._engine.setMatrix(this.getUniform(uniformName), matrix);
  1090. }
  1091. return this;
  1092. }
  1093. /**
  1094. * Sets a 3x3 matrix on a uniform variable. (Speicified as [1,2,3,4,5,6,7,8,9] will result in [1,2,3][4,5,6][7,8,9] matrix)
  1095. * @param uniformName Name of the variable.
  1096. * @param matrix matrix to be set.
  1097. * @returns this effect.
  1098. */
  1099. public setMatrix3x3(uniformName: string, matrix: Float32Array): Effect {
  1100. this._valueCache[uniformName] = null;
  1101. this._engine.setMatrix3x3(this.getUniform(uniformName), matrix);
  1102. return this;
  1103. }
  1104. /**
  1105. * Sets a 2x2 matrix on a uniform variable. (Speicified as [1,2,3,4] will result in [1,2][3,4] matrix)
  1106. * @param uniformName Name of the variable.
  1107. * @param matrix matrix to be set.
  1108. * @returns this effect.
  1109. */
  1110. public setMatrix2x2(uniformName: string, matrix: Float32Array): Effect {
  1111. this._valueCache[uniformName] = null;
  1112. this._engine.setMatrix2x2(this.getUniform(uniformName), matrix);
  1113. return this;
  1114. }
  1115. /**
  1116. * Sets a float on a uniform variable.
  1117. * @param uniformName Name of the variable.
  1118. * @param value value to be set.
  1119. * @returns this effect.
  1120. */
  1121. public setFloat(uniformName: string, value: number): Effect {
  1122. var cache = this._valueCache[uniformName];
  1123. if (cache !== undefined && cache === value)
  1124. return this;
  1125. this._valueCache[uniformName] = value;
  1126. this._engine.setFloat(this.getUniform(uniformName), value);
  1127. return this;
  1128. }
  1129. /**
  1130. * Sets a boolean on a uniform variable.
  1131. * @param uniformName Name of the variable.
  1132. * @param bool value to be set.
  1133. * @returns this effect.
  1134. */
  1135. public setBool(uniformName: string, bool: boolean): Effect {
  1136. var cache = this._valueCache[uniformName];
  1137. if (cache !== undefined && cache === bool)
  1138. return this;
  1139. this._valueCache[uniformName] = bool;
  1140. this._engine.setBool(this.getUniform(uniformName), bool ? 1 : 0);
  1141. return this;
  1142. }
  1143. /**
  1144. * Sets a Vector2 on a uniform variable.
  1145. * @param uniformName Name of the variable.
  1146. * @param vector2 vector2 to be set.
  1147. * @returns this effect.
  1148. */
  1149. public setVector2(uniformName: string, vector2: Vector2): Effect {
  1150. if (this._cacheFloat2(uniformName, vector2.x, vector2.y)) {
  1151. this._engine.setFloat2(this.getUniform(uniformName), vector2.x, vector2.y);
  1152. }
  1153. return this;
  1154. }
  1155. /**
  1156. * Sets a float2 on a uniform variable.
  1157. * @param uniformName Name of the variable.
  1158. * @param x First float in float2.
  1159. * @param y Second float in float2.
  1160. * @returns this effect.
  1161. */
  1162. public setFloat2(uniformName: string, x: number, y: number): Effect {
  1163. if (this._cacheFloat2(uniformName, x, y)) {
  1164. this._engine.setFloat2(this.getUniform(uniformName), x, y);
  1165. }
  1166. return this;
  1167. }
  1168. /**
  1169. * Sets a Vector3 on a uniform variable.
  1170. * @param uniformName Name of the variable.
  1171. * @param vector3 Value to be set.
  1172. * @returns this effect.
  1173. */
  1174. public setVector3(uniformName: string, vector3: Vector3): Effect {
  1175. if (this._cacheFloat3(uniformName, vector3.x, vector3.y, vector3.z)) {
  1176. this._engine.setFloat3(this.getUniform(uniformName), vector3.x, vector3.y, vector3.z);
  1177. }
  1178. return this;
  1179. }
  1180. /**
  1181. * Sets a float3 on a uniform variable.
  1182. * @param uniformName Name of the variable.
  1183. * @param x First float in float3.
  1184. * @param y Second float in float3.
  1185. * @param z Third float in float3.
  1186. * @returns this effect.
  1187. */
  1188. public setFloat3(uniformName: string, x: number, y: number, z: number): Effect {
  1189. if (this._cacheFloat3(uniformName, x, y, z)) {
  1190. this._engine.setFloat3(this.getUniform(uniformName), x, y, z);
  1191. }
  1192. return this;
  1193. }
  1194. /**
  1195. * Sets a Vector4 on a uniform variable.
  1196. * @param uniformName Name of the variable.
  1197. * @param vector4 Value to be set.
  1198. * @returns this effect.
  1199. */
  1200. public setVector4(uniformName: string, vector4: Vector4): Effect {
  1201. if (this._cacheFloat4(uniformName, vector4.x, vector4.y, vector4.z, vector4.w)) {
  1202. this._engine.setFloat4(this.getUniform(uniformName), vector4.x, vector4.y, vector4.z, vector4.w);
  1203. }
  1204. return this;
  1205. }
  1206. /**
  1207. * Sets a float4 on a uniform variable.
  1208. * @param uniformName Name of the variable.
  1209. * @param x First float in float4.
  1210. * @param y Second float in float4.
  1211. * @param z Third float in float4.
  1212. * @param w Fourth float in float4.
  1213. * @returns this effect.
  1214. */
  1215. public setFloat4(uniformName: string, x: number, y: number, z: number, w: number): Effect {
  1216. if (this._cacheFloat4(uniformName, x, y, z, w)) {
  1217. this._engine.setFloat4(this.getUniform(uniformName), x, y, z, w);
  1218. }
  1219. return this;
  1220. }
  1221. /**
  1222. * Sets a Color3 on a uniform variable.
  1223. * @param uniformName Name of the variable.
  1224. * @param color3 Value to be set.
  1225. * @returns this effect.
  1226. */
  1227. public setColor3(uniformName: string, color3: Color3): Effect {
  1228. if (this._cacheFloat3(uniformName, color3.r, color3.g, color3.b)) {
  1229. this._engine.setColor3(this.getUniform(uniformName), color3);
  1230. }
  1231. return this;
  1232. }
  1233. /**
  1234. * Sets a Color4 on a uniform variable.
  1235. * @param uniformName Name of the variable.
  1236. * @param color3 Value to be set.
  1237. * @param alpha Alpha value to be set.
  1238. * @returns this effect.
  1239. */
  1240. public setColor4(uniformName: string, color3: Color3, alpha: number): Effect {
  1241. if (this._cacheFloat4(uniformName, color3.r, color3.g, color3.b, alpha)) {
  1242. this._engine.setColor4(this.getUniform(uniformName), color3, alpha);
  1243. }
  1244. return this;
  1245. }
  1246. /**
  1247. * Sets a Color4 on a uniform variable
  1248. * @param uniformName defines the name of the variable
  1249. * @param color4 defines the value to be set
  1250. * @returns this effect.
  1251. */
  1252. public setDirectColor4(uniformName: string, color4: Color4): Effect {
  1253. if (this._cacheFloat4(uniformName, color4.r, color4.g, color4.b, color4.a)) {
  1254. this._engine.setDirectColor4(this.getUniform(uniformName), color4);
  1255. }
  1256. return this;
  1257. }
  1258. // Statics
  1259. /**
  1260. * Store of each shader (The can be looked up using effect.key)
  1261. */
  1262. public static ShadersStore: { [key: string]: string } = {};
  1263. /**
  1264. * Store of each included file for a shader (The can be looked up using effect.key)
  1265. */
  1266. public static IncludesShadersStore: { [key: string]: string } = {};
  1267. /**
  1268. * Resets the cache of effects.
  1269. */
  1270. public static ResetCache() {
  1271. Effect._baseCache = {};
  1272. }
  1273. }
  1274. }