123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366 |
- import { ShaderCodeNode } from './shaderCodeNode';
- import { ShaderCodeCursor } from './shaderCodeCursor';
- import { ShaderCodeConditionNode } from './shaderCodeConditionNode';
- import { ShaderCodeTestNode } from './shaderCodeTestNode';
- import { ShaderDefineIsDefinedOperator } from './Expressions/Operators/shaderDefineIsDefinedOperator';
- import { ShaderDefineOrOperator } from './Expressions/Operators/shaderDefineOrOperator';
- import { ShaderDefineAndOperator } from './Expressions/Operators/shaderDefineAndOperator';
- import { ShaderDefineExpression } from './Expressions/shaderDefineExpression';
- import { ShaderDefineArithmeticOperator } from './Expressions/Operators/shaderDefineArithmeticOperator';
- import { ProcessingOptions } from './shaderProcessingOptions';
- import { _DevTools } from '../../Misc/devTools';
- declare type WebRequest = import("../../Misc/webRequest").WebRequest;
- declare type LoadFileError = import("../../Misc/FileTools").LoadFileError;
- declare type IOfflineProvider = import("../../Offline/IOfflineProvider").IOfflineProvider;
- declare type IFileRequest = import("../../Misc/fileRequest").IFileRequest;
- /** @hidden */
- export class ShaderProcessor {
- public static Process(sourceCode: string, options: ProcessingOptions, callback: (migratedCode: string) => void) {
- this._ProcessIncludes(sourceCode, options, (codeWithIncludes) => {
- let migratedCode = this._ProcessShaderConversion(codeWithIncludes, options);
- callback(migratedCode);
- });
- }
- private static _ProcessPrecision(source: string, options: ProcessingOptions): string {
- const shouldUseHighPrecisionShader = options.shouldUseHighPrecisionShader;
- if (source.indexOf("precision highp float") === -1) {
- if (!shouldUseHighPrecisionShader) {
- source = "precision mediump float;\n" + source;
- } else {
- source = "precision highp float;\n" + source;
- }
- } else {
- if (!shouldUseHighPrecisionShader) { // Moving highp to mediump
- source = source.replace("precision highp float", "precision mediump float");
- }
- }
- return source;
- }
- private static _ExtractOperation(expression: string) {
- let regex = /defined\((.+)\)/;
- let match = regex.exec(expression);
- if (match && match.length) {
- return new ShaderDefineIsDefinedOperator(match[1].trim(), expression[0] === "!");
- }
- let operators = ["==", ">=", "<=", "<", ">"];
- let operator = "";
- let indexOperator = 0;
- for (operator of operators) {
- indexOperator = expression.indexOf(operator);
- if (indexOperator > -1) {
- break;
- }
- }
- if (indexOperator === -1) {
- return new ShaderDefineIsDefinedOperator(expression);
- }
- let define = expression.substring(0, indexOperator).trim();
- let value = expression.substring(indexOperator + operator.length).trim();
- return new ShaderDefineArithmeticOperator(define, operator, value);
- }
- private static _BuildSubExpression(expression: string): ShaderDefineExpression {
- let indexOr = expression.indexOf("||");
- if (indexOr === -1) {
- let indexAnd = expression.indexOf("&&");
- if (indexAnd > -1) {
- let andOperator = new ShaderDefineAndOperator();
- let leftPart = expression.substring(0, indexAnd).trim();
- let rightPart = expression.substring(indexAnd + 2).trim();
- andOperator.leftOperand = this._BuildSubExpression(leftPart);
- andOperator.rightOperand = this._BuildSubExpression(rightPart);
- return andOperator;
- } else {
- return this._ExtractOperation(expression);
- }
- } else {
- let orOperator = new ShaderDefineOrOperator();
- let leftPart = expression.substring(0, indexOr).trim();
- let rightPart = expression.substring(indexOr + 2).trim();
- orOperator.leftOperand = this._BuildSubExpression(leftPart);
- orOperator.rightOperand = this._BuildSubExpression(rightPart);
- return orOperator;
- }
- }
- private static _BuildExpression(line: string, start: number): ShaderCodeTestNode {
- let node = new ShaderCodeTestNode();
- let command = line.substring(0, start);
- let expression = line.substring(start).trim();
- if (command === "#ifdef") {
- node.testExpression = new ShaderDefineIsDefinedOperator(expression);
- } else if (command === "#ifndef") {
- node.testExpression = new ShaderDefineIsDefinedOperator(expression, true);
- } else {
- node.testExpression = this._BuildSubExpression(expression);
- }
- return node;
- }
- private static _MoveCursorWithinIf(cursor: ShaderCodeCursor, rootNode: ShaderCodeConditionNode, ifNode: ShaderCodeNode) {
- let line = cursor.currentLine;
- while (this._MoveCursor(cursor, ifNode)) {
- line = cursor.currentLine;
- let first5 = line.substring(0, 5).toLowerCase();
- if (first5 === "#else") {
- let elseNode = new ShaderCodeNode();
- rootNode.children.push(elseNode);
- this._MoveCursor(cursor, elseNode);
- return;
- } else if (first5 === "#elif") {
- let elifNode = this._BuildExpression(line, 5);
- rootNode.children.push(elifNode);
- ifNode = elifNode;
- }
- }
- }
- private static _MoveCursor(cursor: ShaderCodeCursor, rootNode: ShaderCodeNode): boolean {
- while (cursor.canRead) {
- cursor.lineIndex++;
- let line = cursor.currentLine;
- const keywords = /(#ifdef)|(#else)|(#elif)|(#endif)|(#ifndef)|(#if)/;
- const matches = keywords.exec(line);
- if (matches && matches.length) {
- let keyword = matches[0];
- switch (keyword) {
- case "#ifdef": {
- let newRootNode = new ShaderCodeConditionNode();
- rootNode.children.push(newRootNode);
- let ifNode = this._BuildExpression(line, 6);
- newRootNode.children.push(ifNode);
- this._MoveCursorWithinIf(cursor, newRootNode, ifNode);
- break;
- }
- case "#else":
- case "#elif":
- return true;
- case "#endif":
- return false;
- case "#ifndef": {
- let newRootNode = new ShaderCodeConditionNode();
- rootNode.children.push(newRootNode);
- let ifNode = this._BuildExpression(line, 7);
- newRootNode.children.push(ifNode);
- this._MoveCursorWithinIf(cursor, newRootNode, ifNode);
- break;
- }
- case "#if": {
- let newRootNode = new ShaderCodeConditionNode();
- let ifNode = this._BuildExpression(line, 3);
- rootNode.children.push(newRootNode);
- newRootNode.children.push(ifNode);
- this._MoveCursorWithinIf(cursor, newRootNode, ifNode);
- break;
- }
- }
- }
- else {
- let newNode = new ShaderCodeNode();
- newNode.line = line;
- rootNode.children.push(newNode);
- // Detect additional defines
- if (line[0] === "#" && line[1] === "d") {
- let split = line.replace(";", "").split(" ");
- newNode.additionalDefineKey = split[1];
- if (split.length === 3) {
- newNode.additionalDefineValue = split[2];
- }
- }
- }
- }
- return false;
- }
- private static _EvaluatePreProcessors(sourceCode: string, preprocessors: { [key: string]: string }, options: ProcessingOptions): string {
- const rootNode = new ShaderCodeNode();
- let cursor = new ShaderCodeCursor();
- cursor.lineIndex = -1;
- cursor.lines = sourceCode.split("\n");
- // Decompose (We keep it in 2 steps so it is easier to maintain and perf hit is insignificant)
- this._MoveCursor(cursor, rootNode);
- // Recompose
- return rootNode.process(preprocessors, options);
- }
- private static _PreparePreProcessors(options: ProcessingOptions): { [key: string]: string } {
- let defines = options.defines;
- let preprocessors: { [key: string]: string } = {};
- for (var define of defines) {
- let keyValue = define.replace("#define", "").replace(";", "").trim();
- let split = keyValue.split(" ");
- preprocessors[split[0]] = split.length > 1 ? split[1] : "";
- }
- preprocessors["GL_ES"] = "true";
- preprocessors["__VERSION__"] = options.version;
- preprocessors[options.platformName] = "true";
- return preprocessors;
- }
- private static _ProcessShaderConversion(sourceCode: string, options: ProcessingOptions): string {
- var preparedSourceCode = this._ProcessPrecision(sourceCode, options);
- if (!options.processor) {
- return preparedSourceCode;
- }
- // Already converted
- if (preparedSourceCode.indexOf("#version 3") !== -1) {
- return preparedSourceCode.replace("#version 300 es", "");
- }
- let defines = options.defines;
- let preprocessors = this._PreparePreProcessors(options);
- // General pre processing
- if (options.processor.preProcessor) {
- preparedSourceCode = options.processor.preProcessor(preparedSourceCode, defines, options.isFragment);
- }
- preparedSourceCode = this._EvaluatePreProcessors(preparedSourceCode, preprocessors, options);
- // Post processing
- if (options.processor.postProcessor) {
- preparedSourceCode = options.processor.postProcessor(preparedSourceCode, defines, options.isFragment);
- }
- return preparedSourceCode;
- }
- private static _ProcessIncludes(sourceCode: string, options: ProcessingOptions, callback: (data: any) => void): void {
- var regex = /#include<(.+)>(\((.*)\))*(\[(.*)\])*/g;
- var match = regex.exec(sourceCode);
- var returnValue = new String(sourceCode);
- while (match != null) {
- var includeFile = match[1];
- // Uniform declaration
- if (includeFile.indexOf("__decl__") !== -1) {
- includeFile = includeFile.replace(/__decl__/, "");
- if (options.supportsUniformBuffers) {
- includeFile = includeFile.replace(/Vertex/, "Ubo");
- includeFile = includeFile.replace(/Fragment/, "Ubo");
- }
- includeFile = includeFile + "Declaration";
- }
- if (options.includesShadersStore[includeFile]) {
- // Substitution
- var includeContent = options.includesShadersStore[includeFile];
- if (match[2]) {
- var splits = match[3].split(",");
- for (var index = 0; index < splits.length; index += 2) {
- var source = new RegExp(splits[index], "g");
- var dest = splits[index + 1];
- includeContent = includeContent.replace(source, dest);
- }
- }
- if (match[4]) {
- var indexString = match[5];
- if (indexString.indexOf("..") !== -1) {
- var indexSplits = indexString.split("..");
- var minIndex = parseInt(indexSplits[0]);
- var maxIndex = parseInt(indexSplits[1]);
- var sourceIncludeContent = includeContent.slice(0);
- includeContent = "";
- if (isNaN(maxIndex)) {
- maxIndex = options.indexParameters[indexSplits[1]];
- }
- for (var i = minIndex; i < maxIndex; i++) {
- if (!options.supportsUniformBuffers) {
- // Ubo replacement
- sourceIncludeContent = sourceIncludeContent.replace(/light\{X\}.(\w*)/g, (str: string, p1: string) => {
- return p1 + "{X}";
- });
- }
- includeContent += sourceIncludeContent.replace(/\{X\}/g, i.toString()) + "\n";
- }
- } else {
- if (!options.supportsUniformBuffers) {
- // Ubo replacement
- includeContent = includeContent.replace(/light\{X\}.(\w*)/g, (str: string, p1: string) => {
- return p1 + "{X}";
- });
- }
- includeContent = includeContent.replace(/\{X\}/g, indexString);
- }
- }
- // Replace
- returnValue = returnValue.replace(match[0], includeContent);
- } else {
- var includeShaderUrl = options.shadersRepository + "ShadersInclude/" + includeFile + ".fx";
- ShaderProcessor._FileToolsLoadFile(includeShaderUrl, (fileContent) => {
- options.includesShadersStore[includeFile] = fileContent as string;
- this._ProcessIncludes(<string>returnValue, options, callback);
- });
- return;
- }
- match = regex.exec(sourceCode);
- }
- callback(returnValue);
- }
- /**
- * Loads a file from a url
- * @param url url to load
- * @param onSuccess callback called when the file successfully loads
- * @param onProgress callback called while file is loading (if the server supports this mode)
- * @param offlineProvider defines the offline provider for caching
- * @param useArrayBuffer defines a boolean indicating that date must be returned as ArrayBuffer
- * @param onError callback called when the file fails to load
- * @returns a file request object
- * @hidden
- */
- public static _FileToolsLoadFile(url: string, onSuccess: (data: string | ArrayBuffer, responseURL?: string) => void, onProgress?: (ev: ProgressEvent) => void, offlineProvider?: IOfflineProvider, useArrayBuffer?: boolean, onError?: (request?: WebRequest, exception?: LoadFileError) => void): IFileRequest {
- throw _DevTools.WarnImport("FileTools");
- }
- }
|