@babylonjs/smart-filters 0.3.3-alpha → 0.3.4-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 (54) hide show
  1. package/dist/blocks/inputBlock.d.ts +19 -0
  2. package/dist/blocks/inputBlock.d.ts.map +1 -1
  3. package/dist/blocks/inputBlock.deserializer.d.ts +1 -3
  4. package/dist/blocks/inputBlock.deserializer.d.ts.map +1 -1
  5. package/dist/blocks/inputBlock.deserializer.js +14 -13
  6. package/dist/blocks/inputBlock.deserializer.js.map +1 -1
  7. package/dist/blocks/inputBlock.js.map +1 -1
  8. package/dist/blocks/inputBlock.serialization.types.d.ts +13 -0
  9. package/dist/blocks/inputBlock.serialization.types.d.ts.map +1 -1
  10. package/dist/blocks/inputBlock.serializer.d.ts.map +1 -1
  11. package/dist/blocks/inputBlock.serializer.js +12 -4
  12. package/dist/blocks/inputBlock.serializer.js.map +1 -1
  13. package/dist/blocks/outputBlock.d.ts +7 -0
  14. package/dist/blocks/outputBlock.d.ts.map +1 -1
  15. package/dist/blocks/outputBlock.js +7 -4
  16. package/dist/blocks/outputBlock.js.map +1 -1
  17. package/dist/blocks/shaderBlock.d.ts.map +1 -1
  18. package/dist/blocks/shaderBlock.js +5 -8
  19. package/dist/blocks/shaderBlock.js.map +1 -1
  20. package/dist/runtime/renderTargetGenerator.d.ts +3 -2
  21. package/dist/runtime/renderTargetGenerator.d.ts.map +1 -1
  22. package/dist/runtime/renderTargetGenerator.js +25 -14
  23. package/dist/runtime/renderTargetGenerator.js.map +1 -1
  24. package/dist/serialization/smartFilterDeserializer.d.ts.map +1 -1
  25. package/dist/serialization/smartFilterDeserializer.js +12 -5
  26. package/dist/serialization/smartFilterDeserializer.js.map +1 -1
  27. package/dist/serialization/smartFilterSerializer.js +4 -4
  28. package/dist/serialization/smartFilterSerializer.js.map +1 -1
  29. package/dist/serialization/v1/serialization.types.d.ts +4 -4
  30. package/dist/serialization/v1/serialization.types.d.ts.map +1 -1
  31. package/dist/smartFilter.d.ts +4 -1
  32. package/dist/smartFilter.d.ts.map +1 -1
  33. package/dist/smartFilter.js +7 -7
  34. package/dist/smartFilter.js.map +1 -1
  35. package/dist/utils/buildTools/shaderConverter.js +2 -2
  36. package/dist/utils/buildTools/shaderConverter.js.map +1 -1
  37. package/dist/utils/renderTargetUtils.d.ts +24 -0
  38. package/dist/utils/renderTargetUtils.d.ts.map +1 -0
  39. package/dist/utils/renderTargetUtils.js +38 -0
  40. package/dist/utils/renderTargetUtils.js.map +1 -0
  41. package/package.json +1 -1
  42. package/src/blocks/inputBlock.deserializer.ts +20 -21
  43. package/src/blocks/inputBlock.serialization.types.ts +16 -0
  44. package/src/blocks/inputBlock.serializer.ts +10 -1
  45. package/src/blocks/inputBlock.ts +25 -1
  46. package/src/blocks/outputBlock.ts +10 -6
  47. package/src/blocks/shaderBlock.ts +11 -10
  48. package/src/runtime/renderTargetGenerator.ts +31 -14
  49. package/src/serialization/smartFilterDeserializer.ts +17 -6
  50. package/src/serialization/smartFilterSerializer.ts +4 -4
  51. package/src/serialization/v1/serialization.types.ts +4 -4
  52. package/src/smartFilter.ts +12 -9
  53. package/src/utils/buildTools/shaderConverter.ts +2 -2
  54. package/src/utils/renderTargetUtils.ts +56 -0
@@ -51,11 +51,11 @@ export class RenderTargetGenerator {
51
51
 
52
52
  /**
53
53
  * Sets the output textures for the ShaderBlocks of the smart filter.
54
- * @param smart - The smart filter to generate the render targets for.
54
+ * @param smartFilter - The smart filter to generate the render targets for.
55
55
  * @param initializationData - The initialization data to use.
56
56
  */
57
- public setOutputTextures(smart: SmartFilter, initializationData: InitializationData) {
58
- smart.output.ownerBlock.visit(
57
+ public setOutputTextures(smartFilter: SmartFilter, initializationData: InitializationData) {
58
+ smartFilter.output.ownerBlock.visit(
59
59
  initializationData,
60
60
  (block: BaseBlock, initializationData: InitializationData) => {
61
61
  if (!(block instanceof ShaderBlock)) {
@@ -66,8 +66,8 @@ export class RenderTargetGenerator {
66
66
 
67
67
  // We assign a texture to the output of the block only if this is not the last block in the chain,
68
68
  // i.e. not the block connected to the smart output block (in which case the output of the block is to the canvas and not a texture).
69
- if (!block.output.endpoints.some((cp) => cp.ownerBlock === smart.output.ownerBlock)) {
70
- refCountedTexture = this._getTexture(initializationData.runtime, block.textureRatio);
69
+ if (!block.output.endpoints.some((cp) => cp.ownerBlock === smartFilter.output.ownerBlock)) {
70
+ refCountedTexture = this._getTexture(initializationData.runtime, block.textureRatio, smartFilter);
71
71
 
72
72
  if (!block.output.runtimeData) {
73
73
  const runtimeOutput = createStrongRef(refCountedTexture.texture);
@@ -122,11 +122,15 @@ export class RenderTargetGenerator {
122
122
  return null;
123
123
  }
124
124
 
125
- private _getTexture(runtime: InternalSmartFilterRuntime, ratio: number): RefCountedTexture {
125
+ private _getTexture(
126
+ runtime: InternalSmartFilterRuntime,
127
+ ratio: number,
128
+ smartFilter: SmartFilter
129
+ ): RefCountedTexture {
126
130
  if (!this._optimize) {
127
131
  this._numTargetsCreated++;
128
132
  return {
129
- texture: this._createTexture(runtime, ratio),
133
+ texture: this._createTexture(runtime, smartFilter, ratio),
130
134
  refCount: 0,
131
135
  };
132
136
  }
@@ -140,7 +144,7 @@ export class RenderTargetGenerator {
140
144
  let refCountedTexture = this._findAvailableTexture(ratio);
141
145
  if (!refCountedTexture) {
142
146
  refCountedTexture = {
143
- texture: this._createTexture(runtime, ratio),
147
+ texture: this._createTexture(runtime, smartFilter, ratio),
144
148
  refCount: 0,
145
149
  };
146
150
  refCountedTextures.add(refCountedTexture);
@@ -173,10 +177,15 @@ export class RenderTargetGenerator {
173
177
  /**
174
178
  * Creates an offscreen texture to hold on the result of the block rendering.
175
179
  * @param runtime - The current runtime we create the texture for
180
+ * @param smartFilter - The smart filter the texture is created for
176
181
  * @param ratio - The ratio of the texture to create compared to the final output
177
182
  * @returns The render target texture
178
183
  */
179
- private _createTexture(runtime: InternalSmartFilterRuntime, ratio: number): ThinRenderTargetTexture {
184
+ private _createTexture(
185
+ runtime: InternalSmartFilterRuntime,
186
+ smartFilter: SmartFilter,
187
+ ratio: number
188
+ ): ThinRenderTargetTexture {
180
189
  const engine = runtime.engine;
181
190
 
182
191
  // We are only rendering full screen post process without depth or stencil information
@@ -187,12 +196,20 @@ export class RenderTargetGenerator {
187
196
  samplingMode: 2, // Babylon Constants.TEXTURE_LINEAR_LINEAR,
188
197
  };
189
198
 
190
- // The size of the output is by default the current rendering size of the engine
191
- const width = engine.getRenderWidth(true);
192
- const height = engine.getRenderHeight(true);
199
+ // Get the smartFilter output size - either from the output block's renderTargetTexture or the engine's render size
200
+ let outputWidth: number;
201
+ let outputHeight: number;
202
+ const renderTargetTextureSize = smartFilter.outputBlock.renderTargetTexture?.getSize();
203
+ if (renderTargetTextureSize) {
204
+ outputWidth = renderTargetTextureSize.width;
205
+ outputHeight = renderTargetTextureSize.height;
206
+ } else {
207
+ outputWidth = engine.getRenderWidth(true);
208
+ outputHeight = engine.getRenderHeight(true);
209
+ }
193
210
  const size = {
194
- width: Math.floor(width * ratio),
195
- height: Math.floor(height * ratio),
211
+ width: Math.floor(outputWidth * ratio),
212
+ height: Math.floor(outputHeight * ratio),
196
213
  };
197
214
 
198
215
  // Creates frame buffers for effects
@@ -31,8 +31,8 @@ export class SmartFilterDeserializer {
31
31
  // Add in the core block deserializers - they are not delay loaded, so they are wrapped in Promise.resolve()
32
32
  this._blockDeserializersV1.set(
33
33
  InputBlock.ClassName,
34
- (smartFilter: SmartFilter, serializedBlock: ISerializedBlockV1, engine: ThinEngine) =>
35
- Promise.resolve(inputBlockDeserializer(smartFilter, serializedBlock, engine))
34
+ (smartFilter: SmartFilter, serializedBlock: ISerializedBlockV1) =>
35
+ Promise.resolve(inputBlockDeserializer(smartFilter, serializedBlock))
36
36
  );
37
37
 
38
38
  this._blockDeserializersV1.set(OutputBlock.ClassName, (smartFilter: SmartFilter) =>
@@ -59,7 +59,10 @@ export class SmartFilterDeserializer {
59
59
  serializedSmartFilter: SerializedSmartFilterV1
60
60
  ): Promise<SmartFilter> {
61
61
  const smartFilter = new SmartFilter(serializedSmartFilter.name);
62
- const blockMap = new Map<string, BaseBlock>();
62
+ const blockIdMap = new Map<number, BaseBlock>();
63
+
64
+ // Only needed for smart filters saved before we started using uniqueIds for the maps, didn't warrant new version
65
+ const blockNameMap = new Map<string, BaseBlock>();
63
66
 
64
67
  // Deserialize the SmartFilter level data
65
68
  smartFilter.comments = serializedSmartFilter.comments;
@@ -85,7 +88,8 @@ export class SmartFilterDeserializer {
85
88
  UniqueIdGenerator.EnsureIdsGreaterThan(newBlock.uniqueId);
86
89
 
87
90
  // Save in the map
88
- blockMap.set(newBlock.name, newBlock);
91
+ blockIdMap.set(newBlock.uniqueId, newBlock);
92
+ blockNameMap.set(newBlock.name, newBlock);
89
93
  })
90
94
  );
91
95
  });
@@ -94,7 +98,11 @@ export class SmartFilterDeserializer {
94
98
  // Deserialize the connections
95
99
  serializedSmartFilter.connections.forEach((connection: ISerializedConnectionV1) => {
96
100
  // Find the source block and its connection point's connectTo function
97
- const sourceBlock = blockMap.get(connection.outputBlock);
101
+ const sourceBlock =
102
+ typeof connection.outputBlock === "string"
103
+ ? blockNameMap.get(connection.outputBlock)
104
+ : blockIdMap.get(connection.outputBlock);
105
+
98
106
  if (!sourceBlock) {
99
107
  throw new Error(`Source block ${connection.outputBlock} not found`);
100
108
  }
@@ -107,7 +115,10 @@ export class SmartFilterDeserializer {
107
115
  const sourceConnectToFunction = sourceConnectionPoint.connectTo.bind(sourceConnectionPoint);
108
116
 
109
117
  // Find the target block and its connection point
110
- const targetBlock = blockMap.get(connection.inputBlock);
118
+ const targetBlock =
119
+ typeof connection.inputBlock === "string"
120
+ ? blockNameMap.get(connection.inputBlock)
121
+ : blockIdMap.get(connection.inputBlock);
111
122
  if (!targetBlock) {
112
123
  throw new Error(`Target block ${connection.inputBlock} not found`);
113
124
  }
@@ -72,9 +72,9 @@ export class SmartFilterSerializer {
72
72
  const connectedTo = input.connectedTo;
73
73
  if (connectedTo) {
74
74
  const newConnection: ISerializedConnectionV1 = {
75
- inputBlock: block.name,
75
+ inputBlock: block.uniqueId,
76
76
  inputConnectionPoint: input.name,
77
- outputBlock: connectedTo.ownerBlock.name,
77
+ outputBlock: connectedTo.ownerBlock.uniqueId,
78
78
  outputConnectionPoint: connectedTo.name,
79
79
  };
80
80
  if (!connections.find((other) => serializedConnectionPointsEqual(newConnection, other))) {
@@ -87,9 +87,9 @@ export class SmartFilterSerializer {
87
87
  block.outputs.forEach((output: ConnectionPoint) => {
88
88
  output.endpoints.forEach((input: ConnectionPoint) => {
89
89
  const newConnection: ISerializedConnectionV1 = {
90
- inputBlock: input.ownerBlock.name,
90
+ inputBlock: input.ownerBlock.uniqueId,
91
91
  inputConnectionPoint: input.name,
92
- outputBlock: block.name,
92
+ outputBlock: block.uniqueId,
93
93
  outputConnectionPoint: output.name,
94
94
  };
95
95
  if (!connections.find((other) => serializedConnectionPointsEqual(newConnection, other))) {
@@ -57,14 +57,14 @@ export interface ISerializedBlockV1 {
57
57
  * V1 Serialized Connection
58
58
  */
59
59
  export interface ISerializedConnectionV1 {
60
- /** The name of the block that the connection is to */
61
- outputBlock: string;
60
+ /** The uniqueId of the block that the connection is to */
61
+ outputBlock: number;
62
62
 
63
63
  /** The name of the connectionPoint on the outputBlock */
64
64
  outputConnectionPoint: string;
65
65
 
66
- /** The name of the block that the connection is from */
67
- inputBlock: string;
66
+ /** The uniqueId of the block that the connection is from */
67
+ inputBlock: number;
68
68
 
69
69
  /** The name of the connectionPoint on the inputBlock */
70
70
  inputConnectionPoint: string;
@@ -55,6 +55,11 @@ export class SmartFilter {
55
55
  */
56
56
  public readonly output: ConnectionPoint<ConnectionPointType.Texture>;
57
57
 
58
+ /**
59
+ * The output block of the smart filter.
60
+ */
61
+ public readonly outputBlock: OutputBlock;
62
+
58
63
  /**
59
64
  * User defined comments to describe the current smart filter.
60
65
  */
@@ -66,8 +71,6 @@ export class SmartFilter {
66
71
  public editorData: Nullable<IEditorData> = null;
67
72
 
68
73
  private readonly _attachedBlocks: Array<BaseBlock>;
69
- private readonly _outputBlock: OutputBlock;
70
-
71
74
  /**
72
75
  * Creates a new instance of a @see SmartFilter.
73
76
  * @param name - The friendly name of the smart filter
@@ -76,8 +79,8 @@ export class SmartFilter {
76
79
  this.name = name;
77
80
 
78
81
  this._attachedBlocks = new Array<BaseBlock>();
79
- this._outputBlock = new OutputBlock(this);
80
- this.output = this._outputBlock.input;
82
+ this.outputBlock = new OutputBlock(this);
83
+ this.output = this.outputBlock.input;
81
84
  }
82
85
 
83
86
  /**
@@ -136,7 +139,7 @@ export class SmartFilter {
136
139
  }
137
140
 
138
141
  private _generateCommandsAndGatherInitPromises(initializationData: InitializationData): void {
139
- const outputBlock = this._outputBlock;
142
+ const outputBlock = this.outputBlock;
140
143
 
141
144
  outputBlock.visit(initializationData, (block: BaseBlock, initializationData: InitializationData) => {
142
145
  // If the block is the output block,
@@ -173,17 +176,17 @@ export class SmartFilter {
173
176
 
174
177
  const initializationData: InitializationData = {
175
178
  runtime,
176
- outputBlock: this._outputBlock,
179
+ outputBlock: this.outputBlock,
177
180
  initializationPromises: [],
178
181
  };
179
182
 
180
183
  this._workWithAggregateFreeGraph(() => {
181
- this._outputBlock.prepareForRuntime();
184
+ this.outputBlock.prepareForRuntime();
182
185
 
183
186
  renderTargetGenerator = renderTargetGenerator ?? new RenderTargetGenerator(false);
184
187
  renderTargetGenerator.setOutputTextures(this, initializationData);
185
188
 
186
- this._outputBlock.propagateRuntimeData();
189
+ this.outputBlock.propagateRuntimeData();
187
190
 
188
191
  this._generateCommandsAndGatherInitPromises(initializationData);
189
192
  });
@@ -210,7 +213,7 @@ export class SmartFilter {
210
213
  const mergedAggregateBlocks: AggregateBlock[] = [];
211
214
 
212
215
  // Merge all aggregate blocks
213
- this._outputBlock.visit({}, (block: BaseBlock, _extraData: Object) => {
216
+ this.outputBlock.visit({}, (block: BaseBlock, _extraData: Object) => {
214
217
  if (block instanceof AggregateBlock) {
215
218
  block._mergeIntoSmartFilter(mergedAggregateBlocks);
216
219
  }
@@ -182,8 +182,8 @@ function processFragmentShaderV1(fragmentShader: string): FragmentShaderInfo {
182
182
  const symbolsToDecorate = [...uniforms, ...consts, ...functionNames];
183
183
  let fragmentShaderWithRenamedSymbols = fragmentShader;
184
184
  for (const symbol of symbolsToDecorate) {
185
- const regex = new RegExp(`(\\S*(?:\\s|;|,|\\)|\\()+)${symbol}((\\s|;|,|\\)|\\()+)`, "gs");
186
- fragmentShaderWithRenamedSymbols = fragmentShaderWithRenamedSymbols.replace(regex, `$1_${symbol}_$2`);
185
+ const regex = new RegExp(`(?<=\\W+)${symbol}(?=\\W+)`, "gs");
186
+ fragmentShaderWithRenamedSymbols = fragmentShaderWithRenamedSymbols.replace(regex, `_${symbol}_`);
187
187
  }
188
188
  console.log(`${symbolsToDecorate.length} symbol(s) renamed`);
189
189
  const uniformNames = uniforms.map((uniform) => `${uniform}: "${uniform}",`);
@@ -0,0 +1,56 @@
1
+ import type { RenderTargetWrapper } from "@babylonjs/core/Engines/renderTargetWrapper";
2
+ import type { ThinRenderTargetTexture } from "@babylonjs/core/Materials/Textures/thinRenderTargetTexture";
3
+ import type { Nullable } from "@babylonjs/core/types";
4
+ import { createCommand } from "../command/command.js";
5
+ import type { BaseBlock } from "../blocks/baseBlock";
6
+ import type { ShaderRuntime } from "../runtime/shaderRuntime";
7
+ import type { InternalSmartFilterRuntime } from "../runtime/smartFilterRuntime";
8
+
9
+ /**
10
+ * Tries to get a renderTarget from a renderTargetTexture, throws an error if it fails.
11
+ * @param renderTargetTexture - The renderTargetTexture to get the renderTarget from.
12
+ * @param callerName - The name of the component calling this one, used for a more descriptive error message.
13
+ * @returns - The renderTarget or throws an Error if it fails.
14
+ */
15
+ export function getRenderTarget(
16
+ renderTargetTexture: Nullable<ThinRenderTargetTexture>,
17
+ callerName: string
18
+ ): RenderTargetWrapper {
19
+ const renderTarget = renderTargetTexture?.renderTarget;
20
+ if (!renderTarget) {
21
+ throw new Error(`${callerName} could not get a renderTarget it needed.`);
22
+ }
23
+ return renderTarget;
24
+ }
25
+
26
+ /**
27
+ * Registers the final command of the command queue - the one that draws to either the canvas or
28
+ * renderTargetTexture.
29
+ * @param renderTargetTexture - If non-null, the render target texture to render to, otherwise the command will
30
+ * render to the canvas.
31
+ * @param runtime - The smart filter runtime to use.
32
+ * @param commandOwner - The owner of the command.
33
+ * @param shaderBlockRuntime - The shader block runtime to use.
34
+ */
35
+ export function registerFinalRenderCommand(
36
+ renderTargetTexture: Nullable<ThinRenderTargetTexture>,
37
+ runtime: InternalSmartFilterRuntime,
38
+ commandOwner: BaseBlock,
39
+ shaderBlockRuntime: ShaderRuntime
40
+ ): void {
41
+ const commandOwnerClassName = commandOwner.getClassName();
42
+ if (renderTargetTexture) {
43
+ const renderTarget = getRenderTarget(renderTargetTexture, commandOwnerClassName);
44
+ runtime.registerCommand(
45
+ createCommand(`${commandOwnerClassName}.renderToFinalTexture`, commandOwner, () => {
46
+ shaderBlockRuntime.renderToTexture(renderTarget);
47
+ })
48
+ );
49
+ } else {
50
+ runtime.registerCommand(
51
+ createCommand(`${commandOwnerClassName}.renderToCanvas`, commandOwner, () => {
52
+ shaderBlockRuntime.renderToCanvas();
53
+ })
54
+ );
55
+ }
56
+ }