Skip to content

Commit 58d6e06

Browse files
authored
Node Render Graph: Add support for custom blocks in NRGE (#17343)
1 parent c703f67 commit 58d6e06

File tree

6 files changed

+88
-9
lines changed

6 files changed

+88
-9
lines changed

packages/dev/core/src/FrameGraph/Node/Types/nodeRenderGraphTypes.ts

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,29 @@
1-
import type { Color4, Scene, FrameGraphTextureHandle, Camera, FrameGraphObjectList, IShadowLight, FrameGraphShadowGeneratorTask, FrameGraphObjectRendererTask } from "core/index";
1+
import type {
2+
Color4,
3+
Scene,
4+
FrameGraphTextureHandle,
5+
Camera,
6+
FrameGraphObjectList,
7+
IShadowLight,
8+
FrameGraphShadowGeneratorTask,
9+
FrameGraphObjectRendererTask,
10+
FrameGraph,
11+
NodeRenderGraphBlock,
12+
} from "core/index";
13+
14+
/**
15+
* Description of a custom block to be used in the node render graph editor
16+
*/
17+
export interface INodeRenderGraphCustomBlockDescription {
18+
/** Block name. It will be used as the block name in the left menu of the editor. Spaces must be replaced by underscores in the name. */
19+
name: string;
20+
/** Description (tooltip) of the block. */
21+
description: string;
22+
/** Category of the block. Spaces must be replaced by underscores in the category name. */
23+
menu: string;
24+
/** Factory function to create the block. */
25+
factory: (frameGraph: FrameGraph, scene: Scene) => NodeRenderGraphBlock;
26+
}
227

328
/**
429
* Interface used to configure the node render graph editor
@@ -10,6 +35,7 @@ export interface INodeRenderGraphEditorOptions {
1035
nodeRenderGraphEditorConfig?: {
1136
backgroundColor?: Color4;
1237
hostScene?: Scene;
38+
customBlockDescriptions?: INodeRenderGraphCustomBlockDescription[];
1339
};
1440
}
1541

packages/dev/core/src/FrameGraph/Node/nodeRenderGraph.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import type {
1010
Scene,
1111
WritableObject,
1212
IShadowLight,
13+
INodeRenderGraphCustomBlockDescription,
1314
} from "core/index";
1415
import { Observable } from "../../Misc/observable";
1516
import { NodeRenderGraphOutputBlock } from "./Blocks/outputBlock";
@@ -49,6 +50,9 @@ export class NodeRenderGraph {
4950
/** Define the Url to load snippets */
5051
public static SnippetUrl = Constants.SnippetUrl;
5152

53+
/** Description of custom blocks to use in the node render graph editor */
54+
public static CustomBlockDescriptions: INodeRenderGraphCustomBlockDescription[] = [];
55+
5256
// eslint-disable-next-line @typescript-eslint/naming-convention
5357
private BJSNODERENDERGRAPHEDITOR = this._getGlobalNodeRenderGraphEditor();
5458

@@ -272,6 +276,7 @@ export class NodeRenderGraph {
272276
private _createNodeEditor(additionalConfig?: any) {
273277
const nodeEditorConfig: any = {
274278
nodeRenderGraph: this,
279+
customBlockDescriptions: NodeRenderGraph.CustomBlockDescriptions,
275280
...additionalConfig,
276281
};
277282
this.BJSNODERENDERGRAPHEDITOR.NodeRenderGraphEditor.Show(nodeEditorConfig);

packages/tools/nodeRenderGraphEditor/src/components/nodeList/nodeListComponent.tsx

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,9 @@ export class NodeListComponent extends React.Component<INodeListComponentProps,
152152
}
153153

154154
// Block types used to create the menu from
155-
const allBlocks: any = {
155+
const allBlocks: {
156+
[key: string]: string[];
157+
} = {
156158
Custom_Frames: customFrameNames,
157159
Inputs: [
158160
"TextureBlock",
@@ -195,26 +197,56 @@ export class NodeListComponent extends React.Component<INodeListComponentProps,
195197
Textures: ["ClearBlock", "CopyTextureBlock", "GenerateMipmapsBlock"],
196198
};
197199

200+
const customBlockDescriptions = this.props.globalState.customBlockDescriptions;
201+
if (customBlockDescriptions) {
202+
for (const desc of customBlockDescriptions) {
203+
const menu = desc.menu || "Custom_Blocks";
204+
let name = desc.name;
205+
if (!name.endsWith("Block")) {
206+
name += "Block";
207+
}
208+
if (allBlocks[menu]) {
209+
allBlocks[menu].push(name);
210+
} else {
211+
allBlocks[menu] = [name];
212+
}
213+
}
214+
}
215+
198216
// Create node menu
199217
const blockMenu = [];
200218
for (const key in allBlocks) {
201219
const blockList = allBlocks[key]
202220
.filter((b: string) => !this.state.filter || b.toLowerCase().indexOf(this.state.filter.toLowerCase()) !== -1)
203221
.sort((a: string, b: string) => a.localeCompare(b))
204-
.map((block: any) => {
222+
.map((blockName: string) => {
223+
const displayBlockName = blockName.replace(/_/g, " ");
205224
if (key === "Custom_Frames") {
206225
return (
207226
<DraggableLineWithButtonComponent
208-
key={block}
209-
data={block}
210-
tooltip={this._customFrameList[block] || ""}
227+
key={blockName}
228+
data={displayBlockName}
229+
tooltip={this._customFrameList[blockName] || ""}
211230
iconImage={deleteButton}
212231
iconTitle="Delete"
213232
onIconClick={(value) => this.removeItem(value)}
214233
/>
215234
);
216235
}
217-
return <DraggableLineComponent key={block} data={block} tooltip={NodeListComponent._Tooltips[block] || ""} />;
236+
return (
237+
<DraggableLineComponent
238+
key={blockName}
239+
data={displayBlockName}
240+
tooltip={
241+
customBlockDescriptions?.find((desc) => {
242+
const name = desc.name.endsWith("Block") ? desc.name : desc.name + "Block";
243+
return name === blockName;
244+
})?.description ||
245+
NodeListComponent._Tooltips[blockName] ||
246+
""
247+
}
248+
/>
249+
);
218250
});
219251

220252
if (key === "Custom_Frames") {
@@ -262,7 +294,7 @@ export class NodeListComponent extends React.Component<INodeListComponentProps,
262294
finalName = name.substring(0, nameIndex);
263295
finalName += " [custom]";
264296
} else {
265-
finalName = name.replace("Block", "");
297+
finalName = name.replace("Block", "").replace(/_/g, " ");
266298
}
267299
return finalName;
268300
};

packages/tools/nodeRenderGraphEditor/src/globalState.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { RegisterDebugSupport } from "./graphSystem/registerDebugSupport";
1717
import { PreviewType } from "./components/preview/previewType";
1818
import type { Scene } from "core/scene";
1919
import { SerializationTools } from "./serializationTools";
20+
import type { INodeRenderGraphCustomBlockDescription } from "core/FrameGraph/Node/Types/nodeRenderGraphTypes";
2021

2122
export class GlobalState {
2223
hostElement: HTMLElement;
@@ -55,6 +56,7 @@ export class GlobalState {
5556
_engine: number;
5657

5758
customSave?: { label: string; action: (data: string) => Promise<void> };
59+
customBlockDescriptions?: INodeRenderGraphCustomBlockDescription[];
5860

5961
private _nodeRenderGraph: NodeRenderGraph;
6062

packages/tools/nodeRenderGraphEditor/src/graphEditor.tsx

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,8 @@ export class GraphEditor extends React.Component<IGraphEditorProps, IGraphEditor
455455

456456
let customBlockData: any;
457457

458+
blockType = blockType.replace(/ /g, "_");
459+
458460
// Dropped something that is not a node
459461
if (blockType === "") {
460462
return;
@@ -498,7 +500,16 @@ export class GraphEditor extends React.Component<IGraphEditorProps, IGraphEditor
498500
if (blockType.indexOf("Block") === -1) {
499501
newNode = this.addValueNode(blockType);
500502
} else {
501-
const block = BlockTools.GetBlockFromString(blockType, this.props.globalState.nodeRenderGraph.frameGraph, this.props.globalState.scene)!;
503+
let block: NodeRenderGraphBlock = BlockTools.GetBlockFromString(blockType, this.props.globalState.nodeRenderGraph.frameGraph, this.props.globalState.scene)!;
504+
if (block === null) {
505+
const customBlock = this.props.globalState.customBlockDescriptions?.find((d) => {
506+
const name = d.name.endsWith("Block") ? d.name : d.name + "Block";
507+
return name === blockType;
508+
});
509+
if (customBlock) {
510+
block = customBlock.factory(this.props.globalState.nodeRenderGraph.frameGraph, this.props.globalState.scene);
511+
}
512+
}
502513

503514
if (block.isUnique) {
504515
const className = block.getClassName();

packages/tools/nodeRenderGraphEditor/src/nodeRenderGraphEditor.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { RegisterToPropertyTabManagers } from "./graphSystem/registerToPropertyL
1010
import { RegisterTypeLedger } from "./graphSystem/registerToTypeLedger";
1111
import type { Scene } from "core/scene";
1212
import { CreatePopup } from "shared-ui-components/popupHelper";
13+
import type { INodeRenderGraphCustomBlockDescription } from "core/FrameGraph/Node/Types/nodeRenderGraphTypes";
1314

1415
/**
1516
* Interface used to specify creation options for the node editor
@@ -20,6 +21,7 @@ export interface INodeEditorOptions {
2021
hostElement?: HTMLElement;
2122
customSave?: { label: string; action: (data: string) => Promise<void> };
2223
customLoadObservable?: Observable<any>;
24+
customBlockDescriptions?: INodeRenderGraphCustomBlockDescription[];
2325
}
2426

2527
/**
@@ -65,6 +67,7 @@ export class NodeRenderGraphEditor {
6567
globalState.hostWindow = hostElement.ownerDocument.defaultView!;
6668
globalState.stateManager.hostDocument = globalState.hostDocument;
6769
globalState.noAutoFillExternalInputs = options.hostScene !== undefined && options.hostScene !== null;
70+
globalState.customBlockDescriptions = options.customBlockDescriptions;
6871

6972
const graphEditor = React.createElement(GraphEditor, {
7073
globalState: globalState,

0 commit comments

Comments
 (0)