effect.ts 56 KB

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