@onerjs/smart-filters 8.25.0
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/license.md +21 -0
- package/package.json +52 -0
- package/readme.md +9 -0
- package/src/IDisposable.ts +9 -0
- package/src/blockFoundation/aggregateBlock.ts +148 -0
- package/src/blockFoundation/baseBlock.ts +339 -0
- package/src/blockFoundation/customAggregateBlock.ts +88 -0
- package/src/blockFoundation/customShaderBlock.ts +362 -0
- package/src/blockFoundation/disableableShaderBlock.ts +91 -0
- package/src/blockFoundation/index.ts +9 -0
- package/src/blockFoundation/inputBlock.deserializer.ts +72 -0
- package/src/blockFoundation/inputBlock.serialization.types.ts +126 -0
- package/src/blockFoundation/inputBlock.serializer.ts +150 -0
- package/src/blockFoundation/inputBlock.ts +181 -0
- package/src/blockFoundation/outputBlock.ts +144 -0
- package/src/blockFoundation/shaderBlock.ts +156 -0
- package/src/blockFoundation/textureOptions.ts +57 -0
- package/src/command/command.ts +59 -0
- package/src/command/commandBuffer.ts +71 -0
- package/src/command/commandBufferDebugger.ts +14 -0
- package/src/command/index.ts +7 -0
- package/src/connection/connectionPoint.ts +205 -0
- package/src/connection/connectionPointCompatibilityState.ts +31 -0
- package/src/connection/connectionPointDirection.ts +9 -0
- package/src/connection/connectionPointType.ts +45 -0
- package/src/connection/connectionPointWithDefault.ts +27 -0
- package/src/connection/index.ts +8 -0
- package/src/editorUtils/editableInPropertyPage.ts +106 -0
- package/src/editorUtils/index.ts +3 -0
- package/src/index.ts +16 -0
- package/src/optimization/dependencyGraph.ts +96 -0
- package/src/optimization/index.ts +1 -0
- package/src/optimization/optimizedShaderBlock.ts +131 -0
- package/src/optimization/smartFilterOptimizer.ts +757 -0
- package/src/runtime/index.ts +8 -0
- package/src/runtime/renderTargetGenerator.ts +222 -0
- package/src/runtime/shaderRuntime.ts +174 -0
- package/src/runtime/smartFilterRuntime.ts +112 -0
- package/src/runtime/strongRef.ts +18 -0
- package/src/serialization/importCustomBlockDefinition.ts +86 -0
- package/src/serialization/index.ts +10 -0
- package/src/serialization/serializedBlockDefinition.ts +12 -0
- package/src/serialization/serializedShaderBlockDefinition.ts +7 -0
- package/src/serialization/serializedSmartFilter.ts +6 -0
- package/src/serialization/smartFilterDeserializer.ts +190 -0
- package/src/serialization/smartFilterSerializer.ts +110 -0
- package/src/serialization/v1/defaultBlockSerializer.ts +21 -0
- package/src/serialization/v1/index.ts +4 -0
- package/src/serialization/v1/shaderBlockSerialization.types.ts +85 -0
- package/src/serialization/v1/smartFilterSerialization.types.ts +129 -0
- package/src/smartFilter.ts +255 -0
- package/src/utils/buildTools/buildShaders.ts +14 -0
- package/src/utils/buildTools/convertGlslIntoBlock.ts +370 -0
- package/src/utils/buildTools/convertGlslIntoShaderProgram.ts +173 -0
- package/src/utils/buildTools/convertShaders.ts +65 -0
- package/src/utils/buildTools/recordVersionNumber.js +24 -0
- package/src/utils/buildTools/shaderCode.types.ts +59 -0
- package/src/utils/buildTools/shaderConverter.ts +466 -0
- package/src/utils/buildTools/watchShaders.ts +44 -0
- package/src/utils/index.ts +4 -0
- package/src/utils/renderTargetUtils.ts +30 -0
- package/src/utils/shaderCodeUtils.ts +192 -0
- package/src/utils/textureLoaders.ts +31 -0
- package/src/utils/textureUtils.ts +28 -0
- package/src/utils/uniqueIdGenerator.ts +28 -0
- package/src/version.ts +4 -0
package/license.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Babylon.js
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@onerjs/smart-filters",
|
|
3
|
+
"version": "8.25.0",
|
|
4
|
+
"description": "Babylon.js Smart Filter core",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"video",
|
|
7
|
+
"composition",
|
|
8
|
+
"3D",
|
|
9
|
+
"2D",
|
|
10
|
+
"javascript",
|
|
11
|
+
"html5",
|
|
12
|
+
"webgl",
|
|
13
|
+
"webgl2",
|
|
14
|
+
"webgpu",
|
|
15
|
+
"babylon"
|
|
16
|
+
],
|
|
17
|
+
"license": "MIT",
|
|
18
|
+
"readme": "README.md",
|
|
19
|
+
"main": "dist/index",
|
|
20
|
+
"module": "dist/index",
|
|
21
|
+
"esnext": "dist/index",
|
|
22
|
+
"types": "dist/index",
|
|
23
|
+
"type": "module",
|
|
24
|
+
"sideEffects": [
|
|
25
|
+
"./dist/utils/buildTools/**"
|
|
26
|
+
],
|
|
27
|
+
"repository": {
|
|
28
|
+
"type": "git",
|
|
29
|
+
"url": "git+https://github.com/BabylonJS/Babylon.js.git"
|
|
30
|
+
},
|
|
31
|
+
"files": [
|
|
32
|
+
"dist",
|
|
33
|
+
"src",
|
|
34
|
+
"license.md",
|
|
35
|
+
"readme.md"
|
|
36
|
+
],
|
|
37
|
+
"scripts": {
|
|
38
|
+
"build": "npm run clean && npm run copySrc && npm run compile",
|
|
39
|
+
"clean": "rimraf dist && rimraf src && rimraf *.tsbuildinfo -g && rimraf \"./**/*.!(cmd|md|json|build.json|lts.json|tasks.json|cjs)\" -g",
|
|
40
|
+
"copySrc": "node -e \"require('fs').cpSync('../../../dev/smartFilters/src', './src', { recursive: true })\"",
|
|
41
|
+
"compile": "node src/utils/buildTools/recordVersionNumber.js && tsc -b tsconfig.build.json",
|
|
42
|
+
"postcompile": "build-tools -c add-js-to-es6"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@dev/build-tools": "^1.0.0",
|
|
46
|
+
"@dev/core": "^1.0.0",
|
|
47
|
+
"@dev/shared-ui-components": "1.0.0"
|
|
48
|
+
},
|
|
49
|
+
"peerDependencies": {
|
|
50
|
+
"@onerjs/core": "^7.47.3 || ^8.0.1"
|
|
51
|
+
}
|
|
52
|
+
}
|
package/readme.md
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Babylon.js Smart Filters
|
|
2
|
+
|
|
3
|
+
## Core
|
|
4
|
+
|
|
5
|
+
A Smart Filter is a node based description of effects to apply on various inputs in order to create a visual output on a canvas element or specified render target.
|
|
6
|
+
|
|
7
|
+
The main usage is for video processing and composition but it could be easily reused for picture edition or procedural creation.
|
|
8
|
+
|
|
9
|
+
See the full documentation at [doc.babylonjs.com](https://doc.babylonjs.com/features/featuresDeepDive/smartFilters/)
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import type { Nullable } from "core/types.js";
|
|
2
|
+
import type { ConnectionPoint, RuntimeData } from "../connection/connectionPoint.js";
|
|
3
|
+
import type { ConnectionPointType } from "../connection/connectionPointType.js";
|
|
4
|
+
|
|
5
|
+
import { BaseBlock } from "./baseBlock.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The aggregate block class is the base class for all blocks that be created from other blocks.
|
|
9
|
+
*
|
|
10
|
+
* It is responsible for managing a hidden chain of smart filter blocks in order and expose them through
|
|
11
|
+
* its own connection points.
|
|
12
|
+
*
|
|
13
|
+
* The internal state is basically a filter itself.
|
|
14
|
+
*/
|
|
15
|
+
export abstract class AggregateBlock extends BaseBlock {
|
|
16
|
+
/**
|
|
17
|
+
* The class name of the block.
|
|
18
|
+
*/
|
|
19
|
+
public static override ClassName = "AggregateBlock";
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* The list of relationships between the internal graph output and the outside ones.
|
|
23
|
+
*/
|
|
24
|
+
private readonly _aggregatedOutputs: [ConnectionPoint, ConnectionPoint][] = [];
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* The list of relationships between the internal graph inputs and the outside ones.
|
|
28
|
+
*/
|
|
29
|
+
private readonly _aggregatedInputs: [ConnectionPoint[], ConnectionPoint][] = [];
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Do not override prepareForRuntime for aggregate blocks. It is not supported.
|
|
33
|
+
*/
|
|
34
|
+
public override prepareForRuntime(): never {
|
|
35
|
+
throw new Error("Aggregate blocks should not be prepared for runtime.");
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* @internal
|
|
40
|
+
* Merges the internal graph into the SmartFilter
|
|
41
|
+
*/
|
|
42
|
+
public _mergeIntoSmartFilter(mergedAggregateBlocks: AggregateBlock[]): void {
|
|
43
|
+
// Rewire output connections
|
|
44
|
+
for (const [internalConnectionPoint, externalConnectionPoint] of this._aggregatedOutputs) {
|
|
45
|
+
const endpointsToConnectTo = externalConnectionPoint.endpoints.slice();
|
|
46
|
+
externalConnectionPoint.disconnectAllEndpoints();
|
|
47
|
+
for (const endpoint of endpointsToConnectTo) {
|
|
48
|
+
internalConnectionPoint.connectTo(endpoint);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Rewire input connections
|
|
53
|
+
for (const [internalConnectionPoints, externalConnectionPoint] of this._aggregatedInputs) {
|
|
54
|
+
const connectedToExternalConnectionPoint = externalConnectionPoint.connectedTo;
|
|
55
|
+
if (connectedToExternalConnectionPoint) {
|
|
56
|
+
connectedToExternalConnectionPoint.disconnectFrom(externalConnectionPoint);
|
|
57
|
+
for (const internalConnectionPoint of internalConnectionPoints) {
|
|
58
|
+
connectedToExternalConnectionPoint.connectTo(internalConnectionPoint);
|
|
59
|
+
}
|
|
60
|
+
} else {
|
|
61
|
+
// If the external connection point is not connected to anything but has a default value,
|
|
62
|
+
// pass that default value along to the internal connection points it is associated with
|
|
63
|
+
const defaultValue = externalConnectionPoint.runtimeData;
|
|
64
|
+
if (defaultValue !== null) {
|
|
65
|
+
for (const internalConnectionPoint of internalConnectionPoints) {
|
|
66
|
+
internalConnectionPoint.runtimeData = defaultValue;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Tell any internal aggregate blocks to merge
|
|
73
|
+
// Must be done after the inputs and outputs were merged at our level, or the internal aggregate block may not be wired up to anything
|
|
74
|
+
for (const aggregateOutput of this._aggregatedOutputs) {
|
|
75
|
+
const internalConnectionPoint = aggregateOutput[0];
|
|
76
|
+
internalConnectionPoint.ownerBlock.visit({}, (block: BaseBlock, _extraData: object) => {
|
|
77
|
+
if (block instanceof AggregateBlock) {
|
|
78
|
+
block._mergeIntoSmartFilter(mergedAggregateBlocks);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Add ourselves to the list of merged aggregate blocks
|
|
84
|
+
mergedAggregateBlocks.push(this);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* @internal
|
|
89
|
+
* Undoes a previous mergeIntoSmartFilter call.
|
|
90
|
+
*/
|
|
91
|
+
public _unmergeFromSmartFilter(): void {
|
|
92
|
+
for (const [internalConnectionPoint, externalConnectionPoint] of this._aggregatedOutputs) {
|
|
93
|
+
const endpointsToConnectTo = internalConnectionPoint.endpoints.slice();
|
|
94
|
+
internalConnectionPoint.disconnectAllEndpoints();
|
|
95
|
+
for (const endpoint of endpointsToConnectTo) {
|
|
96
|
+
externalConnectionPoint.connectTo(endpoint);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
for (const [internalConnectionPoints, externalConnectionPoint] of this._aggregatedInputs) {
|
|
101
|
+
if (internalConnectionPoints[0]) {
|
|
102
|
+
const connectedToInternalConnectionPoint = internalConnectionPoints[0].connectedTo;
|
|
103
|
+
if (connectedToInternalConnectionPoint) {
|
|
104
|
+
for (const internalConnectionPoint of internalConnectionPoints) {
|
|
105
|
+
connectedToInternalConnectionPoint.disconnectFrom(internalConnectionPoint);
|
|
106
|
+
}
|
|
107
|
+
connectedToInternalConnectionPoint.connectTo(externalConnectionPoint);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Registers an input connection from the internal graph as an input of the aggregated graph.
|
|
115
|
+
* @param name - The name of the exposed input connection point
|
|
116
|
+
* @param internalConnectionPoints - The input connection points in the inner graph to wire up to the new subfilter input
|
|
117
|
+
* @param defaultValue - The default value to use for the input connection point
|
|
118
|
+
* @returns the connection point referencing the input block
|
|
119
|
+
*/
|
|
120
|
+
protected _registerSubfilterInput<U extends ConnectionPointType>(
|
|
121
|
+
name: string,
|
|
122
|
+
internalConnectionPoints: ConnectionPoint<U>[],
|
|
123
|
+
defaultValue: Nullable<RuntimeData<U>> = null
|
|
124
|
+
): ConnectionPoint<U> {
|
|
125
|
+
const type = internalConnectionPoints[0]?.type;
|
|
126
|
+
if (type === undefined) {
|
|
127
|
+
throw new Error("Cannot register an input connection point with no internal connection points");
|
|
128
|
+
}
|
|
129
|
+
const externalInputConnectionPoint = this._registerInput(name, type, defaultValue);
|
|
130
|
+
|
|
131
|
+
this._aggregatedInputs.push([internalConnectionPoints, externalInputConnectionPoint]);
|
|
132
|
+
|
|
133
|
+
return externalInputConnectionPoint;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Registers an output connection point from the internal graph as an output of the aggregated graph.
|
|
138
|
+
* @param name - The name of the exposed output connection point
|
|
139
|
+
* @param internalConnectionPoint - The output connection point in the inner graph to expose as an output on the aggregate block
|
|
140
|
+
* @returns the connection point referencing the output connection point
|
|
141
|
+
*/
|
|
142
|
+
protected _registerSubfilterOutput<U extends ConnectionPointType>(name: string, internalConnectionPoint: ConnectionPoint<U>): ConnectionPoint<U> {
|
|
143
|
+
const externalOutputConnectionPoint = this._registerOutput(name, internalConnectionPoint.type);
|
|
144
|
+
|
|
145
|
+
this._aggregatedOutputs.push([internalConnectionPoint, externalOutputConnectionPoint]);
|
|
146
|
+
return externalOutputConnectionPoint;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
import type { Nullable } from "core/types.js";
|
|
2
|
+
import { ConnectionPointType, type ConnectionPointValue } from "../connection/connectionPointType.js";
|
|
3
|
+
import type { InitializationData, SmartFilter } from "../smartFilter.js";
|
|
4
|
+
import type { ICommandOwner } from "../command/command.js";
|
|
5
|
+
import { ConnectionPoint, type RuntimeData } from "../connection/connectionPoint.js";
|
|
6
|
+
import { ConnectionPointWithDefault } from "../connection/connectionPointWithDefault.js";
|
|
7
|
+
import { ConnectionPointDirection } from "../connection/connectionPointDirection.js";
|
|
8
|
+
import { UniqueIdGenerator } from "../utils/uniqueIdGenerator.js";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Defines a callback function that is triggered when visiting a block,
|
|
12
|
+
* It also carries over extra data preventing the need to use global variables or closures.
|
|
13
|
+
*/
|
|
14
|
+
export type BlockVisitor<T extends object> = (block: BaseBlock, extraData: T) => void;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* This class represents the base class for all smart filter blocks.
|
|
18
|
+
*
|
|
19
|
+
* It defines the basic structure of a smart filter block and provides the base implementation for
|
|
20
|
+
* managing the connection points.
|
|
21
|
+
*
|
|
22
|
+
* It enforces common behavior for all smart filter blocks.
|
|
23
|
+
*/
|
|
24
|
+
export abstract class BaseBlock implements ICommandOwner {
|
|
25
|
+
protected static _AlreadyVisitedBlocks = new Set<BaseBlock>();
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* The class name of the block.
|
|
29
|
+
*/
|
|
30
|
+
public static ClassName = "BaseBlock";
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* The namespace of the block, which is used to reduce name collisions between blocks and also to group blocks in the editor UI.
|
|
34
|
+
* By convention, sub namespaces are separated by a period (e.g. "Babylon.Demo.Effects").
|
|
35
|
+
*/
|
|
36
|
+
public static Namespace: Nullable<string> = null;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* The smart filter the block belongs to.
|
|
40
|
+
*/
|
|
41
|
+
public readonly smartFilter: SmartFilter;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Global unique id of the block (This is unique for the current session).
|
|
45
|
+
*/
|
|
46
|
+
public uniqueId: number;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* The name of the block. This is used to identify the block in the smart filter or in debug.
|
|
50
|
+
*/
|
|
51
|
+
public readonly name: string;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* The type of the block - used when serializing / deserializing the block, and in the editor.
|
|
55
|
+
* For programmatically created blocks, this should be the class name of the block.
|
|
56
|
+
* For custom blocks, this is specified in the block definition.
|
|
57
|
+
*/
|
|
58
|
+
public get blockType(): string {
|
|
59
|
+
return this.getClassName();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* The namespace of the block, which is used to reduce name collisions between blocks and also to group blocks in the editor UI.
|
|
64
|
+
* By convention, sub namespaces are separated by a period (e.g. "Babylon.Demo.Effects").
|
|
65
|
+
*/
|
|
66
|
+
public get namespace(): Nullable<string> {
|
|
67
|
+
// Note that we use a static property instead of doing this.constructor.name to avoid problems with minifiers that would change the name of the class
|
|
68
|
+
return (this.constructor as typeof BaseBlock).Namespace;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* User provided comments about the block. It can be used to document the block.
|
|
73
|
+
*/
|
|
74
|
+
public comments: Nullable<string> = null;
|
|
75
|
+
|
|
76
|
+
private readonly _inputs: ConnectionPoint[] = [];
|
|
77
|
+
private readonly _outputs: ConnectionPoint[] = [];
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Instantiates a new block.
|
|
81
|
+
* @param smartFilter - Defines the smart filter the block belongs to
|
|
82
|
+
* @param name - Defines the name of the block
|
|
83
|
+
* @param disableOptimization - Defines if the block is optimizable or not
|
|
84
|
+
*/
|
|
85
|
+
constructor(
|
|
86
|
+
smartFilter: SmartFilter,
|
|
87
|
+
name: string,
|
|
88
|
+
public readonly disableOptimization = false
|
|
89
|
+
) {
|
|
90
|
+
this.uniqueId = UniqueIdGenerator.UniqueId;
|
|
91
|
+
this.name = name;
|
|
92
|
+
this.smartFilter = smartFilter;
|
|
93
|
+
|
|
94
|
+
// Register the block in the smart filter
|
|
95
|
+
smartFilter.registerBlock(this);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Returns the inputs connection points of the current block.
|
|
100
|
+
*/
|
|
101
|
+
public get inputs(): ReadonlyArray<ConnectionPoint> {
|
|
102
|
+
return this._inputs;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Returns the outputs connection points of the current block.
|
|
107
|
+
*/
|
|
108
|
+
public get outputs(): ReadonlyArray<ConnectionPoint> {
|
|
109
|
+
return this._outputs;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Returns if the block is an input block.
|
|
114
|
+
*/
|
|
115
|
+
public get isInput(): boolean {
|
|
116
|
+
return this._inputs.length === 0;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Returns if the block is an output block.
|
|
121
|
+
*/
|
|
122
|
+
public get isOutput(): boolean {
|
|
123
|
+
return this._outputs.length === 0;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* @returns the class name of the block
|
|
128
|
+
*/
|
|
129
|
+
public getClassName(): string {
|
|
130
|
+
// Note that we use a static property instead of doing this.constructor.name to avoid problems with minifiers that would change the name of the class
|
|
131
|
+
return (this.constructor as typeof BaseBlock).ClassName;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Checks if the block is an "ancestor" of another giving block.
|
|
136
|
+
* @param block - Defines the block to check against
|
|
137
|
+
* @returns True if the block is an ancestor of the given block, otherwise false
|
|
138
|
+
*/
|
|
139
|
+
public isAnAncestorOf(block: BaseBlock): boolean {
|
|
140
|
+
for (const output of this._outputs) {
|
|
141
|
+
if (!output.endpoints.length) {
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
for (const endpoint of output.endpoints) {
|
|
146
|
+
if (endpoint.ownerBlock === block) {
|
|
147
|
+
return true;
|
|
148
|
+
}
|
|
149
|
+
if (endpoint.ownerBlock.isAnAncestorOf(block)) {
|
|
150
|
+
return true;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
protected _visitInputs<T extends object>(extraData: T, callback: BlockVisitor<T>, alreadyVisited: Set<BaseBlock>): void {
|
|
159
|
+
for (const input of this.inputs) {
|
|
160
|
+
if (!input.connectedTo) {
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const block = input.connectedTo.ownerBlock;
|
|
165
|
+
|
|
166
|
+
block.visit(extraData, callback, alreadyVisited);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Visits the block and its inputs recursively.
|
|
172
|
+
* When starting from the smart filter output block, this will visit all the blocks in the smart filter.
|
|
173
|
+
* Note that it's a depth first visit: the callback is called on the block AFTER visiting its inputs.
|
|
174
|
+
* @param extraData - The extra data to pass to the callback
|
|
175
|
+
* @param callback - The callback to call on each block
|
|
176
|
+
* @param alreadyVisitedBlocks - Defines the set of blocks already visited (if not provided, a new set will be created)
|
|
177
|
+
*/
|
|
178
|
+
public visit<T extends object>(extraData: T, callback: BlockVisitor<T>, alreadyVisitedBlocks?: Set<BaseBlock>): void {
|
|
179
|
+
if (!alreadyVisitedBlocks) {
|
|
180
|
+
alreadyVisitedBlocks = BaseBlock._AlreadyVisitedBlocks;
|
|
181
|
+
alreadyVisitedBlocks.clear();
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (!alreadyVisitedBlocks.has(this)) {
|
|
185
|
+
alreadyVisitedBlocks.add(this);
|
|
186
|
+
|
|
187
|
+
this._visitInputs(extraData, callback, alreadyVisitedBlocks);
|
|
188
|
+
|
|
189
|
+
callback(this, extraData);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Finds the input connection point with the given name.
|
|
195
|
+
* @param name - Name of the input to find
|
|
196
|
+
* @returns The connection point with the given name or null if not found
|
|
197
|
+
*/
|
|
198
|
+
public findInput<U extends ConnectionPointType>(name: string): Nullable<ConnectionPoint<U>> {
|
|
199
|
+
for (const input of this._inputs) {
|
|
200
|
+
if (input.name === name) {
|
|
201
|
+
return input as ConnectionPoint<U>;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return null;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Disconnects the block from the graph.
|
|
210
|
+
* @param _disconnectedConnections - Stores the connections that have been broken in the process. You can reconnect them later if needed.
|
|
211
|
+
*/
|
|
212
|
+
public disconnectFromGraph(_disconnectedConnections?: [ConnectionPoint, ConnectionPoint][]): void {}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Prepares the block for runtime.
|
|
216
|
+
* This is called by the smart filter just before creating the smart filter runtime, and by the optimizer.
|
|
217
|
+
*/
|
|
218
|
+
public prepareForRuntime(): void {}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Propagates the runtime data - telling all outputs to propagate their runtime data forward through the graph
|
|
222
|
+
*/
|
|
223
|
+
public propagateRuntimeData(): void {
|
|
224
|
+
for (const output of this._outputs) {
|
|
225
|
+
output.propagateRuntimeData();
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Generates the commands needed to execute the block at runtime and gathers promises for initialization work
|
|
231
|
+
* @param initializationData - The initialization data to use
|
|
232
|
+
* @param _finalOutput - Defines if the block is the final output of the smart filter
|
|
233
|
+
*/
|
|
234
|
+
public generateCommandsAndGatherInitPromises(initializationData: InitializationData, _finalOutput: boolean): void {
|
|
235
|
+
// Check if any inputs are Textures which aren't yet ready, and if so, ensure init waits for them to be ready
|
|
236
|
+
for (const input of this._inputs) {
|
|
237
|
+
if (input.type === ConnectionPointType.Texture) {
|
|
238
|
+
const texture = input.runtimeData?.value as ConnectionPointValue<ConnectionPointType.Texture>;
|
|
239
|
+
if (texture && !texture.isReady()) {
|
|
240
|
+
const internalTexture = texture.getInternalTexture();
|
|
241
|
+
if (internalTexture) {
|
|
242
|
+
const textureReadyPromise = new Promise<void>((resolve, reject) => {
|
|
243
|
+
internalTexture.onLoadedObservable.add(() => {
|
|
244
|
+
resolve();
|
|
245
|
+
});
|
|
246
|
+
internalTexture.onErrorObservable.add((error) => {
|
|
247
|
+
// eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors
|
|
248
|
+
reject(error);
|
|
249
|
+
});
|
|
250
|
+
});
|
|
251
|
+
initializationData.initializationPromises.push(textureReadyPromise);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Disconnects all the inputs and outputs from the Block.
|
|
260
|
+
*/
|
|
261
|
+
public disconnect(): void {
|
|
262
|
+
// Detach inputs
|
|
263
|
+
for (const input of this._inputs) {
|
|
264
|
+
input.connectedTo?.disconnectFrom(input);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Detach outputs
|
|
268
|
+
for (const output of this._outputs) {
|
|
269
|
+
output.disconnectAllEndpoints();
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Registers a new input connection point in the block which must have a connection before the graph can be used.
|
|
275
|
+
* @param name - Defines the name of the input connection point
|
|
276
|
+
* @param type - Defines the type of the input connection point
|
|
277
|
+
* @param defaultValue - Defines the optional default value of the input connection point to use if not connection is made
|
|
278
|
+
* @returns The new ConnectionPoint
|
|
279
|
+
* @internal
|
|
280
|
+
*/
|
|
281
|
+
public _registerInput<U extends ConnectionPointType>(name: string, type: U, defaultValue: Nullable<RuntimeData<U>> = null): ConnectionPoint<U> {
|
|
282
|
+
const input = new ConnectionPoint(name, this, type, ConnectionPointDirection.Input, defaultValue);
|
|
283
|
+
this._inputs.push(input);
|
|
284
|
+
return input;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Registers a new input connection point in the block which doesn't require a connection because it has a default value.
|
|
289
|
+
* @param name - Defines the name of the input connection point
|
|
290
|
+
* @param type - Defines the type of the input connection point
|
|
291
|
+
* @param defaultValue - Defines the default value to use if nothing is connected to this connection point
|
|
292
|
+
* @returns The new ConnectionPointWithDefault
|
|
293
|
+
* @internal
|
|
294
|
+
*/
|
|
295
|
+
public _registerOptionalInput<U extends ConnectionPointType>(name: string, type: U, defaultValue: RuntimeData<U>): ConnectionPointWithDefault<U> {
|
|
296
|
+
const input = new ConnectionPointWithDefault(name, this, type, ConnectionPointDirection.Input, defaultValue);
|
|
297
|
+
this._inputs.push(input);
|
|
298
|
+
return input;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Registers a new output connection point in the block.
|
|
303
|
+
* @param name - Defines the name of the output connection point
|
|
304
|
+
* @param type - Defines the type of the output connection point
|
|
305
|
+
* @returns The new output connection point
|
|
306
|
+
* @internal
|
|
307
|
+
*/
|
|
308
|
+
public _registerOutput<U extends ConnectionPointType>(name: string, type: U): ConnectionPoint<U> {
|
|
309
|
+
const output = new ConnectionPoint(name, this, type, ConnectionPointDirection.Output);
|
|
310
|
+
this._outputs.push(output);
|
|
311
|
+
return output;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Registers a new output connection point in the block that always has runtimeData because it has a default value and doesn't allow it to be overwritten with null.
|
|
316
|
+
* @param name - Defines the name of the output connection point
|
|
317
|
+
* @param type - Defines the type of the output connection point
|
|
318
|
+
* @param initialValue - Defines the initial value of the output connection point
|
|
319
|
+
* @returns The new output connection point with a default value
|
|
320
|
+
* @internal
|
|
321
|
+
*/
|
|
322
|
+
public _registerOutputWithDefault<U extends ConnectionPointType>(name: string, type: U, initialValue: RuntimeData<U>): ConnectionPointWithDefault<U> {
|
|
323
|
+
const output = new ConnectionPointWithDefault(name, this, type, ConnectionPointDirection.Output, initialValue);
|
|
324
|
+
this._outputs.push(output);
|
|
325
|
+
return output;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* Gets the required RuntimeData for the given input, throwing with a clear message if it is null
|
|
330
|
+
* @param input - The input to get the runtime data for
|
|
331
|
+
* @returns The runtimeData or throws if it was undefined
|
|
332
|
+
*/
|
|
333
|
+
protected _confirmRuntimeDataSupplied<U extends ConnectionPointType = ConnectionPointType>(input: ConnectionPoint<U>): RuntimeData<U> {
|
|
334
|
+
if (!input.runtimeData) {
|
|
335
|
+
throw new Error(`The ${ConnectionPointType[input.type]} input named "${input.name}" is missing for the ${this.getClassName()} named "${this.name}"`);
|
|
336
|
+
}
|
|
337
|
+
return input.runtimeData;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/* eslint-disable import/no-internal-modules */
|
|
2
|
+
import type { ThinEngine } from "core/Engines/thinEngine.js";
|
|
3
|
+
import type { SmartFilterDeserializer, SerializedBlockDefinition } from "../serialization/index.js";
|
|
4
|
+
import type { SmartFilter } from "../smartFilter.js";
|
|
5
|
+
import { AggregateBlock } from "./aggregateBlock.js";
|
|
6
|
+
import type { BaseBlock } from "./baseBlock.js";
|
|
7
|
+
import type { Nullable } from "core/types.js";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Loads a serialized Smart Filter into a block which can be used in another SmartFilter.
|
|
11
|
+
*/
|
|
12
|
+
export class CustomAggregateBlock extends AggregateBlock {
|
|
13
|
+
/**
|
|
14
|
+
* Creates a new CustomAggregateBlock
|
|
15
|
+
* @param smartFilter - The Smart Filter to create the block for
|
|
16
|
+
* @param engine - The ThinEngine to use
|
|
17
|
+
* @param name - The friendly name of the block
|
|
18
|
+
* @param serializedSmartFilter - The serialized SmartFilter to load into the block
|
|
19
|
+
* @param smartFilterDeserializer - The deserializer to use
|
|
20
|
+
* @returns A promise that resolves to the new CustomAggregateBlock
|
|
21
|
+
*/
|
|
22
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
23
|
+
public static async Create(
|
|
24
|
+
smartFilter: SmartFilter,
|
|
25
|
+
engine: ThinEngine,
|
|
26
|
+
name: string,
|
|
27
|
+
serializedSmartFilter: SerializedBlockDefinition,
|
|
28
|
+
smartFilterDeserializer: SmartFilterDeserializer
|
|
29
|
+
): Promise<BaseBlock> {
|
|
30
|
+
const innerSmartFilter = await smartFilterDeserializer.deserialize(engine, serializedSmartFilter);
|
|
31
|
+
return new CustomAggregateBlock(smartFilter, name, serializedSmartFilter.blockType, serializedSmartFilter.namespace, innerSmartFilter, false);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* The class name of the block.
|
|
36
|
+
*/
|
|
37
|
+
public static override ClassName = "CustomAggregateBlock";
|
|
38
|
+
|
|
39
|
+
private readonly _blockType: string;
|
|
40
|
+
private readonly _namespace: Nullable<string>;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* The type of the block - used when serializing / deserializing the block, and in the editor.
|
|
44
|
+
*/
|
|
45
|
+
public override get blockType(): string {
|
|
46
|
+
return this._blockType;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* The namespace of the block, which is used to reduce name collisions between blocks and also to group blocks in the editor UI.
|
|
51
|
+
* By convention, sub namespaces are separated by a period (e.g. "Babylon.Demo.Effects").
|
|
52
|
+
*/
|
|
53
|
+
public override get namespace(): Nullable<string> {
|
|
54
|
+
return this._namespace;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
private constructor(smartFilter: SmartFilter, name: string, blockType: string, namespace: Nullable<string>, innerSmartFilter: SmartFilter, disableOptimization: boolean) {
|
|
58
|
+
super(smartFilter, name, disableOptimization);
|
|
59
|
+
|
|
60
|
+
this._blockType = blockType;
|
|
61
|
+
this._namespace = namespace;
|
|
62
|
+
|
|
63
|
+
const attachedBlocks = innerSmartFilter.attachedBlocks;
|
|
64
|
+
for (let index = 0; index < attachedBlocks.length; index++) {
|
|
65
|
+
const block = attachedBlocks[index];
|
|
66
|
+
if (block && block.isInput && block.outputs[0]) {
|
|
67
|
+
// If this input block is connected to anything (has any endpoints), create an input connection point for it
|
|
68
|
+
if (block.outputs[0].endpoints.length > 0) {
|
|
69
|
+
this._registerSubfilterInput(block.name, block.outputs[0].endpoints.slice(), block.outputs[0].runtimeData ?? null);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Remove this input block from the Smart Filter graph - this will reset the runtimeData to the
|
|
73
|
+
// default for that connection point (which may be null)
|
|
74
|
+
innerSmartFilter.removeBlock(block);
|
|
75
|
+
index--;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (!innerSmartFilter.output.connectedTo) {
|
|
80
|
+
throw new Error("The inner smart filter must have an output connected to something");
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
this._registerSubfilterOutput("output", innerSmartFilter.output.connectedTo);
|
|
84
|
+
|
|
85
|
+
// Disconnect the inner Smart Filter output from the inner Smart Filter
|
|
86
|
+
innerSmartFilter.output.connectedTo.disconnectFrom(innerSmartFilter.output);
|
|
87
|
+
}
|
|
88
|
+
}
|