@babylonjs/smart-filters 0.5.0-alpha → 0.6.1-alpha
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/blocks/baseBlock.d.ts +6 -0
- package/dist/blocks/baseBlock.d.ts.map +1 -1
- package/dist/blocks/baseBlock.js +8 -0
- package/dist/blocks/baseBlock.js.map +1 -1
- package/dist/blocks/customShaderBlock.d.ts +55 -0
- package/dist/blocks/customShaderBlock.d.ts.map +1 -0
- package/dist/blocks/customShaderBlock.js +139 -0
- package/dist/blocks/customShaderBlock.js.map +1 -0
- package/dist/blocks/inputBlock.deserializer.d.ts +1 -1
- package/dist/blocks/inputBlock.deserializer.d.ts.map +1 -1
- package/dist/blocks/inputBlock.serializer.d.ts +1 -1
- package/dist/blocks/inputBlock.serializer.d.ts.map +1 -1
- package/dist/blocks/inputBlock.serializer.js +3 -3
- package/dist/blocks/inputBlock.serializer.js.map +1 -1
- package/dist/blocks/shaderBlock.js +2 -2
- package/dist/blocks/shaderBlock.js.map +1 -1
- package/dist/command/command.d.ts +2 -3
- package/dist/command/command.d.ts.map +1 -1
- package/dist/command/command.js.map +1 -1
- package/dist/command/commandBufferDebugger.js +1 -1
- package/dist/command/commandBufferDebugger.js.map +1 -1
- package/dist/connection/connectionPointType.d.ts +4 -0
- package/dist/connection/connectionPointType.d.ts.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/optimization/smartFilterOptimizer.js +3 -3
- package/dist/optimization/smartFilterOptimizer.js.map +1 -1
- package/dist/serialization/importCustomShaderBlockDefinition.d.ts +11 -0
- package/dist/serialization/importCustomShaderBlockDefinition.d.ts.map +1 -0
- package/dist/serialization/importCustomShaderBlockDefinition.js +80 -0
- package/dist/serialization/importCustomShaderBlockDefinition.js.map +1 -0
- package/dist/serialization/index.d.ts +1 -0
- package/dist/serialization/index.d.ts.map +1 -1
- package/dist/serialization/index.js +1 -0
- package/dist/serialization/index.js.map +1 -1
- package/dist/serialization/serializedBlockDefinition.d.ts +7 -0
- package/dist/serialization/serializedBlockDefinition.d.ts.map +1 -0
- package/dist/serialization/serializedBlockDefinition.js +2 -0
- package/dist/serialization/serializedBlockDefinition.js.map +1 -0
- package/dist/serialization/serializedSmartFilter.d.ts +1 -1
- package/dist/serialization/serializedSmartFilter.d.ts.map +1 -1
- package/dist/serialization/smartFilterDeserializer.d.ts +12 -4
- package/dist/serialization/smartFilterDeserializer.d.ts.map +1 -1
- package/dist/serialization/smartFilterDeserializer.js +63 -34
- package/dist/serialization/smartFilterDeserializer.js.map +1 -1
- package/dist/serialization/smartFilterSerializer.d.ts +2 -2
- package/dist/serialization/smartFilterSerializer.d.ts.map +1 -1
- package/dist/serialization/smartFilterSerializer.js +9 -6
- package/dist/serialization/smartFilterSerializer.js.map +1 -1
- package/dist/serialization/v1/blockSerialization.types.d.ts +55 -0
- package/dist/serialization/v1/blockSerialization.types.d.ts.map +1 -0
- package/dist/serialization/v1/blockSerialization.types.js +7 -0
- package/dist/serialization/v1/blockSerialization.types.js.map +1 -0
- package/dist/serialization/v1/defaultBlockSerializer.d.ts +1 -1
- package/dist/serialization/v1/defaultBlockSerializer.d.ts.map +1 -1
- package/dist/serialization/v1/defaultBlockSerializer.js +1 -1
- package/dist/serialization/v1/defaultBlockSerializer.js.map +1 -1
- package/dist/serialization/v1/index.d.ts +1 -1
- package/dist/serialization/v1/index.d.ts.map +1 -1
- package/dist/serialization/v1/index.js +1 -1
- package/dist/serialization/v1/index.js.map +1 -1
- package/dist/serialization/v1/{serialization.types.d.ts → smartFilterSerialization.types.d.ts} +12 -11
- package/dist/serialization/v1/smartFilterSerialization.types.d.ts.map +1 -0
- package/dist/serialization/v1/smartFilterSerialization.types.js +2 -0
- package/dist/serialization/v1/smartFilterSerialization.types.js.map +1 -0
- package/dist/utils/buildTools/buildShaders.js +1 -1
- package/dist/utils/buildTools/buildShaders.js.map +1 -1
- package/dist/utils/buildTools/convertShaderForHardcodedBlock.d.ts +13 -0
- package/dist/utils/buildTools/convertShaderForHardcodedBlock.d.ts.map +1 -0
- package/dist/utils/buildTools/convertShaderForHardcodedBlock.js +116 -0
- package/dist/utils/buildTools/convertShaderForHardcodedBlock.js.map +1 -0
- package/dist/utils/buildTools/shaderCode.types.d.ts +43 -0
- package/dist/utils/buildTools/shaderCode.types.d.ts.map +1 -0
- package/dist/utils/buildTools/shaderCode.types.js +2 -0
- package/dist/utils/buildTools/shaderCode.types.js.map +1 -0
- package/dist/utils/buildTools/shaderConverter.d.ts +56 -8
- package/dist/utils/buildTools/shaderConverter.d.ts.map +1 -1
- package/dist/utils/buildTools/shaderConverter.js +87 -137
- package/dist/utils/buildTools/shaderConverter.js.map +1 -1
- package/dist/utils/buildTools/watchShaders.js +2 -2
- package/dist/utils/buildTools/watchShaders.js.map +1 -1
- package/dist/utils/renderTargetUtils.js +3 -3
- package/dist/utils/renderTargetUtils.js.map +1 -1
- package/dist/utils/shaderCodeUtils.d.ts +1 -42
- package/dist/utils/shaderCodeUtils.d.ts.map +1 -1
- package/dist/utils/shaderCodeUtils.js.map +1 -1
- package/package.json +1 -1
- package/readme.md +19 -15
- package/src/blocks/baseBlock.ts +9 -0
- package/src/blocks/customShaderBlock.ts +217 -0
- package/src/blocks/inputBlock.deserializer.ts +1 -1
- package/src/blocks/inputBlock.serializer.ts +4 -4
- package/src/blocks/shaderBlock.ts +2 -2
- package/src/command/command.ts +2 -3
- package/src/command/commandBufferDebugger.ts +1 -1
- package/src/connection/connectionPointType.ts +11 -0
- package/src/index.ts +2 -0
- package/src/optimization/smartFilterOptimizer.ts +3 -3
- package/src/serialization/importCustomShaderBlockDefinition.ts +85 -0
- package/src/serialization/index.ts +1 -0
- package/src/serialization/serializedBlockDefinition.ts +7 -0
- package/src/serialization/serializedSmartFilter.ts +1 -1
- package/src/serialization/smartFilterDeserializer.ts +106 -52
- package/src/serialization/smartFilterSerializer.ts +11 -7
- package/src/serialization/v1/blockSerialization.types.ts +63 -0
- package/src/serialization/v1/defaultBlockSerializer.ts +2 -2
- package/src/serialization/v1/index.ts +1 -1
- package/src/serialization/v1/{serialization.types.ts → smartFilterSerialization.types.ts} +11 -10
- package/src/utils/buildTools/buildShaders.ts +1 -1
- package/src/utils/buildTools/convertShaderForHardcodedBlock.ts +149 -0
- package/src/utils/buildTools/shaderCode.types.ts +49 -0
- package/src/utils/buildTools/shaderConverter.ts +158 -178
- package/src/utils/buildTools/watchShaders.ts +2 -2
- package/src/utils/renderTargetUtils.ts +3 -3
- package/src/utils/shaderCodeUtils.ts +1 -50
- package/dist/serialization/v1/serialization.types.d.ts.map +0 -1
- package/dist/serialization/v1/serialization.types.js +0 -2
- package/dist/serialization/v1/serialization.types.js.map +0 -1
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import * as fs from "fs";
|
|
2
|
+
import * as path from "path";
|
|
3
|
+
import { parseFragmentShader } from "./shaderConverter.js";
|
|
4
|
+
|
|
5
|
+
const TYPE_IMPORT_PATH = "@TYPE_IMPORT_PATH@";
|
|
6
|
+
const VERTEX_SHADER = "@VERTEX_SHADER@";
|
|
7
|
+
const UNIFORMS = "@UNIFORMS@";
|
|
8
|
+
const CONSTS_VALUE = "@CONSTS@";
|
|
9
|
+
const CONSTS_PROPERTY = "@CONSTS_PROPERTY@";
|
|
10
|
+
const MAIN_INPUT_NAME = "@MAIN_INPUT_NAME@";
|
|
11
|
+
const MAIN_FUNCTION_NAME = "@MAIN_FUNCTION_NAME@";
|
|
12
|
+
const FUNCTIONS = "@FUNCTIONS@";
|
|
13
|
+
const FUNCTION_NAME = "@FUNCTION_NAME@";
|
|
14
|
+
const FUNCTION_CODE = "@FUNCTION_CODE@";
|
|
15
|
+
const UNIFORM_NAMES = "@UNIFORM_NAMES@";
|
|
16
|
+
|
|
17
|
+
const ConstsTemplate = `
|
|
18
|
+
const: \`${CONSTS_VALUE}\`,`;
|
|
19
|
+
|
|
20
|
+
const FunctionTemplate = `
|
|
21
|
+
{
|
|
22
|
+
name: "${FUNCTION_NAME}",
|
|
23
|
+
code: \`
|
|
24
|
+
${FUNCTION_CODE}
|
|
25
|
+
\`,
|
|
26
|
+
},`;
|
|
27
|
+
|
|
28
|
+
const CodeLinePrefix = " ";
|
|
29
|
+
const UniformLinePrefix = " ";
|
|
30
|
+
const ConstLinePrefix = " ";
|
|
31
|
+
|
|
32
|
+
const ShaderTemplate = `import type { ShaderProgram } from "${TYPE_IMPORT_PATH}";
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* The shader program for the block.
|
|
36
|
+
*/
|
|
37
|
+
export const shaderProgram: ShaderProgram = {
|
|
38
|
+
vertex: ${VERTEX_SHADER},
|
|
39
|
+
fragment: {
|
|
40
|
+
uniform: \`${UNIFORMS}\`,${CONSTS_PROPERTY}
|
|
41
|
+
mainInputTexture: "${MAIN_INPUT_NAME}",
|
|
42
|
+
mainFunctionName: "${MAIN_FUNCTION_NAME}",
|
|
43
|
+
functions: [${FUNCTIONS}
|
|
44
|
+
],
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* The uniform names for this shader, to be used in the shader binding so
|
|
50
|
+
* that the names are always in sync.
|
|
51
|
+
*/
|
|
52
|
+
export const uniforms = {
|
|
53
|
+
${UNIFORM_NAMES}
|
|
54
|
+
};
|
|
55
|
+
`;
|
|
56
|
+
|
|
57
|
+
const UniformNameLinePrefix = " ";
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Converts a single shader to a .ts file which can be imported by a hardcoded block
|
|
61
|
+
* @param fragmentShaderPath - The path to the fragment file for the shader
|
|
62
|
+
* @param importPath - The path to import the ShaderProgram type from
|
|
63
|
+
*/
|
|
64
|
+
export function convertShaderForHardcodedBlock(fragmentShaderPath: string, importPath: string): void {
|
|
65
|
+
console.log(`Processing fragment shader: ${fragmentShaderPath}`);
|
|
66
|
+
|
|
67
|
+
// See if there is a corresponding vertex shader
|
|
68
|
+
let vertexShader: string | undefined = undefined;
|
|
69
|
+
const vertexShaderPath = fragmentShaderPath.replace(".fragment.glsl", ".vertex.glsl");
|
|
70
|
+
if (fs.existsSync(vertexShaderPath)) {
|
|
71
|
+
vertexShader = fs.readFileSync(vertexShaderPath, "utf8");
|
|
72
|
+
}
|
|
73
|
+
if (vertexShader) {
|
|
74
|
+
console.log("Found vertex shader");
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Read the fragment shader
|
|
78
|
+
const fragmentShader = fs.readFileSync(fragmentShaderPath, "utf8");
|
|
79
|
+
const fragmentShaderInfo = parseFragmentShader(fragmentShader);
|
|
80
|
+
|
|
81
|
+
// Write the shader TS file
|
|
82
|
+
const shaderFile = fragmentShaderPath.replace(".fragment.glsl", ".shader.ts");
|
|
83
|
+
const functionsSection: string[] = [];
|
|
84
|
+
for (const shaderFunction of fragmentShaderInfo.shaderCode.functions) {
|
|
85
|
+
functionsSection.push(
|
|
86
|
+
FunctionTemplate.replace(FUNCTION_NAME, shaderFunction.name).replace(
|
|
87
|
+
FUNCTION_CODE,
|
|
88
|
+
addLinePrefixes(shaderFunction.code, CodeLinePrefix)
|
|
89
|
+
)
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
const finalContents = ShaderTemplate.replace(VERTEX_SHADER, vertexShader ? `\`${vertexShader}\`` : "undefined")
|
|
93
|
+
.replace(TYPE_IMPORT_PATH, importPath)
|
|
94
|
+
.replace(UNIFORMS, "\n" + addLinePrefixes(fragmentShaderInfo.shaderCode.uniform || "", UniformLinePrefix))
|
|
95
|
+
.replace(MAIN_FUNCTION_NAME, fragmentShaderInfo.shaderCode.mainFunctionName)
|
|
96
|
+
.replace(MAIN_INPUT_NAME, fragmentShaderInfo.shaderCode.mainInputTexture || '""')
|
|
97
|
+
.replace(
|
|
98
|
+
CONSTS_PROPERTY,
|
|
99
|
+
fragmentShaderInfo.shaderCode.const
|
|
100
|
+
? ConstsTemplate.replace(
|
|
101
|
+
CONSTS_VALUE,
|
|
102
|
+
addLinePrefixes(fragmentShaderInfo.shaderCode.const, ConstLinePrefix)
|
|
103
|
+
)
|
|
104
|
+
: ""
|
|
105
|
+
)
|
|
106
|
+
.replace(FUNCTIONS, functionsSection.join(""))
|
|
107
|
+
.replace(
|
|
108
|
+
UNIFORM_NAMES,
|
|
109
|
+
addLinePrefixes(
|
|
110
|
+
fragmentShaderInfo.uniforms.map((u) => `${u.name}: "${u.name}",`).join("\n"),
|
|
111
|
+
UniformNameLinePrefix
|
|
112
|
+
)
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
fs.writeFileSync(shaderFile, finalContents);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Converts .fragment.glsl and vertex.glsl file pairs into .shader.ts files which export a ShaderProgram object.
|
|
120
|
+
* @param shaderPath - The path to the .glsl files to convert.
|
|
121
|
+
* @param importPath - The path to import the ShaderProgram type from.
|
|
122
|
+
*/
|
|
123
|
+
export function convertShaders(shaderPath: string, importPath: string) {
|
|
124
|
+
// Get all files in the path
|
|
125
|
+
const allFiles = fs.readdirSync(shaderPath, { withFileTypes: true, recursive: true });
|
|
126
|
+
|
|
127
|
+
// Find all fragment shaders (excluding the template)
|
|
128
|
+
const fragmentShaderFiles = allFiles.filter(
|
|
129
|
+
(file) => file.isFile() && file.name.endsWith(".fragment.glsl") && !file.name.endsWith("template.fragment.glsl")
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
// Convert all shaders
|
|
133
|
+
for (const fragmentShaderFile of fragmentShaderFiles) {
|
|
134
|
+
convertShaderForHardcodedBlock(path.join(fragmentShaderFile.path, fragmentShaderFile.name), importPath);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Prefixes each line in the input
|
|
140
|
+
* @param input - The input string
|
|
141
|
+
* @param prefix - The prefix to add to each line
|
|
142
|
+
* @returns The input with each line prefixed
|
|
143
|
+
*/
|
|
144
|
+
function addLinePrefixes(input: string, prefix: string): string {
|
|
145
|
+
return input
|
|
146
|
+
.split("\n")
|
|
147
|
+
.map((line) => prefix + line)
|
|
148
|
+
.join("\n");
|
|
149
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Describes a shader function.
|
|
3
|
+
*/
|
|
4
|
+
export type ShaderFunction = {
|
|
5
|
+
/**
|
|
6
|
+
* The name of the function.
|
|
7
|
+
*/
|
|
8
|
+
name: string;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* The code of the function.
|
|
12
|
+
*/
|
|
13
|
+
code: string;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Describes a shader code.
|
|
18
|
+
*/
|
|
19
|
+
export type ShaderCode = {
|
|
20
|
+
/**
|
|
21
|
+
* The declaration of the const variables.
|
|
22
|
+
*/
|
|
23
|
+
const?: string;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The declaration of the uniform variables.
|
|
27
|
+
*/
|
|
28
|
+
uniform?: string;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* The declaration of the uniform variables that should be common for all ShaderBlock instances using this shader code.
|
|
32
|
+
*/
|
|
33
|
+
uniformSingle?: string;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* The name of the main function.
|
|
37
|
+
*/
|
|
38
|
+
mainFunctionName: string;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* The name of the input texture which is passed through if the block is disabled.
|
|
42
|
+
*/
|
|
43
|
+
mainInputTexture?: string;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* The list of functions used in the shader.
|
|
47
|
+
*/
|
|
48
|
+
functions: ShaderFunction[];
|
|
49
|
+
};
|
|
@@ -1,174 +1,116 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
const TYPE_IMPORT_PATH = "@TYPE_IMPORT_PATH@";
|
|
5
|
-
const VERTEX_SHADER = "@VERTEX_SHADER@";
|
|
6
|
-
const UNIFORMS = "@UNIFORMS@";
|
|
7
|
-
const CONSTS_VALUE = "@CONSTS@";
|
|
8
|
-
const CONSTS_PROPERTY = "@CONSTS_PROPERTY@";
|
|
9
|
-
const MAIN_INPUT_NAME = "@MAIN_INPUT_NAME@";
|
|
10
|
-
const MAIN_FUNCTION_NAME = "@MAIN_FUNCTION_NAME@";
|
|
11
|
-
const FUNCTIONS = "@FUNCTIONS@";
|
|
12
|
-
const FUNCTION_NAME = "@FUNCTION_NAME@";
|
|
13
|
-
const FUNCTION_CODE = "@FUNCTION_CODE@";
|
|
14
|
-
const UNIFORM_NAMES = "@UNIFORM_NAMES@";
|
|
15
|
-
|
|
16
|
-
const ConstsTemplate = `
|
|
17
|
-
const: \`${CONSTS_VALUE}\`,`;
|
|
18
|
-
|
|
19
|
-
const FunctionTemplate = `
|
|
20
|
-
{
|
|
21
|
-
name: "${FUNCTION_NAME}",
|
|
22
|
-
code: \`
|
|
23
|
-
${FUNCTION_CODE}
|
|
24
|
-
\`,
|
|
25
|
-
},`;
|
|
26
|
-
|
|
27
|
-
const CodeLinePrefix = " ";
|
|
28
|
-
const UniformLinePrefix = " ";
|
|
29
|
-
const ConstLinePrefix = " ";
|
|
30
|
-
|
|
31
|
-
const ShaderTemplate = `import type { ShaderProgram } from "${TYPE_IMPORT_PATH}";
|
|
1
|
+
import type { Nullable } from "@babylonjs/core/types";
|
|
2
|
+
import type { ShaderCode, ShaderFunction } from "./shaderCode.types";
|
|
32
3
|
|
|
33
|
-
|
|
34
|
-
* The shader program for the block.
|
|
35
|
-
*/
|
|
36
|
-
export const shaderProgram: ShaderProgram = {
|
|
37
|
-
vertex: ${VERTEX_SHADER},
|
|
38
|
-
fragment: {
|
|
39
|
-
uniform: \`${UNIFORMS}\`,${CONSTS_PROPERTY}
|
|
40
|
-
mainInputTexture: "${MAIN_INPUT_NAME}",
|
|
41
|
-
mainFunctionName: "${MAIN_FUNCTION_NAME}",
|
|
42
|
-
functions: [${FUNCTIONS}
|
|
43
|
-
],
|
|
44
|
-
},
|
|
45
|
-
};
|
|
4
|
+
const GetFunctionNamesRegEx = /\S*\w+\s+(\w+)\s*\(/g;
|
|
46
5
|
|
|
47
6
|
/**
|
|
48
|
-
*
|
|
49
|
-
* that the names are always in sync.
|
|
7
|
+
* Describes the supported metadata properties for a uniform
|
|
50
8
|
*/
|
|
51
|
-
export
|
|
52
|
-
|
|
9
|
+
export type UniformMetadataProperties = {
|
|
10
|
+
/**
|
|
11
|
+
* If supplied, the default value to use for the corresponding input connection point
|
|
12
|
+
*/
|
|
13
|
+
default?: any;
|
|
53
14
|
};
|
|
54
|
-
`;
|
|
55
|
-
|
|
56
|
-
const UniformNameLinePrefix = " ";
|
|
57
|
-
|
|
58
|
-
const GetFunctionNamesRegEx = /\S*\w+\s+(\w+)\s*\(/g;
|
|
59
15
|
|
|
60
16
|
/**
|
|
61
|
-
*
|
|
62
|
-
* @param fragmentShaderPath - The path to the fragment file for the shader
|
|
63
|
-
* @param importPath - The path to import the ShaderProgram type from
|
|
17
|
+
* Describes a uniform in a shader
|
|
64
18
|
*/
|
|
65
|
-
export
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
const vertexShaderPath = fragmentShaderPath.replace(".fragment.glsl", ".vertex.glsl");
|
|
71
|
-
if (fs.existsSync(vertexShaderPath)) {
|
|
72
|
-
vertexShader = fs.readFileSync(vertexShaderPath, "utf8");
|
|
73
|
-
}
|
|
74
|
-
if (vertexShader) {
|
|
75
|
-
console.log("Found vertex shader");
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// Read the fragment shader
|
|
79
|
-
const fragmentShader = fs.readFileSync(fragmentShaderPath, "utf8");
|
|
80
|
-
|
|
81
|
-
// Version check
|
|
82
|
-
const versionsFound = [...fragmentShader.matchAll(/\/\/\s+\[Smart Filter Shader Version\]\s*=\s*(\d+)/g)].map(
|
|
83
|
-
(match) => match[1]
|
|
84
|
-
);
|
|
85
|
-
let version: number = 1;
|
|
86
|
-
if (versionsFound.length === 1 && versionsFound[0]) {
|
|
87
|
-
version = parseInt(versionsFound[0]);
|
|
88
|
-
}
|
|
89
|
-
console.log(`Shader version: ${version}`);
|
|
90
|
-
|
|
91
|
-
let fragmentShaderInfo: FragmentShaderInfo;
|
|
92
|
-
|
|
93
|
-
switch (version) {
|
|
94
|
-
case 1:
|
|
95
|
-
{
|
|
96
|
-
fragmentShaderInfo = processFragmentShaderV1(fragmentShader);
|
|
97
|
-
}
|
|
98
|
-
break;
|
|
99
|
-
default: {
|
|
100
|
-
throw new Error(`Unsupported shader version: ${version}`);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// Write the shader TS file
|
|
105
|
-
const shaderFile = fragmentShaderPath.replace(".fragment.glsl", ".shader.ts");
|
|
106
|
-
const finalContents = ShaderTemplate.replace(VERTEX_SHADER, vertexShader ? `\`${vertexShader}\`` : "undefined")
|
|
107
|
-
.replace(TYPE_IMPORT_PATH, importPath)
|
|
108
|
-
.replace(UNIFORMS, "\n" + addLinePrefixes(fragmentShaderInfo.finalUniforms.join("\n"), UniformLinePrefix))
|
|
109
|
-
.replace(MAIN_FUNCTION_NAME, fragmentShaderInfo.mainFunctionName)
|
|
110
|
-
.replace(MAIN_INPUT_NAME, fragmentShaderInfo.mainInputName)
|
|
111
|
-
.replace(
|
|
112
|
-
CONSTS_PROPERTY,
|
|
113
|
-
fragmentShaderInfo.finalConsts.length > 0
|
|
114
|
-
? ConstsTemplate.replace(
|
|
115
|
-
CONSTS_VALUE,
|
|
116
|
-
addLinePrefixes(fragmentShaderInfo.finalConsts.join("\n"), ConstLinePrefix)
|
|
117
|
-
)
|
|
118
|
-
: ""
|
|
119
|
-
)
|
|
120
|
-
.replace(FUNCTIONS, fragmentShaderInfo.extractedFunctions.join(""))
|
|
121
|
-
.replace(UNIFORM_NAMES, addLinePrefixes(fragmentShaderInfo.uniformNames.join("\n"), UniformNameLinePrefix));
|
|
122
|
-
|
|
123
|
-
fs.writeFileSync(shaderFile, finalContents);
|
|
124
|
-
}
|
|
19
|
+
export type UniformMetadata = {
|
|
20
|
+
/**
|
|
21
|
+
* The original name of the uniform (not renamed)
|
|
22
|
+
*/
|
|
23
|
+
name: string;
|
|
125
24
|
|
|
126
|
-
/**
|
|
127
|
-
* Information about a fragment shader
|
|
128
|
-
*/
|
|
129
|
-
type FragmentShaderInfo = {
|
|
130
25
|
/**
|
|
131
|
-
* The
|
|
26
|
+
* The type string of the uniform
|
|
132
27
|
*/
|
|
133
|
-
|
|
28
|
+
type: string;
|
|
134
29
|
|
|
135
30
|
/**
|
|
136
|
-
*
|
|
31
|
+
* Optional properties of the uniform
|
|
137
32
|
*/
|
|
138
|
-
|
|
33
|
+
properties?: UniformMetadataProperties;
|
|
34
|
+
};
|
|
139
35
|
|
|
36
|
+
/**
|
|
37
|
+
* Information about a fragment shader
|
|
38
|
+
*/
|
|
39
|
+
export type FragmentShaderInfo = {
|
|
140
40
|
/**
|
|
141
|
-
*
|
|
41
|
+
* If supplied, the blockType to use for the block
|
|
142
42
|
*/
|
|
143
|
-
|
|
43
|
+
blockType?: string;
|
|
144
44
|
|
|
145
45
|
/**
|
|
146
|
-
*
|
|
46
|
+
* If true, optimization should be disabled for this shader
|
|
147
47
|
*/
|
|
148
|
-
|
|
48
|
+
disableOptimization?: boolean;
|
|
149
49
|
|
|
150
50
|
/**
|
|
151
|
-
* The
|
|
51
|
+
* The shader code
|
|
152
52
|
*/
|
|
153
|
-
|
|
53
|
+
shaderCode: ShaderCode;
|
|
154
54
|
|
|
155
55
|
/**
|
|
156
|
-
* The
|
|
56
|
+
* The set of uniforms
|
|
157
57
|
*/
|
|
158
|
-
|
|
58
|
+
uniforms: UniformMetadata[];
|
|
159
59
|
};
|
|
160
60
|
|
|
161
61
|
/**
|
|
162
|
-
*
|
|
62
|
+
* Parses a fragment shader
|
|
163
63
|
* @param fragmentShader - The fragment shader to process
|
|
164
64
|
* @returns The processed fragment shader
|
|
165
65
|
*/
|
|
166
|
-
function
|
|
66
|
+
export function parseFragmentShader(fragmentShader: string): FragmentShaderInfo {
|
|
67
|
+
const { header, fragmentShaderWithoutHeader } = readHeader(fragmentShader);
|
|
68
|
+
fragmentShader = fragmentShaderWithoutHeader;
|
|
69
|
+
const blockType = header?.[SmartFilterBlockTypeKey] || undefined;
|
|
70
|
+
|
|
71
|
+
// Read the uniforms
|
|
72
|
+
const uniforms: UniformMetadata[] = [];
|
|
73
|
+
const uniformRegExp = new RegExp(/(\/\/\s*\{.*\}\s*(?:\r\n|\r|\n)+)?(uniform .*)/gm);
|
|
74
|
+
const uniformGroups = fragmentShader.matchAll(uniformRegExp);
|
|
75
|
+
for (const matches of uniformGroups) {
|
|
76
|
+
const annotationJSON = matches[1];
|
|
77
|
+
const uniformLine = matches[2];
|
|
78
|
+
|
|
79
|
+
if (!uniformLine) {
|
|
80
|
+
throw new Error("Uniform line not found");
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const uniformLineMatches = new RegExp(/^uniform\s+(\w+)\s+(\w+)\s*;?/gm).exec(uniformLine);
|
|
84
|
+
if (!uniformLineMatches || uniformLineMatches.length < 3) {
|
|
85
|
+
throw new Error(`Uniforms must have a type and a name: '${uniformLine}'`);
|
|
86
|
+
}
|
|
87
|
+
const uniformTypeString = uniformLineMatches[1];
|
|
88
|
+
const uniformName = uniformLineMatches[2];
|
|
89
|
+
|
|
90
|
+
if (!uniformTypeString) {
|
|
91
|
+
throw new Error(`Uniforms must have a type: '${uniformLine}'`);
|
|
92
|
+
}
|
|
93
|
+
if (!uniformName) {
|
|
94
|
+
throw new Error(`Uniforms must have a name: '${uniformLine}'`);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
uniforms.push({
|
|
98
|
+
name: uniformName,
|
|
99
|
+
type: uniformTypeString,
|
|
100
|
+
properties: annotationJSON ? JSON.parse(annotationJSON.replace("//", "").trim()) : undefined,
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
if (annotationJSON) {
|
|
104
|
+
// Strip out any annotation so it isn't mistaken for function bodies
|
|
105
|
+
fragmentShader = fragmentShader.replace(annotationJSON, "");
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
167
109
|
const fragmentShaderWithNoFunctionBodies = removeFunctionBodies(fragmentShader);
|
|
168
110
|
|
|
169
111
|
// Collect uniform, const, and function names which need to be decorated
|
|
170
112
|
// eslint-disable-next-line prettier/prettier
|
|
171
|
-
const
|
|
113
|
+
const uniformNames = uniforms.map((uniform) => uniform.name);
|
|
172
114
|
console.log(`Uniforms found: ${JSON.stringify(uniforms)}`);
|
|
173
115
|
const consts = [...fragmentShader.matchAll(/\S*const\s+\w*\s+(\w*)\s*=.*;/g)].map((match) => match[1]);
|
|
174
116
|
console.log(`Consts found: ${JSON.stringify(consts)}`);
|
|
@@ -178,14 +120,13 @@ function processFragmentShaderV1(fragmentShader: string): FragmentShaderInfo {
|
|
|
178
120
|
console.log(`Functions found: ${JSON.stringify(functionNames)}`);
|
|
179
121
|
|
|
180
122
|
// Decorate the uniforms, consts, and functions
|
|
181
|
-
const symbolsToDecorate = [...
|
|
123
|
+
const symbolsToDecorate = [...uniformNames, ...consts, ...functionNames];
|
|
182
124
|
let fragmentShaderWithRenamedSymbols = fragmentShader;
|
|
183
125
|
for (const symbol of symbolsToDecorate) {
|
|
184
126
|
const regex = new RegExp(`(?<=\\W+)${symbol}(?=\\W+)`, "gs");
|
|
185
127
|
fragmentShaderWithRenamedSymbols = fragmentShaderWithRenamedSymbols.replace(regex, `_${symbol}_`);
|
|
186
128
|
}
|
|
187
129
|
console.log(`${symbolsToDecorate.length} symbol(s) renamed`);
|
|
188
|
-
const uniformNames = uniforms.map((uniform) => `${uniform}: "${uniform}",`);
|
|
189
130
|
|
|
190
131
|
// Extract all the uniforms
|
|
191
132
|
const finalUniforms = [...fragmentShaderWithRenamedSymbols.matchAll(/^\s*(uniform\s.*)/gm)].map(
|
|
@@ -202,32 +143,28 @@ function processFragmentShaderV1(fragmentShader: string): FragmentShaderInfo {
|
|
|
202
143
|
if (mainInputs.length !== 1 || !mainInputs[0]) {
|
|
203
144
|
throw new Error("Exactly one main input must be defined in the shader");
|
|
204
145
|
}
|
|
205
|
-
const
|
|
146
|
+
const mainInputTexture = mainInputs[0];
|
|
206
147
|
|
|
207
148
|
// Extract all the functions
|
|
208
149
|
const { extractedFunctions, mainFunctionName } = extractFunctions(fragmentShaderWithRenamedSymbols);
|
|
209
150
|
|
|
210
|
-
|
|
211
|
-
finalUniforms,
|
|
151
|
+
const shaderCode: ShaderCode = {
|
|
152
|
+
uniform: finalUniforms.join("\n"),
|
|
212
153
|
mainFunctionName,
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
extractedFunctions,
|
|
216
|
-
uniformNames,
|
|
154
|
+
mainInputTexture,
|
|
155
|
+
functions: extractedFunctions,
|
|
217
156
|
};
|
|
218
|
-
}
|
|
219
157
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
.join("\n");
|
|
158
|
+
if (finalConsts.length > 0) {
|
|
159
|
+
shaderCode.const = finalConsts.join("\n");
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return {
|
|
163
|
+
blockType,
|
|
164
|
+
shaderCode,
|
|
165
|
+
uniforms,
|
|
166
|
+
disableOptimization: !!header?.disableOptimizer,
|
|
167
|
+
};
|
|
231
168
|
}
|
|
232
169
|
|
|
233
170
|
/**
|
|
@@ -239,7 +176,7 @@ function extractFunctions(fragment: string): {
|
|
|
239
176
|
/**
|
|
240
177
|
* The extracted functions
|
|
241
178
|
*/
|
|
242
|
-
extractedFunctions:
|
|
179
|
+
extractedFunctions: ShaderFunction[];
|
|
243
180
|
|
|
244
181
|
/**
|
|
245
182
|
* The name of the main function
|
|
@@ -247,7 +184,7 @@ function extractFunctions(fragment: string): {
|
|
|
247
184
|
mainFunctionName: string;
|
|
248
185
|
} {
|
|
249
186
|
const lines = fragment.split("\n");
|
|
250
|
-
const extractedFunctions:
|
|
187
|
+
const extractedFunctions: ShaderFunction[] = [];
|
|
251
188
|
let mainFunctionName: string | undefined;
|
|
252
189
|
let inFunction = false;
|
|
253
190
|
let depth = 0;
|
|
@@ -271,12 +208,11 @@ function extractFunctions(fragment: string): {
|
|
|
271
208
|
inFunction = false;
|
|
272
209
|
const { functionBody, functionName, isMainFunction } = processFunctionBody(currentFunction);
|
|
273
210
|
|
|
274
|
-
extractedFunctions.push(
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
);
|
|
211
|
+
extractedFunctions.push({
|
|
212
|
+
name: functionName,
|
|
213
|
+
code: functionBody,
|
|
214
|
+
});
|
|
215
|
+
|
|
280
216
|
if (isMainFunction) {
|
|
281
217
|
if (mainFunctionName) {
|
|
282
218
|
throw new Error("Multiple main functions found in shader code");
|
|
@@ -365,24 +301,68 @@ function removeFunctionBodies(input: string): string {
|
|
|
365
301
|
return output;
|
|
366
302
|
}
|
|
367
303
|
|
|
304
|
+
const SmartFilterBlockTypeKey = "smartFilterBlockType";
|
|
305
|
+
|
|
368
306
|
/**
|
|
369
|
-
*
|
|
370
|
-
* @param shaderPath - The path to the .glsl files to convert.
|
|
371
|
-
* @param importPath - The path to import the ShaderProgram type from.
|
|
307
|
+
* The format of the header we expect in a GLSL shader
|
|
372
308
|
*/
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
309
|
+
type GlslHeader = {
|
|
310
|
+
/**
|
|
311
|
+
* The block type to use for the shader, required when converting to a
|
|
312
|
+
* SerializedBlockDefinition, but not when exporting to a .ts file
|
|
313
|
+
* to be included in a hardcoded block definition
|
|
314
|
+
*/
|
|
315
|
+
[SmartFilterBlockTypeKey]: string;
|
|
376
316
|
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
317
|
+
/**
|
|
318
|
+
* If true, optimization should be disabled for this shader
|
|
319
|
+
*/
|
|
320
|
+
disableOptimizer?: boolean;
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Reads the GlslHeader from the shader code
|
|
325
|
+
* @param fragmentShader - The shader code to read
|
|
326
|
+
* @returns - The GlslHeader if found, otherwise null
|
|
327
|
+
*/
|
|
328
|
+
function readHeader(fragmentShader: string): {
|
|
329
|
+
/**
|
|
330
|
+
* The glsl header, or null if there wasn't one
|
|
331
|
+
*/
|
|
332
|
+
header: Nullable<GlslHeader>;
|
|
381
333
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
334
|
+
/**
|
|
335
|
+
* The fragment shader code with the header removed if there was one
|
|
336
|
+
*/
|
|
337
|
+
fragmentShaderWithoutHeader: string;
|
|
338
|
+
} {
|
|
339
|
+
const singleLineHeaderMatch = new RegExp(/^\/\/\s*(\{.*\})/gm).exec(fragmentShader);
|
|
340
|
+
if (singleLineHeaderMatch && singleLineHeaderMatch[1]) {
|
|
341
|
+
return {
|
|
342
|
+
header: JSON.parse(singleLineHeaderMatch[1].trim()),
|
|
343
|
+
fragmentShaderWithoutHeader: fragmentShader.replace(singleLineHeaderMatch[0], ""),
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
const multiLineHeaderMatch = new RegExp("^\\/\\*(.*)\\*\\/", "gs").exec(fragmentShader);
|
|
348
|
+
if (multiLineHeaderMatch && multiLineHeaderMatch[1]) {
|
|
349
|
+
return {
|
|
350
|
+
header: JSON.parse(multiLineHeaderMatch[1].trim()),
|
|
351
|
+
fragmentShaderWithoutHeader: fragmentShader.replace(multiLineHeaderMatch[0], ""),
|
|
352
|
+
};
|
|
385
353
|
}
|
|
354
|
+
|
|
355
|
+
return {
|
|
356
|
+
header: null,
|
|
357
|
+
fragmentShaderWithoutHeader: fragmentShader,
|
|
358
|
+
};
|
|
386
359
|
}
|
|
387
360
|
|
|
388
|
-
|
|
361
|
+
/**
|
|
362
|
+
* Determines if a fragment shader has the GLSL header required for parsing
|
|
363
|
+
* @param fragmentShader - The fragment shader to check
|
|
364
|
+
* @returns True if the fragment shader has the GLSL header
|
|
365
|
+
*/
|
|
366
|
+
export function hasGlslHeader(fragmentShader: string): boolean {
|
|
367
|
+
return fragmentShader.indexOf(SmartFilterBlockTypeKey) !== -1;
|
|
368
|
+
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* @example node watchShaders.js <shaderPath> <importPath>
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import {
|
|
9
|
+
import { convertShaderForHardcodedBlock } from "./convertShaderForHardcodedBlock.js";
|
|
10
10
|
import { watch } from "chokidar";
|
|
11
11
|
import { extname } from "path";
|
|
12
12
|
|
|
@@ -29,7 +29,7 @@ if (externalArguments.length >= 2 && externalArguments[0] && externalArguments[1
|
|
|
29
29
|
// Wrap in try-catch to prevent the watcher from crashing
|
|
30
30
|
// if the new shader changes are invalid
|
|
31
31
|
try {
|
|
32
|
-
|
|
32
|
+
convertShaderForHardcodedBlock(file, importPath);
|
|
33
33
|
console.log(`Successfully updated shader ${file}`);
|
|
34
34
|
} catch (error) {
|
|
35
35
|
console.error(`Failed to convert shader ${file}: ${error}`);
|
|
@@ -38,16 +38,16 @@ export function registerFinalRenderCommand(
|
|
|
38
38
|
commandOwner: BaseBlock,
|
|
39
39
|
shaderBlockRuntime: ShaderRuntime
|
|
40
40
|
): void {
|
|
41
|
-
const
|
|
41
|
+
const commandOwnerBlockType = commandOwner.blockType;
|
|
42
42
|
if (renderTargetWrapper) {
|
|
43
43
|
runtime.registerCommand(
|
|
44
|
-
createCommand(`${
|
|
44
|
+
createCommand(`${commandOwnerBlockType}.renderToFinalTexture`, commandOwner, () => {
|
|
45
45
|
shaderBlockRuntime.renderToTexture(renderTargetWrapper);
|
|
46
46
|
})
|
|
47
47
|
);
|
|
48
48
|
} else {
|
|
49
49
|
runtime.registerCommand(
|
|
50
|
-
createCommand(`${
|
|
50
|
+
createCommand(`${commandOwnerBlockType}.renderToCanvas`, commandOwner, () => {
|
|
51
51
|
shaderBlockRuntime.renderToCanvas();
|
|
52
52
|
})
|
|
53
53
|
);
|