babylon.effect.ts 32 KB

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