diff --git a/packages/dev/core/src/Materials/Node/Blocks/Dual/imageSourceBlock.ts b/packages/dev/core/src/Materials/Node/Blocks/Dual/imageSourceBlock.ts index 034670ad7db..fe1952d3ef2 100644 --- a/packages/dev/core/src/Materials/Node/Blocks/Dual/imageSourceBlock.ts +++ b/packages/dev/core/src/Materials/Node/Blocks/Dual/imageSourceBlock.ts @@ -13,6 +13,7 @@ import { NodeMaterial } from "../../nodeMaterial"; import type { Scene } from "../../../../scene"; import { NodeMaterialConnectionPointCustomObject } from "../../nodeMaterialConnectionPointCustomObject"; import { EngineStore } from "../../../../Engines/engineStore"; +import { ShaderLanguage } from "core/Materials/shaderLanguage"; /** * Block used to provide an image for a TextureBlock */ @@ -68,6 +69,8 @@ export class ImageSourceBlock extends NodeMaterialBlock { NodeMaterialBlockTargets.VertexAndFragment, new NodeMaterialConnectionPointCustomObject("source", this, NodeMaterialConnectionPointDirection.Output, ImageSourceBlock, "ImageSourceBlock") ); + + this.registerOutput("dimensions", NodeMaterialBlockConnectionPointTypes.Vector2); } public override bind(effect: Effect) { @@ -101,6 +104,13 @@ export class ImageSourceBlock extends NodeMaterialBlock { return this._outputs[0]; } + /** + * Gets the dimension component + */ + public get dimensions(): NodeMaterialConnectionPoint { + return this._outputs[1]; + } + protected override _buildBlock(state: NodeMaterialBuildState) { super._buildBlock(state); @@ -113,6 +123,17 @@ export class ImageSourceBlock extends NodeMaterialBlock { state.sharedData.bindableBlocks.push(this); } + if (this.dimensions.isConnected) { + let affect: string = ""; + if (state.shaderLanguage === ShaderLanguage.WGSL) { + affect = `vec2f(textureDimensions(${this._samplerName}, 0).xy)`; + } else { + affect = `vec2(textureSize(${this._samplerName}, 0).xy)`; + } + + state.compilationString += `${state._declareOutput(this.dimensions)} = ${affect};\n`; + } + state._emit2DSampler(this._samplerName); return this; diff --git a/packages/dev/core/src/Materials/Node/Blocks/Dual/textureBlock.ts b/packages/dev/core/src/Materials/Node/Blocks/Dual/textureBlock.ts index c20c68da602..cf158ea3fa2 100644 --- a/packages/dev/core/src/Materials/Node/Blocks/Dual/textureBlock.ts +++ b/packages/dev/core/src/Materials/Node/Blocks/Dual/textureBlock.ts @@ -271,7 +271,36 @@ export class TextureBlock extends NodeMaterialBlock { return this._outputs[6]; } - public override get target() { + private _isTiedToFragment(input: NodeMaterialConnectionPoint) { + if (input.target === NodeMaterialBlockTargets.Fragment) { + return true; + } + + if (input.target === NodeMaterialBlockTargets.Vertex) { + return false; + } + + if (input.target === NodeMaterialBlockTargets.Neutral || input.target === NodeMaterialBlockTargets.VertexAndFragment) { + const parentBlock = input.ownerBlock; + + if (parentBlock.target === NodeMaterialBlockTargets.Fragment) { + return true; + } + + for (const input of parentBlock.inputs) { + if (!input.isConnected) { + continue; + } + if (this._isTiedToFragment(input.connectedPoint!)) { + return true; + } + } + } + + return false; + } + + private _getEffectiveTarget() { if (this._fragmentOnly) { return NodeMaterialBlockTargets.Fragment; } @@ -286,35 +315,15 @@ export class TextureBlock extends NodeMaterialBlock { return NodeMaterialBlockTargets.VertexAndFragment; } - let parent = this.uv.connectedPoint; - - while (parent) { - if (parent.target === NodeMaterialBlockTargets.Fragment) { - return NodeMaterialBlockTargets.Fragment; - } - - if (parent.target === NodeMaterialBlockTargets.Vertex) { - return NodeMaterialBlockTargets.VertexAndFragment; - } - - if (parent.target === NodeMaterialBlockTargets.Neutral || parent.target === NodeMaterialBlockTargets.VertexAndFragment) { - const parentBlock = parent.ownerBlock; - - if (parentBlock.target === NodeMaterialBlockTargets.Fragment) { - return NodeMaterialBlockTargets.Fragment; - } - - parent = null; - for (const input of parentBlock.inputs) { - if (input.connectedPoint) { - parent = input.connectedPoint; - break; - } - } - } + if (this._isTiedToFragment(this.uv.connectedPoint!)) { + return NodeMaterialBlockTargets.Fragment; } - return NodeMaterialBlockTargets.VertexAndFragment; + return NodeMaterialBlockTargets.Fragment; + } + + public override get target() { + return this._getEffectiveTarget(); } public override set target(value: NodeMaterialBlockTargets) {} diff --git a/packages/dev/core/src/Materials/Node/Blocks/index.ts b/packages/dev/core/src/Materials/Node/Blocks/index.ts index bd7589e3fe7..bacf06c8eed 100644 --- a/packages/dev/core/src/Materials/Node/Blocks/index.ts +++ b/packages/dev/core/src/Materials/Node/Blocks/index.ts @@ -63,3 +63,6 @@ export * from "./matrixTransposeBlock"; export * from "./meshAttributeExistsBlock"; export * from "./curveBlock"; export * from "./colorConverterBlock"; +export * from "./loopBlock"; +export * from "./storageReadBlock"; +export * from "./storageWriteBlock"; diff --git a/packages/dev/core/src/Materials/Node/Blocks/loopBlock.ts b/packages/dev/core/src/Materials/Node/Blocks/loopBlock.ts new file mode 100644 index 00000000000..ee72aec30a1 --- /dev/null +++ b/packages/dev/core/src/Materials/Node/Blocks/loopBlock.ts @@ -0,0 +1,145 @@ +import { NodeMaterialBlock } from "../nodeMaterialBlock"; +import { NodeMaterialBlockConnectionPointTypes } from "../Enums/nodeMaterialBlockConnectionPointTypes"; +import type { NodeMaterialBuildState } from "../nodeMaterialBuildState"; +import { NodeMaterialConnectionPointDirection } from "../nodeMaterialBlockConnectionPoint"; +import type { NodeMaterialConnectionPoint } from "../nodeMaterialBlockConnectionPoint"; +import { NodeMaterialBlockTargets } from "../Enums/nodeMaterialBlockTargets"; +import { RegisterClass } from "../../../Misc/typeStore"; +import { editableInPropertyPage, PropertyTypeForEdition } from "core/Decorators/nodeDecorator"; +import type { Scene } from "core/scene"; +import { ShaderLanguage } from "core/Materials/shaderLanguage"; +import { NodeMaterialConnectionPointCustomObject } from "../nodeMaterialConnectionPointCustomObject"; +/** + * Block used to repeat code + */ +export class LoopBlock extends NodeMaterialBlock { + /** + * Gets or sets number of iterations + * Will be ignored if the iterations input is connected + */ + @editableInPropertyPage("Iterations", PropertyTypeForEdition.Int) + public iterations = 4; + + /** + * Creates a new LoopBlock + * @param name defines the block name + */ + public constructor(name: string) { + super(name, NodeMaterialBlockTargets.Neutral); + + this.registerInput("input", NodeMaterialBlockConnectionPointTypes.AutoDetect); + this.registerInput("iterations", NodeMaterialBlockConnectionPointTypes.Float, true); + this.registerOutput("output", NodeMaterialBlockConnectionPointTypes.BasedOnInput); + this.registerOutput("index", NodeMaterialBlockConnectionPointTypes.Float, NodeMaterialBlockTargets.Fragment); + this.registerOutput( + "loopID", + NodeMaterialBlockConnectionPointTypes.Object, + undefined, + new NodeMaterialConnectionPointCustomObject("loopID", this, NodeMaterialConnectionPointDirection.Output, LoopBlock, "LoopBlock") + ); + + this._outputs[0]._typeConnectionSource = this._inputs[0]; + this._outputs[0]._forPostBuild = true; + + this._outputs[2]._redirectedSource = this._inputs[0]; + + this._outputs[1]._preventBubbleUp = true; + this._outputs[2]._preventBubbleUp = true; + } + + /** + * Gets the current class name + * @returns the class name + */ + public override getClassName() { + return "LoopBlock"; + } + + /** + * Gets the main input component + */ + public get input(): NodeMaterialConnectionPoint { + return this._inputs[0]; + } + + /** + * Gets the iterations input component + */ + public get iterationsInput(): NodeMaterialConnectionPoint { + return this._inputs[1]; + } + + /** + * Gets the output component + */ + public get output(): NodeMaterialConnectionPoint { + return this._outputs[0]; + } + + /** + * Gets the index component which will be incremented at each iteration + */ + public get index(): NodeMaterialConnectionPoint { + return this._outputs[1]; + } + + /** + * Gets the loop ID component + */ + public get loopID(): NodeMaterialConnectionPoint { + return this._outputs[2]; + } + + protected override _buildBlock(state: NodeMaterialBuildState) { + super._buildBlock(state); + + const output = this._outputs[0]; + const index = this._outputs[1]; + + const indexName = state._getFreeVariableName("index"); + + const decl = state.shaderLanguage === ShaderLanguage.WGSL ? "var" : "int"; + const castFloat = state.shaderLanguage === ShaderLanguage.WGSL ? "f32" : "float"; + const castInt = state.shaderLanguage === ShaderLanguage.WGSL ? "i32" : "int"; + + // Declare storage variable and store initial value + state.compilationString += state._declareOutput(output) + ` = ${this.input.associatedVariableName};\n`; + + // Iterations + const iterations = this.iterationsInput.isConnected ? `${castInt}(${this.iterationsInput.associatedVariableName})` : this.iterations; + + // Loop + state.compilationString += `for (${decl} ${indexName} = 0; ${indexName} < ${iterations}; ${indexName}++){\n`; + state.compilationString += `${state._declareOutput(index)} = ${castFloat}(${indexName});\n`; + + return this; + } + + protected override _postBuildBlock(state: NodeMaterialBuildState) { + super._postBuildBlock(state); + + state.compilationString += `}\n`; + + return this; + } + + protected override _dumpPropertiesCode() { + return super._dumpPropertiesCode() + `${this._codeVariableName}.iterations = ${this.iterations};\n`; + } + + public override serialize(): any { + const serializationObject = super.serialize(); + + serializationObject.iterations = this.iterations; + + return serializationObject; + } + + public override _deserialize(serializationObject: any, scene: Scene, rootUrl: string) { + super._deserialize(serializationObject, scene, rootUrl); + + this.iterations = serializationObject.iterations; + } +} + +RegisterClass("BABYLON.LoopBlock", LoopBlock); diff --git a/packages/dev/core/src/Materials/Node/Blocks/storageReadBlock.ts b/packages/dev/core/src/Materials/Node/Blocks/storageReadBlock.ts new file mode 100644 index 00000000000..9c51aa7b0f1 --- /dev/null +++ b/packages/dev/core/src/Materials/Node/Blocks/storageReadBlock.ts @@ -0,0 +1,71 @@ +import { NodeMaterialBlock } from "../nodeMaterialBlock"; +import { NodeMaterialBlockConnectionPointTypes } from "../Enums/nodeMaterialBlockConnectionPointTypes"; +import type { NodeMaterialBuildState } from "../nodeMaterialBuildState"; +import { NodeMaterialConnectionPointDirection, type NodeMaterialConnectionPoint } from "../nodeMaterialBlockConnectionPoint"; +import { NodeMaterialBlockTargets } from "../Enums/nodeMaterialBlockTargets"; +import { RegisterClass } from "../../../Misc/typeStore"; +import { LoopBlock } from "./loopBlock"; +import { NodeMaterialConnectionPointCustomObject } from "../nodeMaterialConnectionPointCustomObject"; +/** + * Block used to read from a variable within a loop + */ +export class StorageReadBlock extends NodeMaterialBlock { + /** + * Creates a new StorageReadBlock + * @param name defines the block name + */ + public constructor(name: string) { + super(name, NodeMaterialBlockTargets.Neutral); + + this.registerInput( + "loopID", + NodeMaterialBlockConnectionPointTypes.Object, + false, + undefined, + new NodeMaterialConnectionPointCustomObject("loopID", this, NodeMaterialConnectionPointDirection.Input, LoopBlock, "LoopBlock") + ); + this.registerOutput("value", NodeMaterialBlockConnectionPointTypes.AutoDetect); + + this._outputs[0]._linkedConnectionSource = this._inputs[0]; + } + + /** + * Gets the current class name + * @returns the class name + */ + public override getClassName() { + return "StorageReadBlock"; + } + + /** + * Gets the loop link component + */ + public get loopID(): NodeMaterialConnectionPoint { + return this._inputs[0]; + } + + /** + * Gets the value component + */ + public get value(): NodeMaterialConnectionPoint { + return this._outputs[0]; + } + + protected override _buildBlock(state: NodeMaterialBuildState) { + super._buildBlock(state); + + const value = this.value; + + if (!this.loopID.isConnected) { + return this; + } + + const loopBlock = this.loopID.connectedPoint!.ownerBlock as LoopBlock; + + state.compilationString += state._declareOutput(value) + ` = ${loopBlock.output.associatedVariableName};\n`; + + return this; + } +} + +RegisterClass("BABYLON.StorageReadBlock", StorageReadBlock); diff --git a/packages/dev/core/src/Materials/Node/Blocks/storageWriteBlock.ts b/packages/dev/core/src/Materials/Node/Blocks/storageWriteBlock.ts new file mode 100644 index 00000000000..4cbab9e4696 --- /dev/null +++ b/packages/dev/core/src/Materials/Node/Blocks/storageWriteBlock.ts @@ -0,0 +1,83 @@ +import { NodeMaterialBlock } from "../nodeMaterialBlock"; +import { NodeMaterialBlockConnectionPointTypes } from "../Enums/nodeMaterialBlockConnectionPointTypes"; +import type { NodeMaterialBuildState } from "../nodeMaterialBuildState"; +import { NodeMaterialConnectionPointDirection, type NodeMaterialConnectionPoint } from "../nodeMaterialBlockConnectionPoint"; +import { NodeMaterialBlockTargets } from "../Enums/nodeMaterialBlockTargets"; +import { RegisterClass } from "../../../Misc/typeStore"; +import { LoopBlock } from "./loopBlock"; +import { NodeMaterialConnectionPointCustomObject } from "../nodeMaterialConnectionPointCustomObject"; +/** + * Block used to write to a variable within a loop + */ +export class StorageWriteBlock extends NodeMaterialBlock { + /** + * Creates a new StorageWriteBlock + * @param name defines the block name + */ + public constructor(name: string) { + super(name, NodeMaterialBlockTargets.Neutral); + + this.registerInput( + "loopID", + NodeMaterialBlockConnectionPointTypes.Object, + false, + undefined, + new NodeMaterialConnectionPointCustomObject("loopID", this, NodeMaterialConnectionPointDirection.Input, LoopBlock, "LoopBlock") + ); + this.registerInput("value", NodeMaterialBlockConnectionPointTypes.AutoDetect); + + this._linkConnectionTypes(0, 1); + } + + /** + * Gets the current class name + * @returns the class name + */ + public override getClassName() { + return "StorageWriteBlock"; + } + + /** + * Gets the loop link component + */ + public get loopID(): NodeMaterialConnectionPoint { + return this._inputs[0]; + } + + /** + * Gets the value component + */ + public get value(): NodeMaterialConnectionPoint { + return this._inputs[1]; + } + + /** Gets a boolean indicating that this connection will be used in the fragment shader + * @returns true if connected in fragment shader + */ + public override isConnectedInFragmentShader() { + if (!this.loopID.isConnected) { + return false; + } + const loopBlock = this.loopID.connectedPoint!.ownerBlock as LoopBlock; + + return loopBlock.output.isConnectedInFragmentShader; + } + + protected override _buildBlock(state: NodeMaterialBuildState) { + super._buildBlock(state); + + const value = this.value; + + if (!this.loopID.isConnected) { + return this; + } + + const loopBlock = this.loopID.connectedPoint!.ownerBlock as LoopBlock; + + state.compilationString += `${loopBlock.output.associatedVariableName} = ${value.associatedVariableName};\n`; + + return this; + } +} + +RegisterClass("BABYLON.StorageWriteBlock", StorageWriteBlock); diff --git a/packages/dev/core/src/Materials/Node/nodeMaterial.ts b/packages/dev/core/src/Materials/Node/nodeMaterial.ts index f6116f18cc8..9a0a7d24496 100644 --- a/packages/dev/core/src/Materials/Node/nodeMaterial.ts +++ b/packages/dev/core/src/Materials/Node/nodeMaterial.ts @@ -69,6 +69,7 @@ import { PrepareDefinesForCamera, PrepareDefinesForPrePass } from "../materialHe import type { IImageProcessingConfigurationDefines } from "../imageProcessingConfiguration.defines"; import { ShaderLanguage } from "../shaderLanguage"; import { AbstractEngine } from "../../Engines/abstractEngine"; +import type { LoopBlock } from "./Blocks/loopBlock"; const onCreatedEffectParameters = { effect: null as unknown as Effect, subMesh: null as unknown as Nullable }; @@ -727,13 +728,7 @@ export class NodeMaterial extends PushMaterial { this._initializeBlock(block, state, nodesToProcessForOtherBuildState, autoConfigure); } - private _initializeBlock(node: NodeMaterialBlock, state: NodeMaterialBuildState, nodesToProcessForOtherBuildState: NodeMaterialBlock[], autoConfigure = true) { - node.initialize(state); - if (autoConfigure) { - node.autoConfigure(this); - } - node._preparationId = this._buildId; - + private _attachBlock(node: NodeMaterialBlock) { if (this.attachedBlocks.indexOf(node) === -1) { if (node.isUnique) { const className = node.getClassName(); @@ -747,12 +742,22 @@ export class NodeMaterial extends PushMaterial { } this.attachedBlocks.push(node); } + } + + private _initializeBlock(node: NodeMaterialBlock, state: NodeMaterialBuildState, nodesToProcessForOtherBuildState: NodeMaterialBlock[], autoConfigure = true) { + node.initialize(state); + if (autoConfigure) { + node.autoConfigure(this); + } + node._preparationId = this._buildId; + + this._attachBlock(node); for (const input of node.inputs) { input.associatedVariableName = ""; const connectedPoint = input.connectedPoint; - if (connectedPoint) { + if (connectedPoint && !connectedPoint._preventBubbleUp) { const block = connectedPoint.ownerBlock; if (block !== node) { this._processInitializeOnLink(block, state, nodesToProcessForOtherBuildState, autoConfigure); @@ -760,8 +765,22 @@ export class NodeMaterial extends PushMaterial { } } - // Teleportation - if (node.isTeleportOut) { + // Loop + if (node.isLoop) { + // We need to keep the storage write block in the active blocks + const loopBlock = node as LoopBlock; + if (loopBlock.loopID.hasEndpoints) { + for (const endpoint of loopBlock.loopID.endpoints) { + const block = endpoint.ownerBlock; + if (block.outputs.length !== 0) { + continue; + } + state._terminalBlocks.add(block); // Attach the storage write only + this._processInitializeOnLink(block, state, nodesToProcessForOtherBuildState, autoConfigure); + } + } + } else if (node.isTeleportOut) { + // Teleportation const teleport = node as NodeMaterialTeleportOutBlock; if (teleport.entryPoint) { this._processInitializeOnLink(teleport.entryPoint, state, nodesToProcessForOtherBuildState, autoConfigure); @@ -778,9 +797,9 @@ export class NodeMaterial extends PushMaterial { node.buildId = id; } - for (const inputs of node.inputs) { - const connectedPoint = inputs.connectedPoint; - if (connectedPoint) { + for (const input of node.inputs) { + const connectedPoint = input.connectedPoint; + if (connectedPoint && !connectedPoint._preventBubbleUp) { const block = connectedPoint.ownerBlock; if (block !== node) { this._resetDualBlocks(block, id); @@ -794,6 +813,18 @@ export class NodeMaterial extends PushMaterial { if (teleportOut.entryPoint) { this._resetDualBlocks(teleportOut.entryPoint, id); } + } else if (node.isLoop) { + // Loop + const loopBlock = node as LoopBlock; + if (loopBlock.loopID.hasEndpoints) { + for (const endpoint of loopBlock.loopID.endpoints) { + const block = endpoint.ownerBlock; + if (block.outputs.length !== 0) { + continue; + } + this._resetDualBlocks(block, id); + } + } } } @@ -1929,6 +1960,7 @@ export class NodeMaterial extends PushMaterial { this._vertexOutputNodes.length = 0; this._fragmentOutputNodes.length = 0; this.attachedBlocks.length = 0; + this._buildIsInProgress = false; } /** diff --git a/packages/dev/core/src/Materials/Node/nodeMaterialBlock.ts b/packages/dev/core/src/Materials/Node/nodeMaterialBlock.ts index 4bf0f979730..ace30308516 100644 --- a/packages/dev/core/src/Materials/Node/nodeMaterialBlock.ts +++ b/packages/dev/core/src/Materials/Node/nodeMaterialBlock.ts @@ -26,6 +26,7 @@ export class NodeMaterialBlock { protected _target: NodeMaterialBlockTargets; private _isFinalMerger = false; private _isInput = false; + private _isLoop = false; private _isTeleportOut = false; private _isTeleportIn = false; private _name = ""; @@ -124,6 +125,13 @@ export class NodeMaterialBlock { return this._isTeleportIn; } + /** + * Gets a boolean indicating if this block is a loop + */ + public get isLoop(): boolean { + return this._isLoop; + } + /** * Gets or sets the build Id */ @@ -207,9 +215,21 @@ export class NodeMaterialBlock { this._target = target; this._originalTargetIsNeutral = target === NodeMaterialBlockTargets.Neutral; this._isFinalMerger = isFinalMerger; - this._isInput = this.getClassName() === "InputBlock"; - this._isTeleportOut = this.getClassName() === "NodeMaterialTeleportOutBlock"; - this._isTeleportIn = this.getClassName() === "NodeMaterialTeleportInBlock"; + switch (this.getClassName()) { + case "InputBlock": + this._isInput = true; + break; + case "NodeMaterialTeleportOutBlock": + this._isTeleportOut = true; + break; + case "NodeMaterialTeleportInBlock": + this._isTeleportIn = true; + break; + case "LoopBlock": + this._isLoop = true; + break; + } + this._name = name; this.uniqueId = UniqueIdGenerator.UniqueId; } @@ -440,6 +460,11 @@ export class NodeMaterialBlock { // Empty. Must be defined by child nodes } + // eslint-disable-next-line @typescript-eslint/no-unused-vars + protected _postBuildBlock(state: NodeMaterialBuildState) { + // Empty. Must be defined by child nodes + } + /** * Add uniforms, samplers and uniform buffers at compilation time * @param state defines the state to update @@ -702,6 +727,31 @@ export class NodeMaterialBlock { // Compile connected blocks for (const output of this._outputs) { + if (output._forPostBuild) { + continue; + } + if ((output.target & state.target) === 0) { + continue; + } + + for (const endpoint of output.endpoints) { + const block = endpoint.ownerBlock; + + if (block) { + if (((block.target & state.target) !== 0 && activeBlocks.indexOf(block) !== -1) || state._terminalBlocks.has(block)) { + this._processBuild(block, state, endpoint, activeBlocks); + } + } + } + } + + this._postBuildBlock(state); + + // Compile post build connected blocks + for (const output of this._outputs) { + if (!output._forPostBuild) { + continue; + } if ((output.target & state.target) === 0) { continue; } diff --git a/packages/dev/core/src/Materials/Node/nodeMaterialBlockConnectionPoint.ts b/packages/dev/core/src/Materials/Node/nodeMaterialBlockConnectionPoint.ts index f338cf90eb3..43a5e19a7ad 100644 --- a/packages/dev/core/src/Materials/Node/nodeMaterialBlockConnectionPoint.ts +++ b/packages/dev/core/src/Materials/Node/nodeMaterialBlockConnectionPoint.ts @@ -71,6 +71,9 @@ export class NodeMaterialConnectionPoint { return false; } + /** @internal */ + public _preventBubbleUp = false; + /** @internal */ public readonly _ownerBlock: NodeMaterialBlock; @@ -99,6 +102,9 @@ export class NodeMaterialConnectionPoint { private _associatedVariableName: string; private readonly _direction: NodeMaterialConnectionPointDirection; + /** @internal */ + public _redirectedSource: Nullable = null; + private _typeConnectionSourceBackingField: Nullable = null; private _typeConnectionSourceTypeChangedObserver: Nullable>; @@ -165,6 +171,9 @@ export class NodeMaterialConnectionPoint { /** @internal */ public _enforceAssociatedVariableName = false; + /** @internal */ + public _forPostBuild = false; + /** Gets the direction of the point */ public get direction() { return this._direction; @@ -255,6 +264,9 @@ export class NodeMaterialConnectionPoint { } if (this._linkedConnectionSource && this._linkedConnectionSource.isConnected) { + if (this._linkedConnectionSource.connectedPoint!._redirectedSource && this._linkedConnectionSource.connectedPoint!._redirectedSource.isConnected) { + return this._linkedConnectionSource.connectedPoint!._redirectedSource.type; + } return this._linkedConnectionSource.type; } } diff --git a/packages/dev/core/src/Materials/Node/nodeMaterialBuildState.ts b/packages/dev/core/src/Materials/Node/nodeMaterialBuildState.ts index b39b1b060e8..37c6b29316c 100644 --- a/packages/dev/core/src/Materials/Node/nodeMaterialBuildState.ts +++ b/packages/dev/core/src/Materials/Node/nodeMaterialBuildState.ts @@ -5,6 +5,7 @@ import { ShaderLanguage } from "../shaderLanguage"; import type { NodeMaterialConnectionPoint } from "./nodeMaterialBlockConnectionPoint"; import { ShaderStore as EngineShaderStore } from "../../Engines/shaderStore"; import { Constants } from "../../Engines/constants"; +import type { NodeMaterialBlock } from "./nodeMaterialBlock"; /** * Class used to store node based material build state @@ -55,6 +56,9 @@ export class NodeMaterialBuildState { */ public sharedData: NodeMaterialBuildStateSharedData; + /** @internal */ + public _terminalBlocks: Set = new Set(); + /** @internal */ public _vertexState: NodeMaterialBuildState; diff --git a/packages/dev/sharedUiComponents/src/nodeGraphSystem/graphFrame.ts b/packages/dev/sharedUiComponents/src/nodeGraphSystem/graphFrame.ts index 02e08cd2391..b2fb95c6cf1 100644 --- a/packages/dev/sharedUiComponents/src/nodeGraphSystem/graphFrame.ts +++ b/packages/dev/sharedUiComponents/src/nodeGraphSystem/graphFrame.ts @@ -764,6 +764,13 @@ export class GraphFrame { link.path.style.opacity = ""; link.selectionPath.style.pointerEvents = ""; } + for (const frame of this._ownerCanvas.frames) { + if (frame !== this) { + frame.element.style.transition = ""; + frame.element.style.opacity = ""; + frame.element.style.pointerEvents = ""; + } + } return; } this._isFocused = true; @@ -776,12 +783,25 @@ export class GraphFrame { } } for (const link of this._ownerCanvas.links) { + if (this._nodes.indexOf(link.nodeA) === -1 || this._nodes.indexOf(link.nodeB!) === -1) { + link.path.style.transition = "opacity 0.5s"; + link.path.style.opacity = "0.3"; + link.selectionPath.style.pointerEvents = "none"; + } if (this._nodes.indexOf(link.nodeA) === -1 && this._nodes.indexOf(link.nodeB!) === -1) { link.path.style.transition = "opacity 0.5s"; link.path.style.opacity = "0.05"; link.selectionPath.style.pointerEvents = "none"; } } + + for (const frame of this._ownerCanvas.frames) { + if (frame !== this) { + frame.element.style.transition = "opacity 0.5s"; + frame.element.style.opacity = "0.05"; + frame.element.style.pointerEvents = "none"; + } + } } public refresh() { diff --git a/packages/tools/nodeEditor/src/blockTools.ts b/packages/tools/nodeEditor/src/blockTools.ts index c684d410e09..59d57182df6 100644 --- a/packages/tools/nodeEditor/src/blockTools.ts +++ b/packages/tools/nodeEditor/src/blockTools.ts @@ -102,10 +102,19 @@ import { PrePassTextureBlock } from "core/Materials/Node/Blocks/Input/prePassTex import { NodeMaterialTeleportInBlock } from "core/Materials/Node/Blocks/Teleport/teleportInBlock"; import { NodeMaterialTeleportOutBlock } from "core/Materials/Node/Blocks/Teleport/teleportOutBlock"; import { ColorConverterBlock } from "core/Materials/Node/Blocks/colorConverterBlock"; +import { LoopBlock } from "core/Materials/Node/Blocks/loopBlock"; +import { StorageReadBlock } from "core/Materials/Node/Blocks/storageReadBlock"; +import { StorageWriteBlock } from "core/Materials/Node/Blocks/storageWriteBlock"; export class BlockTools { public static GetBlockFromString(data: string, scene: Scene, nodeMaterial: NodeMaterial) { switch (data) { + case "StorageWriteBlock": + return new StorageWriteBlock("StorageWrite"); + case "StorageReadBlock": + return new StorageReadBlock("StorageRead"); + case "LoopBlock": + return new LoopBlock("Loop"); case "ColorConverterBlock": return new ColorConverterBlock("ColorConverter"); case "TeleportInBlock": diff --git a/packages/tools/nodeEditor/src/components/nodeList/nodeListComponent.tsx b/packages/tools/nodeEditor/src/components/nodeList/nodeListComponent.tsx index 7e49209c43e..5be813d60ee 100644 --- a/packages/tools/nodeEditor/src/components/nodeList/nodeListComponent.tsx +++ b/packages/tools/nodeEditor/src/components/nodeList/nodeListComponent.tsx @@ -187,6 +187,9 @@ export class NodeListComponent extends React.Component { + DisplayLedger.RegisteredControls["LoopBlock"] = LoopDisplayManager; DisplayLedger.RegisteredControls["InputBlock"] = InputDisplayManager; DisplayLedger.RegisteredControls["VertexOutputBlock"] = OutputDisplayManager; DisplayLedger.RegisteredControls["FragmentOutputBlock"] = OutputDisplayManager;