@onerjs/smart-filters 8.31.5 → 8.31.7

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 (57) hide show
  1. package/dist/blockFoundation/customShaderBlock.d.ts +16 -2
  2. package/dist/blockFoundation/customShaderBlock.d.ts.map +1 -1
  3. package/dist/blockFoundation/customShaderBlock.js +69 -5
  4. package/dist/blockFoundation/customShaderBlock.js.map +1 -1
  5. package/dist/blockFoundation/customShaderBlock.serializer.d.ts +22 -0
  6. package/dist/blockFoundation/customShaderBlock.serializer.d.ts.map +1 -0
  7. package/dist/blockFoundation/customShaderBlock.serializer.js +32 -0
  8. package/dist/blockFoundation/customShaderBlock.serializer.js.map +1 -0
  9. package/dist/blockFoundation/disableableShaderBlock.d.ts +5 -0
  10. package/dist/blockFoundation/disableableShaderBlock.d.ts.map +1 -1
  11. package/dist/blockFoundation/disableableShaderBlock.js +18 -11
  12. package/dist/blockFoundation/disableableShaderBlock.js.map +1 -1
  13. package/dist/editorUtils/editableInPropertyPage.d.ts +5 -0
  14. package/dist/editorUtils/editableInPropertyPage.d.ts.map +1 -1
  15. package/dist/editorUtils/editableInPropertyPage.js +11 -2
  16. package/dist/editorUtils/editableInPropertyPage.js.map +1 -1
  17. package/dist/optimization/smartFilterOptimizer.d.ts +11 -0
  18. package/dist/optimization/smartFilterOptimizer.d.ts.map +1 -1
  19. package/dist/optimization/smartFilterOptimizer.js +28 -14
  20. package/dist/optimization/smartFilterOptimizer.js.map +1 -1
  21. package/dist/serialization/importCustomBlockDefinition.js +1 -0
  22. package/dist/serialization/importCustomBlockDefinition.js.map +1 -1
  23. package/dist/serialization/smartFilterSerializer.d.ts.map +1 -1
  24. package/dist/serialization/smartFilterSerializer.js +6 -3
  25. package/dist/serialization/smartFilterSerializer.js.map +1 -1
  26. package/dist/serialization/v1/shaderBlockSerialization.types.d.ts +37 -0
  27. package/dist/serialization/v1/shaderBlockSerialization.types.d.ts.map +1 -1
  28. package/dist/utils/buildTools/shaderCode.types.d.ts +5 -0
  29. package/dist/utils/buildTools/shaderCode.types.d.ts.map +1 -1
  30. package/dist/utils/buildTools/shaderConverter.d.ts +10 -4
  31. package/dist/utils/buildTools/shaderConverter.d.ts.map +1 -1
  32. package/dist/utils/buildTools/shaderConverter.js +58 -2
  33. package/dist/utils/buildTools/shaderConverter.js.map +1 -1
  34. package/dist/utils/index.d.ts +1 -1
  35. package/dist/utils/index.d.ts.map +1 -1
  36. package/dist/utils/index.js +1 -0
  37. package/dist/utils/index.js.map +1 -1
  38. package/dist/utils/shaderCodeUtils.d.ts +6 -0
  39. package/dist/utils/shaderCodeUtils.d.ts.map +1 -1
  40. package/dist/utils/shaderCodeUtils.js +14 -1
  41. package/dist/utils/shaderCodeUtils.js.map +1 -1
  42. package/dist/version.d.ts +1 -1
  43. package/dist/version.js +1 -1
  44. package/package.json +1 -1
  45. package/src/blockFoundation/customShaderBlock.serializer.ts +51 -0
  46. package/src/blockFoundation/customShaderBlock.ts +80 -6
  47. package/src/blockFoundation/disableableShaderBlock.ts +18 -9
  48. package/src/editorUtils/editableInPropertyPage.ts +16 -2
  49. package/src/optimization/smartFilterOptimizer.ts +30 -16
  50. package/src/serialization/importCustomBlockDefinition.ts +1 -0
  51. package/src/serialization/smartFilterSerializer.ts +6 -3
  52. package/src/serialization/v1/shaderBlockSerialization.types.ts +42 -0
  53. package/src/utils/buildTools/shaderCode.types.ts +65 -59
  54. package/src/utils/buildTools/shaderConverter.ts +96 -6
  55. package/src/utils/index.ts +1 -1
  56. package/src/utils/shaderCodeUtils.ts +16 -1
  57. package/src/version.ts +1 -1
@@ -0,0 +1,51 @@
1
+ import type { ISerializedBlockV1, SerializeBlockV1 } from "../serialization/v1/smartFilterSerialization.types.js";
2
+ import type { BaseBlock } from "./baseBlock.js";
3
+ import { CustomShaderBlock } from "./customShaderBlock.js";
4
+
5
+ /**
6
+ * Data for a dynamic property on a CustomShaderBlock
7
+ */
8
+ export type CustomShaderBlockData = {
9
+ /**
10
+ * The custom properties of the CustomShaderBlock
11
+ */
12
+ customProperties: CustomPropertyData[];
13
+ };
14
+
15
+ type CustomPropertyData = {
16
+ name: string;
17
+ value: any;
18
+ };
19
+
20
+ /**
21
+ * Serializes a CustomShaderBlock to V1 serialized data.
22
+ * @param block - The block to serialize
23
+ * @returns The serialized block
24
+ */
25
+ export const CustomShaderBlockSerializer: SerializeBlockV1 = (block: BaseBlock): ISerializedBlockV1 => {
26
+ if (block.getClassName() !== CustomShaderBlock.ClassName) {
27
+ throw new Error("Was asked to serialize an unrecognized block type");
28
+ }
29
+ const customShaderBlock = block as CustomShaderBlock;
30
+
31
+ let data: CustomShaderBlockData | undefined;
32
+ const dynamicPropertyNames = customShaderBlock.dynamicPropertyNames;
33
+ if (dynamicPropertyNames.length > 0) {
34
+ data = {
35
+ customProperties: dynamicPropertyNames.map((propertyName) => ({
36
+ name: propertyName,
37
+ value: (customShaderBlock as any)[propertyName],
38
+ })),
39
+ };
40
+ }
41
+
42
+ return {
43
+ name: customShaderBlock.name,
44
+ uniqueId: customShaderBlock.uniqueId,
45
+ blockType: customShaderBlock.blockType,
46
+ namespace: customShaderBlock.namespace,
47
+ comments: customShaderBlock.comments,
48
+ data,
49
+ outputTextureOptions: customShaderBlock.outputTextureOptions,
50
+ };
51
+ };
@@ -4,12 +4,14 @@ import { ConnectionPointType, type ConnectionPointValue } from "../connection/co
4
4
  import { ShaderBinding } from "../runtime/shaderRuntime.js";
5
5
  import { CreateStrongRef } from "../runtime/strongRef.js";
6
6
  import type { SerializedShaderBlockDefinition } from "../serialization/serializedShaderBlockDefinition.js";
7
- import type { SerializedInputConnectionPointV1 } from "../serialization/v1/shaderBlockSerialization.types.js";
7
+ import type { SerializedInputConnectionPointV1, ConstPropertyMetadata } from "../serialization/v1/shaderBlockSerialization.types.js";
8
8
  import type { SmartFilter } from "../smartFilter.js";
9
- import type { ShaderProgram } from "../utils/shaderCodeUtils.js";
9
+ import { CloneShaderProgram, type ShaderProgram } from "../utils/shaderCodeUtils.js";
10
10
  import { ShaderBlock } from "./shaderBlock.js";
11
11
  import type { RuntimeData } from "../connection/connectionPoint.js";
12
12
  import type { Nullable } from "core/types.js";
13
+ import { EditableInPropertyPage, type IEditablePropertyOption, PropertyTypeForEdition } from "../editorUtils/editableInPropertyPage.js";
14
+ import type { CustomShaderBlockData } from "./customShaderBlock.serializer.js";
13
15
 
14
16
  /**
15
17
  * The binding for a CustomShaderBlock
@@ -95,20 +97,33 @@ export class CustomShaderBlock extends ShaderBlock {
95
97
  * @param smartFilter - The smart filter this block belongs to
96
98
  * @param name - Defines the name of the block
97
99
  * @param blockDefinition - The serialized block definition
100
+ * @param data - The data property from the serialized block, if applicable
98
101
  * @returns The deserialized CustomShaderBlock instance
99
102
  */
100
- public static Create(smartFilter: SmartFilter, name: string, blockDefinition: SerializedShaderBlockDefinition): CustomShaderBlock {
103
+ public static Create(smartFilter: SmartFilter, name: string, blockDefinition: SerializedShaderBlockDefinition, data?: any): CustomShaderBlock {
101
104
  // When a new version of SerializedBlockDefinition is created, this function should be updated to handle the new properties.
102
105
 
103
- return new CustomShaderBlock(
106
+ const newBlock = new CustomShaderBlock(
104
107
  smartFilter,
105
108
  name,
106
109
  blockDefinition.disableOptimization,
107
110
  blockDefinition.blockType,
108
111
  blockDefinition.namespace,
109
112
  blockDefinition.inputConnectionPoints,
113
+ blockDefinition.fragmentConstProperties || [],
110
114
  blockDefinition.shaderProgram
111
115
  );
116
+
117
+ if (data && (data as CustomShaderBlockData).customProperties) {
118
+ const customProperties = (data as CustomShaderBlockData).customProperties;
119
+ for (const customProperty of customProperties) {
120
+ if (newBlock.dynamicPropertyNames.indexOf(customProperty.name) !== -1) {
121
+ (newBlock as any)[customProperty.name] = customProperty.value;
122
+ }
123
+ }
124
+ }
125
+
126
+ return newBlock;
112
127
  }
113
128
 
114
129
  /**
@@ -119,8 +134,16 @@ export class CustomShaderBlock extends ShaderBlock {
119
134
  private readonly _shaderProgram: ShaderProgram;
120
135
  private readonly _blockType: string;
121
136
  private readonly _namespace: Nullable<string>;
137
+ private readonly _fragmentConstProperties: ConstPropertyMetadata[];
122
138
  private _autoBoundInputs: Nullable<SerializedInputConnectionPointV1[]> = null;
123
139
 
140
+ /**
141
+ * A list of the names of the properties added to this instance of the block, for example,
142
+ * fragment const properties.
143
+ *
144
+ */
145
+ public readonly dynamicPropertyNames: string[] = [];
146
+
124
147
  /**
125
148
  * The type of the block - used when serializing / deserializing the block, and in the editor.
126
149
  */
@@ -144,6 +167,7 @@ export class CustomShaderBlock extends ShaderBlock {
144
167
  * @param blockType - The type of the block
145
168
  * @param namespace - The namespace of the block
146
169
  * @param inputConnectionPoints - The input connection points of the
170
+ * @param fragmentConstProperties - The define properties for the block
147
171
  * @param shaderProgram - The shader program for the block
148
172
  */
149
173
  private constructor(
@@ -153,17 +177,22 @@ export class CustomShaderBlock extends ShaderBlock {
153
177
  blockType: string,
154
178
  namespace: Nullable<string>,
155
179
  inputConnectionPoints: SerializedInputConnectionPointV1[],
180
+ fragmentConstProperties: ConstPropertyMetadata[],
156
181
  shaderProgram: ShaderProgram
157
182
  ) {
158
183
  super(smartFilter, name, disableOptimization);
159
184
  this._blockType = blockType;
160
185
  this._namespace = namespace;
186
+ this._shaderProgram = shaderProgram;
187
+ this._fragmentConstProperties = fragmentConstProperties;
161
188
 
162
189
  for (const input of inputConnectionPoints) {
163
190
  this._registerSerializedInputConnectionPointV1(input);
164
191
  }
165
192
 
166
- this._shaderProgram = shaderProgram;
193
+ for (const constProperty of fragmentConstProperties) {
194
+ this._createConstProperty(constProperty);
195
+ }
167
196
  }
168
197
 
169
198
  /**
@@ -171,7 +200,52 @@ export class CustomShaderBlock extends ShaderBlock {
171
200
  * @returns The shader program to use to render the block
172
201
  */
173
202
  public override getShaderProgram() {
174
- return this._shaderProgram;
203
+ if (this._fragmentConstProperties.length === 0) {
204
+ return this._shaderProgram;
205
+ } else {
206
+ // Make a copy of the shader program and append const properties to the fragment shader consts
207
+ const shaderProgramForThisInstance = CloneShaderProgram(this._shaderProgram);
208
+ shaderProgramForThisInstance.fragment.constPerInstance =
209
+ this._fragmentConstProperties
210
+ .map((property) => {
211
+ switch (property.type) {
212
+ case "float": {
213
+ const value = (this as any)[property.friendlyName] as number;
214
+ const valueStr = Number.isInteger(value) ? value.toString() + "." : value.toString();
215
+ return `const float ${property.name} = ${valueStr};`;
216
+ }
217
+ }
218
+ })
219
+ .join("\n") + "\n";
220
+
221
+ return shaderProgramForThisInstance;
222
+ }
223
+ }
224
+
225
+ /**
226
+ * Creates a dynamic property for the supplied const property with EditableInPropertyPage decorator.
227
+ * @param constProperty - The const property metadata
228
+ */
229
+ private _createConstProperty(constProperty: ConstPropertyMetadata): void {
230
+ // Create the property and assign the default value
231
+ (this as any)[constProperty.friendlyName] = constProperty.defaultValue;
232
+ this.dynamicPropertyNames.push(constProperty.friendlyName);
233
+
234
+ // Use the EditableInPropertyPage decorator to make the property editable in the Smart Filters Editor
235
+ const editablePropertyOptions: IEditablePropertyOption = {
236
+ notifiers: { rebuild: true },
237
+ blockType: this._blockType,
238
+ };
239
+ if (constProperty.options) {
240
+ editablePropertyOptions.options = Object.keys(constProperty.options).map((key) => {
241
+ return { label: key, value: (constProperty.options as any)[key] };
242
+ });
243
+ }
244
+
245
+ const propertyType: PropertyTypeForEdition = constProperty.options ? PropertyTypeForEdition.List : PropertyTypeForEdition.Float;
246
+
247
+ const decoratorApplier = EditableInPropertyPage(constProperty.friendlyName, propertyType, "PROPERTIES", editablePropertyOptions);
248
+ decoratorApplier(this, constProperty.friendlyName);
175
249
  }
176
250
 
177
251
  /**
@@ -65,27 +65,36 @@ export abstract class DisableableShaderBlock extends ShaderBlock implements IDis
65
65
  }
66
66
 
67
67
  /**
68
- * Instantiates a new block.
69
- * @param smartFilter - Defines the smart filter the block belongs to
70
- * @param name - Defines the name of the block
71
- * @param disableOptimization - Defines if the block should not be optimized (default: false)
72
- * @param disableStrategy - Defines the strategy to use for making this block disableable (default: BlockDisableStrategy.AutoSample)
68
+ * Gets the shader program to use to render the block.
69
+ * @returns The shader program to use to render the block
73
70
  */
74
- constructor(smartFilter: SmartFilter, name: string, disableOptimization = false, disableStrategy = BlockDisableStrategy.AutoSample) {
75
- super(smartFilter, name, disableOptimization);
76
- this.blockDisableStrategy = disableStrategy;
71
+ public override getShaderProgram() {
72
+ const shaderProgram = super.getShaderProgram();
77
73
 
78
74
  // If we haven't already modified the shader code for this block type, do so now
79
75
  if (!this._hasModifiedShaderCode) {
80
76
  this._hasModifiedShaderCode = true;
81
77
 
82
78
  // Apply the disable strategy
83
- const shaderProgram = this.getShaderProgram();
84
79
  switch (this.blockDisableStrategy) {
85
80
  case BlockDisableStrategy.AutoSample:
86
81
  InjectAutoSampleDisableCode(shaderProgram);
87
82
  break;
88
83
  }
89
84
  }
85
+
86
+ return shaderProgram;
87
+ }
88
+
89
+ /**
90
+ * Instantiates a new block.
91
+ * @param smartFilter - Defines the smart filter the block belongs to
92
+ * @param name - Defines the name of the block
93
+ * @param disableOptimization - Defines if the block should not be optimized (default: false)
94
+ * @param disableStrategy - Defines the strategy to use for making this block disableable (default: BlockDisableStrategy.AutoSample)
95
+ */
96
+ constructor(smartFilter: SmartFilter, name: string, disableOptimization = false, disableStrategy = BlockDisableStrategy.AutoSample) {
97
+ super(smartFilter, name, disableOptimization);
98
+ this.blockDisableStrategy = disableStrategy;
90
99
  }
91
100
  }
@@ -53,6 +53,11 @@ export interface IEditablePropertyOption {
53
53
  valuesAreStrings?: boolean;
54
54
  /** If supplied, the sub property to read/write */
55
55
  subPropertyName?: string;
56
+ /**
57
+ * If supplied, scope this to a specific block type - useful for the
58
+ * CustomShaderBlock where multiple block types are implemented with the same class
59
+ */
60
+ blockType?: string;
56
61
  }
57
62
 
58
63
  /**
@@ -94,13 +99,22 @@ export function EditableInPropertyPage(
94
99
  target._propStore = propStore;
95
100
  }
96
101
 
97
- propStore.push({
102
+ const propToAdd: IPropertyDescriptionForEdition = {
98
103
  propertyName: propertyKey,
99
104
  displayName: displayName,
100
105
  type: propertyType,
101
106
  groupName: groupName,
102
107
  options: options ?? {},
103
108
  className: target.constructor.name,
104
- });
109
+ };
110
+
111
+ // If the property already exists, overwrite it, otherwise add it
112
+ // Note: It may have been redefined since the application started
113
+ const existingIndex = propStore.findIndex((p) => p.propertyName === propertyKey && p.className === target.constructor.name && options?.blockType === p.options?.blockType);
114
+ if (existingIndex !== -1) {
115
+ propStore[existingIndex] = propToAdd;
116
+ } else {
117
+ propStore.push(propToAdd);
118
+ }
105
119
  };
106
120
  }
@@ -243,8 +243,8 @@ export class SmartFilterOptimizer {
243
243
  return newVarName;
244
244
  }
245
245
 
246
- private _processDefines(block: ShaderBlock, renameWork: RenameWork) {
247
- const defines = block.getShaderProgram().fragment.defines;
246
+ private _processDefines(block: ShaderBlock, shaderProgram: ShaderProgram, renameWork: RenameWork) {
247
+ const defines = shaderProgram.fragment.defines;
248
248
  if (!defines) {
249
249
  return;
250
250
  }
@@ -292,11 +292,12 @@ export class SmartFilterOptimizer {
292
292
  * folded into the final optimized block.
293
293
  * NOTE: so this function can know about the uniforms to test for them, it must be called after _processVariables.
294
294
  * @param block - The block we are processing
295
+ * @param shaderProgram - The shader program associated with the block
295
296
  * @param renameWork - The list of rename work to add to as needed
296
297
  * @param samplerList - The list of sampler names
297
298
  */
298
- private _processHelperFunctions(block: ShaderBlock, renameWork: RenameWork, samplerList: string[]): void {
299
- const functions = block.getShaderProgram().fragment.functions;
299
+ private _processHelperFunctions(block: ShaderBlock, shaderProgram: ShaderProgram, renameWork: RenameWork, samplerList: string[]): void {
300
+ const functions = shaderProgram.fragment.functions;
300
301
 
301
302
  if (functions.length === 1) {
302
303
  // There's only the main function, so we don't need to do anything
@@ -306,7 +307,7 @@ export class SmartFilterOptimizer {
306
307
  for (const func of functions) {
307
308
  let funcName = func.name;
308
309
 
309
- if (funcName === block.getShaderProgram().fragment.mainFunctionName) {
310
+ if (funcName === shaderProgram.fragment.mainFunctionName) {
310
311
  continue;
311
312
  }
312
313
 
@@ -381,19 +382,29 @@ export class SmartFilterOptimizer {
381
382
  }
382
383
  }
383
384
 
385
+ /**
386
+ * Processes either consts or uniforms. Handles capturing the rename work needed, updating the sampler list, and
387
+ * accounting for single instance situations (where a const or uniform is shared across all instances of this block).
388
+ * @param block - The block to work on
389
+ * @param renameWork - The RenameWork list to update
390
+ * @param varDecl - Which type of variable we're working with
391
+ * @param declarations - The declarations of those variables from the shader
392
+ * @param sharedByAllInstances - If this should be treated as a shared value across all instances of this shader block
393
+ * @returns The list of samplers
394
+ */
384
395
  private _processVariables(
385
396
  block: ShaderBlock,
386
397
  renameWork: RenameWork,
387
398
  varDecl: "const" | "uniform",
388
- declarations?: string,
389
- hasValue = false,
390
- forceSingleInstance = false
399
+ declarations: string | undefined,
400
+ sharedByAllInstances: boolean
391
401
  ): Array<string> {
392
402
  if (!declarations) {
393
403
  return [];
394
404
  }
395
405
 
396
406
  let rex = `${varDecl}\\s+(\\S+)\\s+${DecorateChar}(\\w+)${DecorateChar}\\s*`;
407
+ const hasValue = varDecl !== "uniform";
397
408
  if (hasValue) {
398
409
  rex += "=\\s*(.+);";
399
410
  } else {
@@ -405,7 +416,6 @@ export class SmartFilterOptimizer {
405
416
 
406
417
  let match = rx.exec(declarations);
407
418
  while (match !== null) {
408
- const singleInstance = forceSingleInstance || varDecl === "const";
409
419
  const varType = match[1]!;
410
420
  const varName = match[2]!;
411
421
  const varValue = hasValue ? match[3]! : null;
@@ -416,7 +426,7 @@ export class SmartFilterOptimizer {
416
426
  samplerList.push(DecorateSymbol(varName));
417
427
  } else {
418
428
  const existingRemapped = this._remappedSymbols.find((s) => s.type === varDecl && s.name === varName && s.owners[0] && s.owners[0].blockType === block.blockType);
419
- if (existingRemapped && singleInstance) {
429
+ if (existingRemapped && sharedByAllInstances) {
420
430
  newVarName = existingRemapped.remappedName;
421
431
  if (varDecl === "uniform") {
422
432
  existingRemapped.owners.push(block);
@@ -486,13 +496,13 @@ export class SmartFilterOptimizer {
486
496
  return UndecorateSymbol(newSamplerName);
487
497
  }
488
498
 
489
- private _canBeOptimized(block: BaseBlock): boolean {
499
+ private _canBeOptimized(block: BaseBlock, shaderProgram: ShaderProgram): boolean {
490
500
  if (block.disableOptimization) {
491
501
  return false;
492
502
  }
493
503
 
494
504
  if (block instanceof ShaderBlock) {
495
- if (block.getShaderProgram().vertex !== this._vertexShaderCode) {
505
+ if (shaderProgram.vertex !== this._vertexShaderCode) {
496
506
  return false;
497
507
  }
498
508
 
@@ -517,6 +527,7 @@ export class SmartFilterOptimizer {
517
527
  this._currentOutputTextureOptions = block.outputTextureOptions;
518
528
  }
519
529
 
530
+ // Sometimes getShaderProgram() does work, so only grab it once for efficiency
520
531
  const shaderProgram = block.getShaderProgram();
521
532
  if (!shaderProgram) {
522
533
  throw new Error(`Shader program not found for block "${block.name}"!`);
@@ -546,22 +557,25 @@ export class SmartFilterOptimizer {
546
557
  }
547
558
 
548
559
  // Processes the defines to make them unique
549
- this._processDefines(block, renameWork);
560
+ this._processDefines(block, shaderProgram, renameWork);
550
561
 
551
562
  // Processes the constants to make them unique
552
563
  this._processVariables(block, renameWork, "const", shaderProgram.fragment.const, true);
553
564
 
565
+ // Processes the per-instance constants
566
+ this._processVariables(block, renameWork, "const", shaderProgram.fragment.constPerInstance, false);
567
+
554
568
  // Processes the uniform inputs to make them unique. Also extract the list of samplers
555
569
  let samplerList: string[] = [];
556
570
  samplerList = this._processVariables(block, renameWork, "uniform", shaderProgram.fragment.uniform, false);
557
571
 
558
572
  let additionalSamplers = [];
559
- additionalSamplers = this._processVariables(block, renameWork, "uniform", shaderProgram.fragment.uniformSingle, false, true);
573
+ additionalSamplers = this._processVariables(block, renameWork, "uniform", shaderProgram.fragment.uniformSingle, true);
560
574
 
561
575
  samplerList.push(...additionalSamplers);
562
576
 
563
577
  // Processes the functions other than the main function - must be done after _processVariables()
564
- this._processHelperFunctions(block, renameWork, samplerList);
578
+ this._processHelperFunctions(block, shaderProgram, renameWork, samplerList);
565
579
 
566
580
  // Processes the texture inputs
567
581
  for (const sampler of samplerList) {
@@ -589,7 +603,7 @@ export class SmartFilterOptimizer {
589
603
  if (IsTextureInputBlock(parentBlock)) {
590
604
  // input is connected to an InputBlock of type "Texture": we must directly sample a texture
591
605
  this._processSampleTexture(block, renameWork, samplerName, samplers, parentBlock);
592
- } else if (this._forceUnoptimized || !this._canBeOptimized(parentBlock)) {
606
+ } else if (this._forceUnoptimized || !this._canBeOptimized(parentBlock, shaderProgram)) {
593
607
  // the block connected to this input cannot be optimized: we must directly sample its output texture
594
608
  const uniqueSamplerName = this._processSampleTexture(block, renameWork, samplerName, samplers);
595
609
  let stackItem = this._blockToStackItem.get(parentBlock);
@@ -81,6 +81,7 @@ function ImportAnnotatedGlsl(fragmentShader: string): SerializedShaderBlockDefin
81
81
  fragment: fragmentShaderInfo.shaderCode,
82
82
  },
83
83
  inputConnectionPoints,
84
+ fragmentConstProperties: fragmentShaderInfo.fragmentConstProperties,
84
85
  disableOptimization: !!fragmentShaderInfo.disableOptimization,
85
86
  };
86
87
  }
@@ -7,6 +7,7 @@ import { OutputBlock } from "../blockFoundation/outputBlock.js";
7
7
  import type { IBlockSerializerV1, ISerializedBlockV1, ISerializedConnectionV1, SerializeBlockV1, SerializedSmartFilterV1 } from "./v1/smartFilterSerialization.types.js";
8
8
  import { CustomShaderBlock } from "../blockFoundation/customShaderBlock.js";
9
9
  import { CustomAggregateBlock } from "../blockFoundation/customAggregateBlock.js";
10
+ import { CustomShaderBlockSerializer } from "../blockFoundation/customShaderBlock.serializer.js";
10
11
 
11
12
  /**
12
13
  * Determines if two serialized connection points are equivalent to each other
@@ -54,9 +55,11 @@ export class SmartFilterSerializer {
54
55
  // Serialize the block itself
55
56
  const blockClassName = block.getClassName();
56
57
  const serializeFn =
57
- blockClassName === CustomShaderBlock.ClassName || blockClassName === CustomAggregateBlock.ClassName
58
- ? DefaultBlockSerializer
59
- : this._blockSerializers.get(block.blockType);
58
+ blockClassName === CustomShaderBlock.ClassName
59
+ ? CustomShaderBlockSerializer
60
+ : blockClassName === CustomAggregateBlock.ClassName
61
+ ? DefaultBlockSerializer
62
+ : this._blockSerializers.get(block.blockType);
60
63
  if (!serializeFn) {
61
64
  throw new Error(`No serializer was provided for a block of type ${block.blockType}`);
62
65
  }
@@ -8,6 +8,43 @@ import type { Nullable } from "core/types.js";
8
8
  import type { AllConnectionPointTypes, ConnectionPointValue } from "../../connection/connectionPointType.js";
9
9
  import type { ShaderProgram } from "../../utils/shaderCodeUtils.js";
10
10
 
11
+ /**
12
+ * Description of a const property exposed by a shader block.
13
+ */
14
+ type ConstPropertyMetadataBase = {
15
+ /**
16
+ * The name of the const in the shader code
17
+ */
18
+ name: string;
19
+
20
+ /**
21
+ * A friendly name for the property to be displayed in the Smart Filters Editor UI.
22
+ * This is the undecorated name of the const in the shader code.
23
+ */
24
+ friendlyName: string;
25
+
26
+ /**
27
+ * The type of the property
28
+ */
29
+ type: string;
30
+ };
31
+
32
+ type ConstPropertyMetadataFloat = ConstPropertyMetadataBase & {
33
+ type: "float";
34
+
35
+ /**
36
+ * The default value of the property
37
+ */
38
+ defaultValue: number;
39
+
40
+ /**
41
+ * Optional mapping of values to strings to be displayed in the Smart Filters Editor UI for this property.
42
+ */
43
+ options?: { [key: string]: number };
44
+ };
45
+
46
+ export type ConstPropertyMetadata = ConstPropertyMetadataFloat;
47
+
11
48
  /**
12
49
  * The V1 definition of a serialized shader block. This block definition is loaded by a CustomShaderBlock and defines how a
13
50
  * blockType works. This should not be confused with an ISerializedBockV1, which is a serialized instance of a block in a
@@ -48,6 +85,11 @@ export type SerializedShaderBlockDefinitionV1 = {
48
85
  */
49
86
  inputConnectionPoints: SerializedInputConnectionPointV1[];
50
87
 
88
+ /**
89
+ * Properties which map to consts in the fragment shader.
90
+ */
91
+ fragmentConstProperties?: ConstPropertyMetadata[];
92
+
51
93
  /**
52
94
  * If true, the optimizer will not attempt to optimize this block.
53
95
  */
@@ -1,59 +1,65 @@
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
- * The parameters of the function.
17
- */
18
- params?: string;
19
- };
20
-
21
- /**
22
- * Describes a shader code.
23
- */
24
- export type ShaderCode = {
25
- /**
26
- * The declaration of the const variables.
27
- */
28
- const?: string;
29
-
30
- /**
31
- * The declaration of the uniform variables.
32
- */
33
- uniform?: string;
34
-
35
- /**
36
- * The declaration of the uniform variables that should be common for all ShaderBlock instances using this shader code.
37
- */
38
- uniformSingle?: string;
39
-
40
- /**
41
- * The name of the main function.
42
- */
43
- mainFunctionName: string;
44
-
45
- /**
46
- * The name of the input texture which is passed through if the block is disabled.
47
- */
48
- mainInputTexture?: string;
49
-
50
- /**
51
- * The list of functions used in the shader.
52
- */
53
- functions: ShaderFunction[];
54
-
55
- /**
56
- * The declaration of define statements.
57
- */
58
- defines?: string[];
59
- };
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
+ * The parameters of the function.
17
+ */
18
+ params?: string;
19
+ };
20
+
21
+ /**
22
+ * Describes a shader code.
23
+ */
24
+ export type ShaderCode = {
25
+ /**
26
+ * The declaration of the const variables.
27
+ */
28
+ const?: string;
29
+
30
+ /**
31
+ * The declaration of const variables which can be modified by each instance of the block using
32
+ * this ShaderCode. The optimizer will not consolidate when there are multiple instances of the same block.
33
+ */
34
+ constPerInstance?: string;
35
+
36
+ /**
37
+ * The declaration of the uniform variables.
38
+ */
39
+ uniform?: string;
40
+
41
+ /**
42
+ * The declaration of the uniform variables that should be common for all ShaderBlock instances using this shader code.
43
+ */
44
+ uniformSingle?: string;
45
+
46
+ /**
47
+ * The name of the main function.
48
+ */
49
+ mainFunctionName: string;
50
+
51
+ /**
52
+ * The name of the input texture which is passed through if the block is disabled.
53
+ */
54
+ mainInputTexture?: string;
55
+
56
+ /**
57
+ * The list of functions used in the shader.
58
+ */
59
+ functions: ShaderFunction[];
60
+
61
+ /**
62
+ * The declaration of define statements.
63
+ */
64
+ defines?: string[];
65
+ };