@babylonjs/smart-filters 0.7.12-alpha → 0.7.14-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.
Files changed (60) hide show
  1. package/dist/command/commandBufferDebugger.d.ts.map +1 -1
  2. package/dist/command/commandBufferDebugger.js +4 -3
  3. package/dist/command/commandBufferDebugger.js.map +1 -1
  4. package/dist/index.d.ts +1 -0
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +2 -0
  7. package/dist/index.js.map +1 -1
  8. package/dist/optimization/dependencyGraph.d.ts.map +1 -1
  9. package/dist/optimization/dependencyGraph.js +2 -1
  10. package/dist/optimization/dependencyGraph.js.map +1 -1
  11. package/dist/optimization/smartFilterOptimizer.d.ts.map +1 -1
  12. package/dist/optimization/smartFilterOptimizer.js +7 -6
  13. package/dist/optimization/smartFilterOptimizer.js.map +1 -1
  14. package/dist/serialization/importCustomBlockDefinition.js +1 -25
  15. package/dist/serialization/importCustomBlockDefinition.js.map +1 -1
  16. package/dist/utils/buildTools/buildShaders.js +1 -1
  17. package/dist/utils/buildTools/buildShaders.js.map +1 -1
  18. package/dist/utils/buildTools/convertGlslIntoBlock.d.ts +7 -0
  19. package/dist/utils/buildTools/convertGlslIntoBlock.d.ts.map +1 -0
  20. package/dist/utils/buildTools/convertGlslIntoBlock.js +217 -0
  21. package/dist/utils/buildTools/convertGlslIntoBlock.js.map +1 -0
  22. package/dist/utils/buildTools/convertGlslIntoShaderProgram.d.ts +26 -0
  23. package/dist/utils/buildTools/convertGlslIntoShaderProgram.d.ts.map +1 -0
  24. package/dist/utils/buildTools/{convertShaderForHardcodedBlock.js → convertGlslIntoShaderProgram.js} +45 -31
  25. package/dist/utils/buildTools/convertGlslIntoShaderProgram.js.map +1 -0
  26. package/dist/utils/buildTools/convertShaders.d.ts +13 -0
  27. package/dist/utils/buildTools/convertShaders.d.ts.map +1 -0
  28. package/dist/utils/buildTools/convertShaders.js +38 -0
  29. package/dist/utils/buildTools/convertShaders.js.map +1 -0
  30. package/dist/utils/buildTools/determineVersion.d.ts.map +1 -1
  31. package/dist/utils/buildTools/determineVersion.js.map +1 -1
  32. package/dist/utils/buildTools/shaderConverter.d.ts +2 -1
  33. package/dist/utils/buildTools/shaderConverter.d.ts.map +1 -1
  34. package/dist/utils/buildTools/shaderConverter.js +32 -6
  35. package/dist/utils/buildTools/shaderConverter.js.map +1 -1
  36. package/dist/utils/buildTools/versionUp.js +1 -0
  37. package/dist/utils/buildTools/versionUp.js.map +1 -1
  38. package/dist/utils/buildTools/watchShaders.js +2 -2
  39. package/dist/utils/buildTools/watchShaders.js.map +1 -1
  40. package/dist/version.d.ts +1 -1
  41. package/dist/version.js +1 -1
  42. package/package.json +1 -1
  43. package/readme.md +1 -171
  44. package/src/command/commandBufferDebugger.ts +4 -3
  45. package/src/index.ts +3 -0
  46. package/src/optimization/dependencyGraph.ts +3 -1
  47. package/src/optimization/smartFilterOptimizer.ts +7 -6
  48. package/src/serialization/importCustomBlockDefinition.ts +1 -26
  49. package/src/utils/buildTools/buildShaders.ts +1 -1
  50. package/src/utils/buildTools/convertGlslIntoBlock.ts +251 -0
  51. package/src/utils/buildTools/{convertShaderForHardcodedBlock.ts → convertGlslIntoShaderProgram.ts} +58 -36
  52. package/src/utils/buildTools/convertShaders.ts +43 -0
  53. package/src/utils/buildTools/determineVersion.ts +1 -0
  54. package/src/utils/buildTools/shaderConverter.ts +34 -7
  55. package/src/utils/buildTools/versionUp.ts +1 -0
  56. package/src/utils/buildTools/watchShaders.ts +2 -2
  57. package/src/version.ts +1 -1
  58. package/dist/utils/buildTools/convertShaderForHardcodedBlock.d.ts +0 -13
  59. package/dist/utils/buildTools/convertShaderForHardcodedBlock.d.ts.map +0 -1
  60. package/dist/utils/buildTools/convertShaderForHardcodedBlock.js.map +0 -1
@@ -1,3 +1,4 @@
1
+ import { Logger } from "@babylonjs/core/Misc/logger.js";
1
2
  import type { CommandBuffer } from "./commandBuffer";
2
3
 
3
4
  /**
@@ -5,9 +6,9 @@ import type { CommandBuffer } from "./commandBuffer";
5
6
  * @param commandBuffer - The command buffer to log
6
7
  */
7
8
  export function logCommands(commandBuffer: Readonly<CommandBuffer>) {
8
- console.log("----- Command buffer commands -----");
9
+ Logger.Log("----- Command buffer commands -----");
9
10
  commandBuffer.visitCommands((command) => {
10
- console.log(` Owner: ${command.owner.blockType} (${command.owner.name}) - Command: ${command.name}`);
11
+ Logger.Log(` Owner: ${command.owner.blockType} (${command.owner.name}) - Command: ${command.name}`);
11
12
  });
12
- console.log("-----------------------------------");
13
+ Logger.Log("-----------------------------------");
13
14
  }
package/src/index.ts CHANGED
@@ -10,3 +10,6 @@ export * from "./utils/index.js";
10
10
  export { type IDisposable } from "./IDisposable.js";
11
11
  export { SmartFilter, type InitializationData } from "./smartFilter.js";
12
12
  export * from "./version.js";
13
+
14
+ // So that users of the Smart Filters core can easily modify the logger settings (e.g. to change the logging level)
15
+ export { Logger } from "@babylonjs/core/Misc/logger.js";
@@ -1,3 +1,5 @@
1
+ import { Logger } from "@babylonjs/core/Misc/logger.js";
2
+
1
3
  /**
2
4
  * Implementation of a dependency graph.
3
5
  */
@@ -87,7 +89,7 @@ export class DependencyGraph<T> {
87
89
  }
88
90
 
89
91
  if (this._list.size > 0) {
90
- console.error(this._list);
92
+ Logger.Error(JSON.stringify(this._list));
91
93
  throw new Error("Circular dependency detected!");
92
94
  }
93
95
  }
@@ -1,4 +1,5 @@
1
1
  import type { Nullable } from "@babylonjs/core/types";
2
+ import { Logger } from "@babylonjs/core/Misc/logger.js";
2
3
 
3
4
  import type { ConnectionPoint } from "../connection/connectionPoint";
4
5
  import type { ShaderBinding } from "../runtime/shaderRuntime";
@@ -690,12 +691,12 @@ export class SmartFilterOptimizer {
690
691
  code = code!.replace(/\r/g, "");
691
692
  code = code!.replace(/\n(\n)*/g, "\n");
692
693
 
693
- console.log(`=================== BLOCK (forceUnoptimized=${this._forceUnoptimized}) ===================`);
694
- console.log(codeUniforms);
695
- console.log(codeConsts);
696
- console.log(code);
697
- console.log("remappedSymbols=", this._remappedSymbols);
698
- console.log("samplers=", samplers);
694
+ Logger.Log(`=================== BLOCK (forceUnoptimized=${this._forceUnoptimized}) ===================`);
695
+ Logger.Log(codeUniforms);
696
+ Logger.Log(codeConsts);
697
+ Logger.Log(code);
698
+ Logger.Log(`remappedSymbols=${this._remappedSymbols}`);
699
+ Logger.Log(`samplers=${samplers}`);
699
700
  }
700
701
 
701
702
  optimizedBlock.setShaderProgram({
@@ -54,35 +54,10 @@ function importAnnotatedGlsl(fragmentShader: string): SerializedShaderBlockDefin
54
54
  // Calculate the input connection points
55
55
  const inputConnectionPoints: SerializedInputConnectionPointV1[] = [];
56
56
  for (const uniform of fragmentShaderInfo.uniforms) {
57
- // Convert to ConnectionPointType
58
- let type: ConnectionPointType;
59
- switch (uniform.type) {
60
- case "float":
61
- type = ConnectionPointType.Float;
62
- break;
63
- case "sampler2D":
64
- type = ConnectionPointType.Texture;
65
- break;
66
- case "vec3":
67
- type = ConnectionPointType.Color3;
68
- break;
69
- case "vec4":
70
- type = ConnectionPointType.Color4;
71
- break;
72
- case "bool":
73
- type = ConnectionPointType.Boolean;
74
- break;
75
- case "vec2":
76
- type = ConnectionPointType.Vector2;
77
- break;
78
- default:
79
- throw new Error(`Unsupported uniform type: '${uniform.type}'`);
80
- }
81
-
82
57
  // Add to input connection point list
83
58
  const inputConnectionPoint: SerializedInputConnectionPointV1 = {
84
59
  name: uniform.name,
85
- type,
60
+ type: uniform.type,
86
61
  autoBind: uniform.properties?.autoBind as InputAutoBindV1,
87
62
  };
88
63
  if (inputConnectionPoint.type !== ConnectionPointType.Texture && uniform.properties?.default !== undefined) {
@@ -5,7 +5,7 @@
5
5
  * @example node buildShaders.js <shaderPath> <importPath>
6
6
  */
7
7
 
8
- import { convertShaders } from "./convertShaderForHardcodedBlock.js";
8
+ import { convertShaders } from "./convertShaders.js";
9
9
 
10
10
  const externalArguments = process.argv.slice(2);
11
11
  if (externalArguments.length >= 2 && externalArguments[0] && externalArguments[1]) {
@@ -0,0 +1,251 @@
1
+ import * as fs from "fs";
2
+ import { extractShaderProgramFromGlsl } from "./convertGlslIntoShaderProgram.js";
3
+ import { ConnectionPointType } from "../../connection/connectionPointType.js";
4
+
5
+ const SHADER_PROGRAM = "@SHADER_PROGRAM@";
6
+ const BLOCK_NAME = "@BLOCK_NAME@";
7
+ const NAMESPACE = "@NAMESPACE@";
8
+ const SHADER_BINDING_PRIVATE_VARIABLES = "@SHADER_BINDING_PRIVATE_VARIABLES@";
9
+ const CAMEL_CASE_UNIFORM = "@CAMEL_CASE_UNIFORM@";
10
+ const CONNECTION_POINT_TYPE = "@CONNECTION_POINT_TYPE@";
11
+ const SHADER_BINDING_CTOR_DOCSTRING_PARAMS = "@SHADER_BINDING_CTOR_DOCSTRING_PARAMS@";
12
+ const SHADER_BINDING_CTOR_PARAMS = "@SHADER_CTOR_PARAMS@";
13
+ const SHADER_BINDING_CTOR = "@SHADER_BINDING_CTOR@";
14
+ const SHADER_BINDING_BIND = "@SHADER_BINDING_BIND@";
15
+ const BLOCK_INPUT_PROPERTIES = "@BLOCK_INPUT_PROPERTIES@";
16
+ const BLOCK_GET_SHADER_BINDING_VARS = "@BLOCK_SHADER_BINDING_BIND_VARS@";
17
+ const BLOCK_GET_SHADER_PARAM_LIST = "@BLOCK_GET_SHADER_PARAM_LIST@";
18
+ const EFFECT_SETTER = "@EFFECT_SETTER@";
19
+
20
+ const ShaderBindingPrivateVariablesTemplate = ` private readonly _${CAMEL_CASE_UNIFORM}: RuntimeData<ConnectionPointType.${CONNECTION_POINT_TYPE}>;`;
21
+ const ShaderBindingCtorDocstringParams = ` * @param ${CAMEL_CASE_UNIFORM} - The ${CAMEL_CASE_UNIFORM} runtime value`;
22
+ const ShaderBindingCtorParams = ` ${CAMEL_CASE_UNIFORM}: RuntimeData<ConnectionPointType.${CONNECTION_POINT_TYPE}>`;
23
+ const ShaderBindingCtor = ` this._${CAMEL_CASE_UNIFORM} = ${CAMEL_CASE_UNIFORM};`;
24
+ const ShaderBindingBind = ` effect.${EFFECT_SETTER}(this.getRemappedName(uniforms.${CAMEL_CASE_UNIFORM}), this._${CAMEL_CASE_UNIFORM}.value);`;
25
+
26
+ const BlockInputProperties = ` /**
27
+ * The ${CAMEL_CASE_UNIFORM} connection point.
28
+ */
29
+ public readonly ${CAMEL_CASE_UNIFORM} = this._registerInput(uniforms.${CAMEL_CASE_UNIFORM}, ConnectionPointType.${CONNECTION_POINT_TYPE});
30
+ `;
31
+ const BlockGetShaderBindingVars = ` const ${CAMEL_CASE_UNIFORM} = this._confirmRuntimeDataSupplied(this.${CAMEL_CASE_UNIFORM});`;
32
+
33
+ const FileTemplate = `/* eslint-disable prettier/prettier */
34
+ // ************************************************************
35
+ // Note: this file is auto-generated, do not modify it directly
36
+ // ************************************************************
37
+
38
+ // It was generated by convertGlslIntoBlock() from
39
+ // an annotated .glsl file. Modify the .glsl file to make changes
40
+ // to the block. This file will get overwritten when the build
41
+ // is run or during a watch when the .glsl file is updated.
42
+
43
+ import type { Effect } from "@babylonjs/core/Materials/effect";
44
+
45
+ import {
46
+ ShaderBinding,
47
+ type RuntimeData,
48
+ ConnectionPointType,
49
+ type SmartFilter,
50
+ ShaderBlock,
51
+ type ShaderProgram,
52
+ } from "@babylonjs/smart-filters";${SHADER_PROGRAM}
53
+ /**
54
+ * The shader binding for the ${BLOCK_NAME}, used by the runtime
55
+ */
56
+ class ${BLOCK_NAME}ShaderBinding extends ShaderBinding {
57
+ ${SHADER_BINDING_PRIVATE_VARIABLES}
58
+
59
+ /**
60
+ * Creates a new shader binding instance for the block.
61
+ ${SHADER_BINDING_CTOR_DOCSTRING_PARAMS}
62
+ */
63
+ constructor(
64
+ ${SHADER_BINDING_CTOR_PARAMS}
65
+ ) {
66
+ super();
67
+ ${SHADER_BINDING_CTOR}
68
+ }
69
+
70
+ /**
71
+ * Binds all the required data to the shader when rendering.
72
+ * @param effect - defines the effect to bind the data to
73
+ */
74
+ public override bind(effect: Effect): void {
75
+ ${SHADER_BINDING_BIND}
76
+ }
77
+ }
78
+
79
+ /**
80
+ * The implementation of the ${BLOCK_NAME}
81
+ */
82
+ export class ${BLOCK_NAME} extends ShaderBlock {
83
+ /**
84
+ * The class name of the block.
85
+ */
86
+ public static override ClassName = "${BLOCK_NAME}";
87
+
88
+ /**
89
+ * The namespace of the block.
90
+ */
91
+ public static override Namespace = "${NAMESPACE}";
92
+
93
+ ${BLOCK_INPUT_PROPERTIES}
94
+ /**
95
+ * The shader program (vertex and fragment code) to use to render the block
96
+ */
97
+ public static override ShaderCode = shaderProgram;
98
+
99
+ /**
100
+ * Instantiates a new ${BLOCK_NAME}.
101
+ * @param smartFilter - The smart filter this block belongs to
102
+ * @param name - The friendly name of the block
103
+ */
104
+ constructor(smartFilter: SmartFilter, name: string) {
105
+ super(smartFilter, name);
106
+ }
107
+
108
+ /**
109
+ * Get the class instance that binds all the required data to the shader (effect) when rendering.
110
+ * @returns The class instance that binds the data to the effect
111
+ */
112
+ public getShaderBinding(): ShaderBinding {
113
+ ${BLOCK_GET_SHADER_BINDING_VARS}
114
+
115
+ return new ${BLOCK_NAME}ShaderBinding(${BLOCK_GET_SHADER_PARAM_LIST});
116
+ }
117
+ }
118
+
119
+ `;
120
+
121
+ /**
122
+ * Converts a single shader to a .ts file which exports a Smart Filter block
123
+ * @param fragmentShaderPath - The path to the fragment file for the shader
124
+ * @param importPath - The path to import the ShaderProgram type from
125
+ */
126
+ export function convertGlslIntoBlock(fragmentShaderPath: string, importPath: string): void {
127
+ const { shaderProgramCode, fragmentShaderInfo } = extractShaderProgramFromGlsl(
128
+ fragmentShaderPath,
129
+ importPath,
130
+ false,
131
+ false
132
+ );
133
+
134
+ // Validation
135
+ if (!fragmentShaderInfo.blockType) {
136
+ throw new Error("The glsl file must contain a header comment with a smartFilterBlockType value");
137
+ }
138
+
139
+ // Generate shader binding private variables
140
+ const shaderBindingPrivateVariables = fragmentShaderInfo.uniforms.map((uniform) => {
141
+ return ShaderBindingPrivateVariablesTemplate.replace(CAMEL_CASE_UNIFORM, uniform.name).replace(
142
+ CONNECTION_POINT_TYPE,
143
+ getConnectionPointTypeString(uniform.type)
144
+ );
145
+ });
146
+
147
+ // Generate the shader binding constructor docstring params
148
+ const shaderBindingCtorDocstringParams = fragmentShaderInfo.uniforms.map((uniform) => {
149
+ return ShaderBindingCtorDocstringParams.replace(new RegExp(CAMEL_CASE_UNIFORM, "g"), uniform.name);
150
+ });
151
+
152
+ // Generate the shader binding constructor params
153
+ const shaderBindingCtorParams = fragmentShaderInfo.uniforms.map((uniform) => {
154
+ return ShaderBindingCtorParams.replace(CAMEL_CASE_UNIFORM, uniform.name).replace(
155
+ CONNECTION_POINT_TYPE,
156
+ getConnectionPointTypeString(uniform.type)
157
+ );
158
+ });
159
+
160
+ // Generate the shader binding constructor
161
+ const shaderBindingCtor = fragmentShaderInfo.uniforms.map((uniform) => {
162
+ return ShaderBindingCtor.replace(new RegExp(CAMEL_CASE_UNIFORM, "g"), uniform.name);
163
+ });
164
+
165
+ // Generate the shader binding bind
166
+ const shaderBindingBind = fragmentShaderInfo.uniforms.map((uniform) => {
167
+ return ShaderBindingBind.replace(new RegExp(CAMEL_CASE_UNIFORM, "g"), uniform.name).replace(
168
+ EFFECT_SETTER,
169
+ getEffectSetter(uniform.type)
170
+ );
171
+ });
172
+
173
+ // Generate the block input properties
174
+ const blockInputProperties = fragmentShaderInfo.uniforms.map((uniform) => {
175
+ return BlockInputProperties.replace(new RegExp(CAMEL_CASE_UNIFORM, "g"), uniform.name).replace(
176
+ CONNECTION_POINT_TYPE,
177
+ getConnectionPointTypeString(uniform.type)
178
+ );
179
+ });
180
+
181
+ // Generate the block get shader binding vars
182
+ const blockGetShaderBindingVars = fragmentShaderInfo.uniforms.map((uniform) => {
183
+ return BlockGetShaderBindingVars.replace(new RegExp(CAMEL_CASE_UNIFORM, "g"), uniform.name);
184
+ });
185
+
186
+ // Generate the block get shader param list
187
+ const blockGetShaderParamList = fragmentShaderInfo.uniforms.map((uniform) => {
188
+ return uniform.name;
189
+ });
190
+
191
+ // Generate final contents
192
+ const finalContents = FileTemplate.replace(SHADER_PROGRAM, shaderProgramCode)
193
+ .replace(new RegExp(BLOCK_NAME, "g"), fragmentShaderInfo.blockType)
194
+ .replace(NAMESPACE, fragmentShaderInfo.namespace || "Other")
195
+ .replace(SHADER_BINDING_PRIVATE_VARIABLES, shaderBindingPrivateVariables.join("\n"))
196
+ .replace(SHADER_BINDING_CTOR_DOCSTRING_PARAMS, shaderBindingCtorDocstringParams.join("\n"))
197
+ .replace(SHADER_BINDING_CTOR_PARAMS, shaderBindingCtorParams.join(",\n"))
198
+ .replace(SHADER_BINDING_CTOR, shaderBindingCtor.join("\n"))
199
+ .replace(SHADER_BINDING_BIND, shaderBindingBind.join("\n"))
200
+ .replace(BLOCK_INPUT_PROPERTIES, blockInputProperties.join("\n"))
201
+ .replace(BLOCK_GET_SHADER_BINDING_VARS, blockGetShaderBindingVars.join("\n"))
202
+ .replace(BLOCK_GET_SHADER_PARAM_LIST, blockGetShaderParamList.join(","));
203
+
204
+ // Write the block class TS file
205
+ const outputFullPathAndFileName = fragmentShaderPath.replace(".glsl", ".ts");
206
+ fs.writeFileSync(outputFullPathAndFileName, finalContents);
207
+ }
208
+
209
+ /**
210
+ * Get the string representation of a connection point type
211
+ * @param type - The connection point type
212
+ * @returns - The string representation of the connection point type
213
+ */
214
+ function getConnectionPointTypeString(type: ConnectionPointType): string {
215
+ switch (type) {
216
+ case ConnectionPointType.Float:
217
+ return "Float";
218
+ case ConnectionPointType.Texture:
219
+ return "Texture";
220
+ case ConnectionPointType.Color3:
221
+ return "Color3";
222
+ case ConnectionPointType.Color4:
223
+ return "Color4";
224
+ case ConnectionPointType.Vector2:
225
+ return "Vector2";
226
+ case ConnectionPointType.Boolean:
227
+ return "Boolean";
228
+ }
229
+ }
230
+
231
+ /**
232
+ * Get the effect setter for a connection point type
233
+ * @param type - The connection point type
234
+ * @returns - The effect setter for the connection point type
235
+ */
236
+ function getEffectSetter(type: ConnectionPointType): string {
237
+ switch (type) {
238
+ case ConnectionPointType.Float:
239
+ return "setFloat";
240
+ case ConnectionPointType.Texture:
241
+ return "setTexture";
242
+ case ConnectionPointType.Color3:
243
+ return "setColor3";
244
+ case ConnectionPointType.Color4:
245
+ return "setDirectColor4";
246
+ case ConnectionPointType.Vector2:
247
+ return "setVector2";
248
+ case ConnectionPointType.Boolean:
249
+ return "setBool";
250
+ }
251
+ }
@@ -1,6 +1,6 @@
1
1
  import * as fs from "fs";
2
- import * as path from "path";
3
- import { parseFragmentShader } from "./shaderConverter.js";
2
+ import { Logger } from "@babylonjs/core/Misc/logger.js";
3
+ import { parseFragmentShader, type FragmentShaderInfo } from "./shaderConverter.js";
4
4
 
5
5
  const TYPE_IMPORT_PATH = "@TYPE_IMPORT_PATH@";
6
6
  const VERTEX_SHADER = "@VERTEX_SHADER@";
@@ -13,6 +13,8 @@ const FUNCTIONS = "@FUNCTIONS@";
13
13
  const FUNCTION_NAME = "@FUNCTION_NAME@";
14
14
  const FUNCTION_CODE = "@FUNCTION_CODE@";
15
15
  const UNIFORM_NAMES = "@UNIFORM_NAMES@";
16
+ const EXPORT = "@EXPORT_SHADER_PROGRAM@";
17
+ const IMPORTS = "@IMPORT@";
16
18
 
17
19
  const ConstsTemplate = `
18
20
  const: \`${CONSTS_VALUE}\`,`;
@@ -29,12 +31,13 @@ const CodeLinePrefix = " ";
29
31
  const UniformLinePrefix = " ";
30
32
  const ConstLinePrefix = " ";
31
33
 
32
- const ShaderTemplate = `import type { ShaderProgram } from "${TYPE_IMPORT_PATH}";
34
+ const ImportTemplate = `import type { ShaderProgram } from "${TYPE_IMPORT_PATH}";`;
35
+ const ShaderTemplate = `${IMPORTS}
33
36
 
34
37
  /**
35
38
  * The shader program for the block.
36
39
  */
37
- export const shaderProgram: ShaderProgram = {
40
+ ${EXPORT}const shaderProgram: ShaderProgram = {
38
41
  vertex: ${VERTEX_SHADER},
39
42
  fragment: {
40
43
  uniform: \`${UNIFORMS}\`,${CONSTS_PROPERTY}
@@ -49,7 +52,7 @@ export const shaderProgram: ShaderProgram = {
49
52
  * The uniform names for this shader, to be used in the shader binding so
50
53
  * that the names are always in sync.
51
54
  */
52
- export const uniforms = {
55
+ ${EXPORT}const uniforms = {
53
56
  ${UNIFORM_NAMES}
54
57
  };
55
58
  `;
@@ -57,29 +60,63 @@ ${UNIFORM_NAMES}
57
60
  const UniformNameLinePrefix = " ";
58
61
 
59
62
  /**
60
- * Converts a single shader to a .ts file which can be imported by a hardcoded block
63
+ * Converts a single shader to a .ts file which exports a ShaderProgram which can be imported by a hardcoded block
61
64
  * @param fragmentShaderPath - The path to the fragment file for the shader
62
65
  * @param importPath - The path to import the ShaderProgram type from
63
66
  */
64
- export function convertShaderForHardcodedBlock(fragmentShaderPath: string, importPath: string): void {
65
- console.log(`Processing fragment shader: ${fragmentShaderPath}`);
67
+ export function convertGlslIntoShaderProgram(fragmentShaderPath: string, importPath: string): void {
68
+ const { shaderProgramCode } = extractShaderProgramFromGlsl(fragmentShaderPath, importPath, true, true);
69
+ const shaderFile = fragmentShaderPath.replace(".glsl", ".ts");
70
+ fs.writeFileSync(shaderFile, shaderProgramCode);
71
+ }
66
72
 
73
+ /**
74
+ * Extracts the shader program from a glsl file(s) and returns it as a string which can be written to a .ts file
75
+ * @param fragmentShaderPath - The path to the fragment file for the shader
76
+ * @param importPath - The path to import the ShaderProgram type from
77
+ * @param exportObjects - Whether to export the shaderProgram and uniforms objects
78
+ * @param includeImports - Whether to include the imports in the output
79
+ * @returns The string to write to the .ts file
80
+ */
81
+ export function extractShaderProgramFromGlsl(
82
+ fragmentShaderPath: string,
83
+ importPath: string,
84
+ exportObjects: boolean,
85
+ includeImports: boolean
86
+ ): {
87
+ /**
88
+ * The shader program code
89
+ */
90
+ shaderProgramCode: string;
91
+
92
+ /**
93
+ * The FragmentShaderInfo
94
+ */
95
+ fragmentShaderInfo: FragmentShaderInfo;
96
+ } {
67
97
  // See if there is a corresponding vertex shader
68
98
  let vertexShader: string | undefined = undefined;
69
- const vertexShaderPath = fragmentShaderPath.replace(".fragment.glsl", ".vertex.glsl");
99
+ let extensionToFind: string;
100
+ if (fragmentShaderPath.endsWith(".block.glsl")) {
101
+ extensionToFind = ".block.glsl";
102
+ } else if (fragmentShaderPath.endsWith(".fragment.glsl")) {
103
+ extensionToFind = ".fragment.glsl";
104
+ } else {
105
+ throw new Error("The shader file must end with .fragment.glsl or .block.glsl");
106
+ }
107
+ const vertexShaderPath = fragmentShaderPath.replace(extensionToFind, ".vertex.glsl");
70
108
  if (fs.existsSync(vertexShaderPath)) {
71
109
  vertexShader = fs.readFileSync(vertexShaderPath, "utf8");
72
110
  }
73
111
  if (vertexShader) {
74
- console.log("Found vertex shader");
112
+ Logger.Log("Found vertex shader");
75
113
  }
76
114
 
77
115
  // Read the fragment shader
78
116
  const fragmentShader = fs.readFileSync(fragmentShaderPath, "utf8");
79
117
  const fragmentShaderInfo = parseFragmentShader(fragmentShader);
80
118
 
81
- // Write the shader TS file
82
- const shaderFile = fragmentShaderPath.replace(".fragment.glsl", ".shader.ts");
119
+ // Generate the shader program code
83
120
  const functionsSection: string[] = [];
84
121
  for (const shaderFunction of fragmentShaderInfo.shaderCode.functions) {
85
122
  functionsSection.push(
@@ -89,11 +126,12 @@ export function convertShaderForHardcodedBlock(fragmentShaderPath: string, impor
89
126
  )
90
127
  );
91
128
  }
129
+ const imports = includeImports ? ImportTemplate.replace(TYPE_IMPORT_PATH, importPath) : "";
92
130
  const finalContents = ShaderTemplate.replace(VERTEX_SHADER, vertexShader ? `\`${vertexShader}\`` : "undefined")
93
- .replace(TYPE_IMPORT_PATH, importPath)
131
+ .replace(IMPORTS, imports)
94
132
  .replace(UNIFORMS, "\n" + addLinePrefixes(fragmentShaderInfo.shaderCode.uniform || "", UniformLinePrefix))
95
133
  .replace(MAIN_FUNCTION_NAME, fragmentShaderInfo.shaderCode.mainFunctionName)
96
- .replace(MAIN_INPUT_NAME, fragmentShaderInfo.shaderCode.mainInputTexture || '""')
134
+ .replace(MAIN_INPUT_NAME, fragmentShaderInfo.shaderCode.mainInputTexture || "")
97
135
  .replace(
98
136
  CONSTS_PROPERTY,
99
137
  fragmentShaderInfo.shaderCode.const
@@ -110,29 +148,13 @@ export function convertShaderForHardcodedBlock(fragmentShaderPath: string, impor
110
148
  fragmentShaderInfo.uniforms.map((u) => `${u.name}: "${u.name}",`).join("\n"),
111
149
  UniformNameLinePrefix
112
150
  )
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
- );
151
+ )
152
+ .replace(new RegExp(EXPORT, "g"), exportObjects ? "export " : "");
131
153
 
132
- // Convert all shaders
133
- for (const fragmentShaderFile of fragmentShaderFiles) {
134
- convertShaderForHardcodedBlock(path.join(fragmentShaderFile.path, fragmentShaderFile.name), importPath);
135
- }
154
+ return {
155
+ shaderProgramCode: finalContents,
156
+ fragmentShaderInfo,
157
+ };
136
158
  }
137
159
 
138
160
  /**
@@ -0,0 +1,43 @@
1
+ import * as fs from "fs";
2
+ import * as path from "path";
3
+ import { Logger } from "@babylonjs/core/Misc/logger.js";
4
+ import { convertGlslIntoShaderProgram } from "./convertGlslIntoShaderProgram.js";
5
+ import { convertGlslIntoBlock } from "./convertGlslIntoBlock.js";
6
+
7
+ /**
8
+ * Converts all GLSL files in a path into blocks for use in the build system.
9
+ * @param shaderPath - The path to the .glsl files to convert.
10
+ * @param importPath - The path to import the ShaderProgram type from.
11
+ */
12
+ export function convertShaders(shaderPath: string, importPath: string) {
13
+ // Get all files in the path
14
+ const allFiles = fs.readdirSync(shaderPath, { withFileTypes: true, recursive: true });
15
+
16
+ // Find all shaders (files with .fragment.glsl or .block.glsl extensions)
17
+ const shaderFiles = allFiles.filter(
18
+ (file) => file.isFile() && (file.name.endsWith(".fragment.glsl") || file.name.endsWith(".block.glsl"))
19
+ );
20
+
21
+ // Convert all shaders
22
+ for (const shaderFile of shaderFiles) {
23
+ const fullPathAndFileName = path.join(shaderFile.path, shaderFile.name);
24
+ convertShader(fullPathAndFileName, importPath);
25
+ }
26
+ }
27
+
28
+ /**
29
+ * Converts a single GLSL file into a block class or a ShaderProgram for use in the build system.
30
+ * @param fullPathAndFileName - The full path and file name of the .glsl file to convert.
31
+ * @param importPath - The path to import the ShaderProgram type from.
32
+ */
33
+ export function convertShader(fullPathAndFileName: string, importPath: string): void {
34
+ Logger.Log(`\nProcessing shader: ${fullPathAndFileName}`);
35
+
36
+ if (fullPathAndFileName.endsWith(".fragment.glsl")) {
37
+ Logger.Log("Generating a .ts file that exports a ShaderProgram.");
38
+ convertGlslIntoShaderProgram(fullPathAndFileName, importPath);
39
+ } else if (fullPathAndFileName.endsWith(".block.glsl")) {
40
+ Logger.Log("Generating a .ts file that exports the block as a class.");
41
+ convertGlslIntoBlock(fullPathAndFileName, importPath);
42
+ }
43
+ }
@@ -1,3 +1,4 @@
1
+ /* eslint-disable no-console */
1
2
  import type { Nullable } from "@babylonjs/core/types";
2
3
  import type { ExecException } from "child_process";
3
4
 
@@ -1,5 +1,7 @@
1
1
  import type { Nullable } from "@babylonjs/core/types";
2
+ import { Logger } from "@babylonjs/core/Misc/logger.js";
2
3
  import type { ShaderCode, ShaderFunction } from "./shaderCode.types";
4
+ import { ConnectionPointType } from "../../connection/connectionPointType.js";
3
5
 
4
6
  const GetFunctionNamesRegEx = /\S*\w+\s+(\w+)\s*\(/g;
5
7
 
@@ -31,7 +33,7 @@ export type UniformMetadata = {
31
33
  /**
32
34
  * The type string of the uniform
33
35
  */
34
- type: string;
36
+ type: ConnectionPointType;
35
37
 
36
38
  /**
37
39
  * Optional properties of the uniform
@@ -106,9 +108,34 @@ export function parseFragmentShader(fragmentShader: string): FragmentShaderInfo
106
108
  throw new Error(`Uniforms must have a name: '${uniformLine}'`);
107
109
  }
108
110
 
111
+ // Convert to ConnectionPointType
112
+ let type: ConnectionPointType;
113
+ switch (uniformTypeString) {
114
+ case "float":
115
+ type = ConnectionPointType.Float;
116
+ break;
117
+ case "sampler2D":
118
+ type = ConnectionPointType.Texture;
119
+ break;
120
+ case "vec3":
121
+ type = ConnectionPointType.Color3;
122
+ break;
123
+ case "vec4":
124
+ type = ConnectionPointType.Color4;
125
+ break;
126
+ case "bool":
127
+ type = ConnectionPointType.Boolean;
128
+ break;
129
+ case "vec2":
130
+ type = ConnectionPointType.Vector2;
131
+ break;
132
+ default:
133
+ throw new Error(`Unsupported uniform type: '${uniformTypeString}'`);
134
+ }
135
+
109
136
  uniforms.push({
110
137
  name: uniformName,
111
- type: uniformTypeString,
138
+ type,
112
139
  properties: annotationJSON ? JSON.parse(annotationJSON.replace("//", "").trim()) : undefined,
113
140
  });
114
141
 
@@ -123,13 +150,13 @@ export function parseFragmentShader(fragmentShader: string): FragmentShaderInfo
123
150
  // Collect uniform, const, and function names which need to be decorated
124
151
  // eslint-disable-next-line prettier/prettier
125
152
  const uniformNames = uniforms.map((uniform) => uniform.name);
126
- console.log(`Uniforms found: ${JSON.stringify(uniforms)}`);
153
+ Logger.Log(`Uniforms found: ${JSON.stringify(uniforms)}`);
127
154
  const consts = [...fragmentShader.matchAll(/\S*const\s+\w*\s+(\w*)\s*=.*;/g)].map((match) => match[1]);
128
- console.log(`Consts found: ${JSON.stringify(consts)}`);
155
+ Logger.Log(`Consts found: ${JSON.stringify(consts)}`);
129
156
  const functionNames = [...fragmentShaderWithNoFunctionBodies.matchAll(GetFunctionNamesRegEx)].map(
130
157
  (match) => match[1]
131
158
  );
132
- console.log(`Functions found: ${JSON.stringify(functionNames)}`);
159
+ Logger.Log(`Functions found: ${JSON.stringify(functionNames)}`);
133
160
 
134
161
  // Decorate the uniforms, consts, and functions
135
162
  const symbolsToDecorate = [...uniformNames, ...consts, ...functionNames];
@@ -138,7 +165,7 @@ export function parseFragmentShader(fragmentShader: string): FragmentShaderInfo
138
165
  const regex = new RegExp(`(?<=\\W+)${symbol}(?=\\W+)`, "gs");
139
166
  fragmentShaderWithRenamedSymbols = fragmentShaderWithRenamedSymbols.replace(regex, `_${symbol}_`);
140
167
  }
141
- console.log(`${symbolsToDecorate.length} symbol(s) renamed`);
168
+ Logger.Log(`${symbolsToDecorate.length} symbol(s) renamed`);
142
169
 
143
170
  // Extract all the uniforms
144
171
  const finalUniforms = [...fragmentShaderWithRenamedSymbols.matchAll(/^\s*(uniform\s.*)/gm)].map(
@@ -308,7 +335,7 @@ function removeFunctionBodies(input: string): string {
308
335
  }
309
336
 
310
337
  if (depth !== 0) {
311
- console.error("Unbalanced curly braces in shader code");
338
+ Logger.Error("Unbalanced curly braces in shader code");
312
339
  }
313
340
 
314
341
  return output;
@@ -1,3 +1,4 @@
1
+ /* eslint-disable no-console */
1
2
  import * as fs from "fs";
2
3
  import { exec, type ExecException } from "child_process";
3
4
  import { compareVersions, determineVersion, getNpmVersion, type VersionType } from "./determineVersion.js";