@babylonjs/core
Version:
Getting started? Play directly with the Babylon.js API using our [playground](https://playground.babylonjs.com/). It also contains a lot of samples to learn how to use it.
444 lines (439 loc) • 19.8 kB
JavaScript
import { __decorate } from "../../../tslib.es6.js";
import { NodeMaterialBlock } from "../nodeMaterialBlock.js";
import { NodeMaterialBlockConnectionPointTypes } from "../Enums/nodeMaterialBlockConnectionPointTypes.js";
import { NodeMaterialBlockTargets } from "../Enums/nodeMaterialBlockTargets.js";
import { NodeMaterial } from "../nodeMaterial.js";
import { RegisterClass } from "../../../Misc/typeStore.js";
import { Texture } from "../../Textures/texture.js";
import "../../../Shaders/ShadersInclude/helperFunctions.js";
import { ImageSourceBlock } from "./Dual/imageSourceBlock.js";
import { NodeMaterialConnectionPointCustomObject } from "../nodeMaterialConnectionPointCustomObject.js";
import { EngineStore } from "../../../Engines/engineStore.js";
import { editableInPropertyPage } from "../../../Decorators/nodeDecorator.js";
/**
* Block used to read a texture with triplanar mapping (see "boxmap" in https://iquilezles.org/articles/biplanar/)
*/
export class TriPlanarBlock extends NodeMaterialBlock {
/**
* Gets or sets the texture associated with the node
*/
get texture() {
if (this.source.isConnected) {
return (this.source.connectedPoint?.ownerBlock).texture;
}
return this._texture;
}
set texture(texture) {
if (this._texture === texture) {
return;
}
const scene = texture?.getScene() ?? EngineStore.LastCreatedScene;
if (!texture && scene) {
scene.markAllMaterialsAsDirty(1, (mat) => {
return mat.hasTexture(this._texture);
});
}
this._texture = texture;
if (texture && scene) {
scene.markAllMaterialsAsDirty(1, (mat) => {
return mat.hasTexture(texture);
});
}
}
/**
* Gets the textureY associated with the node
*/
get textureY() {
if (this.sourceY.isConnected) {
return (this.sourceY.connectedPoint?.ownerBlock).texture;
}
return null;
}
/**
* Gets the textureZ associated with the node
*/
get textureZ() {
if (this.sourceZ?.isConnected) {
return (this.sourceY.connectedPoint?.ownerBlock).texture;
}
return null;
}
_getImageSourceBlock(connectionPoint) {
return connectionPoint?.isConnected ? connectionPoint.connectedPoint.ownerBlock : null;
}
/**
* Gets the sampler name associated with this texture
*/
get samplerName() {
const imageSourceBlock = this._getImageSourceBlock(this.source);
if (imageSourceBlock) {
return imageSourceBlock.samplerName;
}
return this._samplerName;
}
/**
* Gets the samplerY name associated with this texture
*/
get samplerYName() {
return this._getImageSourceBlock(this.sourceY)?.samplerName ?? null;
}
/**
* Gets the samplerZ name associated with this texture
*/
get samplerZName() {
return this._getImageSourceBlock(this.sourceZ)?.samplerName ?? null;
}
/**
* Gets a boolean indicating that this block is linked to an ImageSourceBlock
*/
get hasImageSource() {
return this.source.isConnected;
}
/**
* Gets or sets a boolean indicating if content needs to be converted to gamma space
*/
set convertToGammaSpace(value) {
if (value === this._convertToGammaSpace) {
return;
}
this._convertToGammaSpace = value;
if (this.texture) {
const scene = this.texture.getScene() ?? EngineStore.LastCreatedScene;
scene?.markAllMaterialsAsDirty(1, (mat) => {
return mat.hasTexture(this.texture);
});
}
}
get convertToGammaSpace() {
return this._convertToGammaSpace;
}
/**
* Gets or sets a boolean indicating if content needs to be converted to linear space
*/
set convertToLinearSpace(value) {
if (value === this._convertToLinearSpace) {
return;
}
this._convertToLinearSpace = value;
if (this.texture) {
const scene = this.texture.getScene() ?? EngineStore.LastCreatedScene;
scene?.markAllMaterialsAsDirty(1, (mat) => {
return mat.hasTexture(this.texture);
});
}
}
get convertToLinearSpace() {
return this._convertToLinearSpace;
}
/**
* Create a new TriPlanarBlock
* @param name defines the block name
* @param hideSourceZ defines a boolean indicating that normal Z should not be used (false by default)
*/
constructor(name, hideSourceZ = false) {
super(name, NodeMaterialBlockTargets.Neutral);
/**
* Project the texture(s) for a better fit to a cube
*/
this.projectAsCube = false;
this._convertToGammaSpace = false;
this._convertToLinearSpace = false;
/**
* Gets or sets a boolean indicating if multiplication of texture with level should be disabled
*/
this.disableLevelMultiplication = false;
this.registerInput("position", NodeMaterialBlockConnectionPointTypes.AutoDetect, false);
this.registerInput("normal", NodeMaterialBlockConnectionPointTypes.AutoDetect, false);
this.registerInput("sharpness", NodeMaterialBlockConnectionPointTypes.Float, true);
this.registerInput("source", NodeMaterialBlockConnectionPointTypes.Object, true, NodeMaterialBlockTargets.VertexAndFragment, new NodeMaterialConnectionPointCustomObject("source", this, 0 /* NodeMaterialConnectionPointDirection.Input */, ImageSourceBlock, "ImageSourceBlock"));
this.registerInput("sourceY", NodeMaterialBlockConnectionPointTypes.Object, true, NodeMaterialBlockTargets.VertexAndFragment, new NodeMaterialConnectionPointCustomObject("sourceY", this, 0 /* NodeMaterialConnectionPointDirection.Input */, ImageSourceBlock, "ImageSourceBlock"));
if (!hideSourceZ) {
this.registerInput("sourceZ", NodeMaterialBlockConnectionPointTypes.Object, true, NodeMaterialBlockTargets.VertexAndFragment, new NodeMaterialConnectionPointCustomObject("sourceZ", this, 0 /* NodeMaterialConnectionPointDirection.Input */, ImageSourceBlock, "ImageSourceBlock"));
}
this.registerOutput("rgba", NodeMaterialBlockConnectionPointTypes.Color4, NodeMaterialBlockTargets.Neutral);
this.registerOutput("rgb", NodeMaterialBlockConnectionPointTypes.Color3, NodeMaterialBlockTargets.Neutral);
this.registerOutput("r", NodeMaterialBlockConnectionPointTypes.Float, NodeMaterialBlockTargets.Neutral);
this.registerOutput("g", NodeMaterialBlockConnectionPointTypes.Float, NodeMaterialBlockTargets.Neutral);
this.registerOutput("b", NodeMaterialBlockConnectionPointTypes.Float, NodeMaterialBlockTargets.Neutral);
this.registerOutput("a", NodeMaterialBlockConnectionPointTypes.Float, NodeMaterialBlockTargets.Neutral);
this.registerOutput("level", NodeMaterialBlockConnectionPointTypes.Float, NodeMaterialBlockTargets.Neutral);
this._inputs[0].addExcludedConnectionPointFromAllowedTypes(NodeMaterialBlockConnectionPointTypes.Color3 | NodeMaterialBlockConnectionPointTypes.Vector3 | NodeMaterialBlockConnectionPointTypes.Vector4);
this._inputs[1].addExcludedConnectionPointFromAllowedTypes(NodeMaterialBlockConnectionPointTypes.Color3 | NodeMaterialBlockConnectionPointTypes.Vector3 | NodeMaterialBlockConnectionPointTypes.Vector4);
}
/**
* Gets the current class name
* @returns the class name
*/
getClassName() {
return "TriPlanarBlock";
}
/**
* Gets the position input component
*/
get position() {
return this._inputs[0];
}
/**
* Gets the normal input component
*/
get normal() {
return this._inputs[1];
}
/**
* Gets the sharpness input component
*/
get sharpness() {
return this._inputs[2];
}
/**
* Gets the source input component
*/
get source() {
return this._inputs[3];
}
/**
* Gets the sourceY input component
*/
get sourceY() {
return this._inputs[4];
}
/**
* Gets the sourceZ input component
*/
get sourceZ() {
return this._inputs[5];
}
/**
* Gets the rgba output component
*/
get rgba() {
return this._outputs[0];
}
/**
* Gets the rgb output component
*/
get rgb() {
return this._outputs[1];
}
/**
* Gets the r output component
*/
get r() {
return this._outputs[2];
}
/**
* Gets the g output component
*/
get g() {
return this._outputs[3];
}
/**
* Gets the b output component
*/
get b() {
return this._outputs[4];
}
/**
* Gets the a output component
*/
get a() {
return this._outputs[5];
}
/**
* Gets the level output component
*/
get level() {
return this._outputs[6];
}
prepareDefines(mesh, nodeMaterial, defines) {
if (!defines._areTexturesDirty) {
return;
}
const toGamma = this.convertToGammaSpace && this.texture && !this.texture.gammaSpace;
const toLinear = this.convertToLinearSpace && this.texture && this.texture.gammaSpace;
// Not a bug... Name defines the texture space not the required conversion
defines.setValue(this._linearDefineName, toGamma, true);
defines.setValue(this._gammaDefineName, toLinear, true);
}
isReady() {
if (this.texture && !this.texture.isReadyOrNotBlocking()) {
return false;
}
return true;
}
bind(effect) {
if (!this.texture) {
return;
}
effect.setFloat(this._textureInfoName, this.texture.level);
if (!this._imageSource) {
effect.setTexture(this._samplerName, this.texture);
}
}
_samplerFunc(state) {
if (state.shaderLanguage === 1 /* ShaderLanguage.WGSL */) {
return "textureSample";
}
return "texture2D";
}
_generateTextureSample(textureName, uv, state) {
if (state.shaderLanguage === 1 /* ShaderLanguage.WGSL */) {
return `${this._samplerFunc(state)}(${textureName},${textureName + `Sampler`}, ${uv})`;
}
return `${this._samplerFunc(state)}(${textureName}, ${uv})`;
}
_generateTextureLookup(state) {
const samplerName = this.samplerName;
const samplerYName = this.samplerYName ?? samplerName;
const samplerZName = this.samplerZName ?? samplerName;
const sharpness = this.sharpness.isConnected ? this.sharpness.associatedVariableName : "1.0";
const x = state._getFreeVariableName("x");
const y = state._getFreeVariableName("y");
const z = state._getFreeVariableName("z");
const w = state._getFreeVariableName("w");
const n = state._getFreeVariableName("n");
const uvx = state._getFreeVariableName("uvx");
const uvy = state._getFreeVariableName("uvy");
const uvz = state._getFreeVariableName("uvz");
state.compilationString += `
${state._declareLocalVar(n, NodeMaterialBlockConnectionPointTypes.Vector3)} = ${this.normal.associatedVariableName}.xyz;
${state._declareLocalVar(uvx, NodeMaterialBlockConnectionPointTypes.Vector2)} = ${this.position.associatedVariableName}.yz;
${state._declareLocalVar(uvy, NodeMaterialBlockConnectionPointTypes.Vector2)} = ${this.position.associatedVariableName}.zx;
${state._declareLocalVar(uvz, NodeMaterialBlockConnectionPointTypes.Vector2)} = ${this.position.associatedVariableName}.xy;
`;
if (this.projectAsCube) {
state.compilationString += `
${uvx}.xy = ${uvx}.yx;
if (${n}.x >= 0.0) {
${uvx}.x = -${uvx}.x;
}
if (${n}.y < 0.0) {
${uvy}.y = -${uvy}.y;
}
if (${n}.z < 0.0) {
${uvz}.x = -${uvz}.x;
}
`;
}
const suffix = state.fSuffix;
state.compilationString += `
${state._declareLocalVar(x, NodeMaterialBlockConnectionPointTypes.Vector4)} = ${this._generateTextureSample(samplerName, uvx, state)};
${state._declareLocalVar(y, NodeMaterialBlockConnectionPointTypes.Vector4)} = ${this._generateTextureSample(samplerYName, uvy, state)};
${state._declareLocalVar(z, NodeMaterialBlockConnectionPointTypes.Vector4)} = ${this._generateTextureSample(samplerZName, uvz, state)};
// blend weights
${state._declareLocalVar(w, NodeMaterialBlockConnectionPointTypes.Vector3)} = pow(abs(${n}), vec3${suffix}(${sharpness}));
// blend and return
${state._declareLocalVar(this._tempTextureRead, NodeMaterialBlockConnectionPointTypes.Vector4)} = (${x}*${w}.x + ${y}*${w}.y + ${z}*${w}.z) / (${w}.x + ${w}.y + ${w}.z);
`;
}
_generateConversionCode(state, output, swizzle) {
let vecSpecifier = "";
if (state.shaderLanguage === 1 /* ShaderLanguage.WGSL */ &&
(output.type === NodeMaterialBlockConnectionPointTypes.Vector3 || output.type === NodeMaterialBlockConnectionPointTypes.Color3)) {
vecSpecifier = "Vec3";
}
if (swizzle !== "a") {
// no conversion if the output is "a" (alpha)
if (!this.texture || !this.texture.gammaSpace) {
state.compilationString += `#ifdef ${this._linearDefineName}
${output.associatedVariableName} = toGammaSpace${vecSpecifier}(${output.associatedVariableName});
#endif
`;
}
state.compilationString += `#ifdef ${this._gammaDefineName}
${output.associatedVariableName} = toLinearSpace${vecSpecifier}(${output.associatedVariableName});
#endif
`;
}
}
_writeOutput(state, output, swizzle) {
let complement = "";
if (!this.disableLevelMultiplication) {
complement = ` * ${state.shaderLanguage === 1 /* ShaderLanguage.WGSL */ ? "uniforms." : ""}${this._textureInfoName}`;
}
state.compilationString += `${state._declareOutput(output)} = ${this._tempTextureRead}.${swizzle}${complement};\n`;
this._generateConversionCode(state, output, swizzle);
}
_buildBlock(state) {
super._buildBlock(state);
if (this.source.isConnected) {
this._imageSource = this.source.connectedPoint.ownerBlock;
}
else {
this._imageSource = null;
}
this._textureInfoName = state._getFreeVariableName("textureInfoName");
this.level.associatedVariableName = (state.shaderLanguage === 1 /* ShaderLanguage.WGSL */ ? "uniforms." : "") + this._textureInfoName;
this._tempTextureRead = state._getFreeVariableName("tempTextureRead");
this._linearDefineName = state._getFreeDefineName("ISLINEAR");
this._gammaDefineName = state._getFreeDefineName("ISGAMMA");
if (!this._imageSource) {
this._samplerName = state._getFreeVariableName(this.name + "Texture");
state._emit2DSampler(this._samplerName);
}
// Declarations
state.sharedData.blockingBlocks.push(this);
state.sharedData.textureBlocks.push(this);
state.sharedData.blocksWithDefines.push(this);
state.sharedData.bindableBlocks.push(this);
const comments = `//${this.name}`;
state._emitFunctionFromInclude("helperFunctions", comments);
state._emitUniformFromString(this._textureInfoName, NodeMaterialBlockConnectionPointTypes.Float);
this._generateTextureLookup(state);
for (const output of this._outputs) {
if (output.hasEndpoints && output.name !== "level") {
this._writeOutput(state, output, output.name);
}
}
return this;
}
_dumpPropertiesCode() {
let codeString = super._dumpPropertiesCode();
codeString += `${this._codeVariableName}.convertToGammaSpace = ${this.convertToGammaSpace};\n`;
codeString += `${this._codeVariableName}.convertToLinearSpace = ${this.convertToLinearSpace};\n`;
codeString += `${this._codeVariableName}.disableLevelMultiplication = ${this.disableLevelMultiplication};\n`;
codeString += `${this._codeVariableName}.projectAsCube = ${this.projectAsCube};\n`;
if (!this.texture) {
return codeString;
}
codeString += `${this._codeVariableName}.texture = new BABYLON.Texture("${this.texture.name}", null, ${this.texture.noMipmap}, ${this.texture.invertY}, ${this.texture.samplingMode});\n`;
codeString += `${this._codeVariableName}.texture.wrapU = ${this.texture.wrapU};\n`;
codeString += `${this._codeVariableName}.texture.wrapV = ${this.texture.wrapV};\n`;
codeString += `${this._codeVariableName}.texture.uAng = ${this.texture.uAng};\n`;
codeString += `${this._codeVariableName}.texture.vAng = ${this.texture.vAng};\n`;
codeString += `${this._codeVariableName}.texture.wAng = ${this.texture.wAng};\n`;
codeString += `${this._codeVariableName}.texture.uOffset = ${this.texture.uOffset};\n`;
codeString += `${this._codeVariableName}.texture.vOffset = ${this.texture.vOffset};\n`;
codeString += `${this._codeVariableName}.texture.uScale = ${this.texture.uScale};\n`;
codeString += `${this._codeVariableName}.texture.vScale = ${this.texture.vScale};\n`;
codeString += `${this._codeVariableName}.texture.coordinatesMode = ${this.texture.coordinatesMode};\n`;
return codeString;
}
serialize() {
const serializationObject = super.serialize();
serializationObject.convertToGammaSpace = this.convertToGammaSpace;
serializationObject.convertToLinearSpace = this.convertToLinearSpace;
serializationObject.disableLevelMultiplication = this.disableLevelMultiplication;
serializationObject.projectAsCube = this.projectAsCube;
if (!this.hasImageSource && this.texture && !this.texture.isRenderTarget && this.texture.getClassName() !== "VideoTexture") {
serializationObject.texture = this.texture.serialize();
}
return serializationObject;
}
_deserialize(serializationObject, scene, rootUrl) {
super._deserialize(serializationObject, scene, rootUrl);
this.convertToGammaSpace = serializationObject.convertToGammaSpace;
this.convertToLinearSpace = !!serializationObject.convertToLinearSpace;
this.disableLevelMultiplication = !!serializationObject.disableLevelMultiplication;
this.projectAsCube = !!serializationObject.projectAsCube;
if (serializationObject.texture && !NodeMaterial.IgnoreTexturesAtLoadTime && serializationObject.texture.url !== undefined) {
rootUrl = serializationObject.texture.url.indexOf("data:") === 0 ? "" : rootUrl;
this.texture = Texture.Parse(serializationObject.texture, scene, rootUrl);
}
}
}
__decorate([
editableInPropertyPage("Project as cube", 0 /* PropertyTypeForEdition.Boolean */, "ADVANCED", { embedded: true, notifiers: { update: true } })
], TriPlanarBlock.prototype, "projectAsCube", void 0);
RegisterClass("BABYLON.TriPlanarBlock", TriPlanarBlock);
//# sourceMappingURL=triPlanarBlock.js.map