@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.
174 lines • 7.2 kB
JavaScript
import { NodeMaterialBlock } from "../../nodeMaterialBlock.js";
import { NodeMaterialBlockTargets } from "../../Enums/nodeMaterialBlockTargets.js";
import { NodeMaterialBlockConnectionPointTypes } from "../../Enums/nodeMaterialBlockConnectionPointTypes.js";
import { NodeMaterialSystemValues } from "../../Enums/nodeMaterialSystemValues.js";
import { InputBlock } from "../Input/inputBlock.js";
import { RegisterClass } from "../../../../Misc/typeStore.js";
/**
* Block used to add support for instances
* @see https://doc.babylonjs.com/features/featuresDeepDive/mesh/copies/instances
*/
export class InstancesBlock extends NodeMaterialBlock {
/**
* Creates a new InstancesBlock
* @param name defines the block name
*/
constructor(name) {
super(name, NodeMaterialBlockTargets.Vertex);
this.registerInput("world0", NodeMaterialBlockConnectionPointTypes.Vector4);
this.registerInput("world1", NodeMaterialBlockConnectionPointTypes.Vector4);
this.registerInput("world2", NodeMaterialBlockConnectionPointTypes.Vector4);
this.registerInput("world3", NodeMaterialBlockConnectionPointTypes.Vector4);
this.registerInput("world", NodeMaterialBlockConnectionPointTypes.Matrix, true);
this.registerOutput("output", NodeMaterialBlockConnectionPointTypes.Matrix);
this.registerOutput("instanceID", NodeMaterialBlockConnectionPointTypes.Float);
}
/**
* Gets the current class name
* @returns the class name
*/
getClassName() {
return "InstancesBlock";
}
/**
* Gets the first world row input component
*/
get world0() {
return this._inputs[0];
}
/**
* Gets the second world row input component
*/
get world1() {
return this._inputs[1];
}
/**
* Gets the third world row input component
*/
get world2() {
return this._inputs[2];
}
/**
* Gets the forth world row input component
*/
get world3() {
return this._inputs[3];
}
/**
* Gets the world input component
*/
get world() {
return this._inputs[4];
}
/**
* Gets the output component
*/
get output() {
return this._outputs[0];
}
/**
* Gets the instanceID component
*/
// eslint-disable-next-line @typescript-eslint/naming-convention
get instanceID() {
return this._outputs[1];
}
autoConfigure(material, additionalFilteringInfo = () => true) {
if (!this.world0.connectedPoint) {
let world0Input = material.getInputBlockByPredicate((b) => b.isAttribute && b.name === "world0" && additionalFilteringInfo(b));
if (!world0Input) {
world0Input = new InputBlock("world0");
world0Input.setAsAttribute("world0");
}
world0Input.output.connectTo(this.world0);
}
if (!this.world1.connectedPoint) {
let world1Input = material.getInputBlockByPredicate((b) => b.isAttribute && b.name === "world1" && additionalFilteringInfo(b));
if (!world1Input) {
world1Input = new InputBlock("world1");
world1Input.setAsAttribute("world1");
}
world1Input.output.connectTo(this.world1);
}
if (!this.world2.connectedPoint) {
let world2Input = material.getInputBlockByPredicate((b) => b.isAttribute && b.name === "world2" && additionalFilteringInfo(b));
if (!world2Input) {
world2Input = new InputBlock("world2");
world2Input.setAsAttribute("world2");
}
world2Input.output.connectTo(this.world2);
}
if (!this.world3.connectedPoint) {
let world3Input = material.getInputBlockByPredicate((b) => b.isAttribute && b.name === "world3" && additionalFilteringInfo(b));
if (!world3Input) {
world3Input = new InputBlock("world3");
world3Input.setAsAttribute("world3");
}
world3Input.output.connectTo(this.world3);
}
if (!this.world.connectedPoint) {
let worldInput = material.getInputBlockByPredicate((b) => b.isAttribute && b.name === "world" && additionalFilteringInfo(b));
if (!worldInput) {
worldInput = new InputBlock("world");
worldInput.setAsSystemValue(NodeMaterialSystemValues.World);
}
worldInput.output.connectTo(this.world);
}
this.world.define = "!INSTANCES || THIN_INSTANCES";
}
prepareDefines(defines, nodeMaterial, mesh, useInstances = false, subMesh) {
let changed = false;
if (defines["INSTANCES"] !== useInstances) {
defines.setValue("INSTANCES", useInstances);
changed = true;
}
if (subMesh && defines["THIN_INSTANCES"] !== !!subMesh?.getRenderingMesh().hasThinInstances) {
defines.setValue("THIN_INSTANCES", !!subMesh?.getRenderingMesh().hasThinInstances);
changed = true;
}
if (changed) {
defines.markAsUnprocessed();
}
}
_buildBlock(state) {
super._buildBlock(state);
const engine = state.sharedData.scene.getEngine();
// Register for defines
state.sharedData.blocksWithDefines.push(this);
// Emit code
const output = this._outputs[0];
const instanceID = this._outputs[1];
const world0 = this.world0;
const world1 = this.world1;
const world2 = this.world2;
const world3 = this.world3;
let mat4 = "mat4";
let instance = "gl_InstanceID";
let floatCast = "float";
if (state.shaderLanguage === 1 /* ShaderLanguage.WGSL */) {
mat4 = "mat4x4f";
instance = "vertexInputs.instanceIndex";
floatCast = "f32";
}
state.compilationString += `#ifdef INSTANCES\n`;
state.compilationString +=
state._declareOutput(output) +
` = ${mat4}(${world0.associatedVariableName}, ${world1.associatedVariableName}, ${world2.associatedVariableName}, ${world3.associatedVariableName});\n`;
state.compilationString += `#ifdef THIN_INSTANCES\n`;
state.compilationString += `${output.associatedVariableName} = ${this.world.associatedVariableName} * ${output.associatedVariableName};\n`;
state.compilationString += `#endif\n`;
if (engine._caps.canUseGLInstanceID) {
state.compilationString += state._declareOutput(instanceID) + ` = ${floatCast}(${instance});\n`;
}
else {
state.compilationString += state._declareOutput(instanceID) + ` = 0.0;\n`;
}
state.compilationString += `#else\n`;
state.compilationString += state._declareOutput(output) + ` = ${this.world.associatedVariableName};\n`;
state.compilationString += state._declareOutput(instanceID) + ` = 0.0;\n`;
state.compilationString += `#endif\n`;
return this;
}
}
RegisterClass("BABYLON.InstancesBlock", InstancesBlock);
//# sourceMappingURL=instancesBlock.js.map