@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.
206 lines (205 loc) • 7.96 kB
JavaScript
import { ProceduralTexture } from "./Procedurals/proceduralTexture.js";
const _ShaderName = "textureMerger";
/**
* @internal
* Check if a channel input is a texture input
* @param input The channel input to check
* @returns True if the input is a texture input, false otherwise
*/
function IsTextureInput(input) {
return "texture" in input;
}
/**
* @internal
* Check if a channel input is a constant input
* @param input The channel input to check
* @returns True if the input is a constant input, false otherwise
*/
function IsConstantInput(input) {
return "value" in input;
}
/**
* @internal
* Copy texture transformation properties from one texture to another
* @param source The source texture
* @param destination The destination texture
*/
function CopyTextureTransform(source, destination) {
destination.uOffset = source.uOffset;
destination.vOffset = source.vOffset;
destination.uScale = source.uScale;
destination.vScale = source.vScale;
destination.uAng = source.uAng;
destination.vAng = source.vAng;
destination.wAng = source.wAng;
destination.uRotationCenter = source.uRotationCenter;
destination.vRotationCenter = source.vRotationCenter;
}
/**
* @internal
* Merge multiple texture channels into a single texture
* @param name Name for the resulting texture
* @param config Merge configuration
* @param scene Scene to create the texture in
* @returns The merged texture
*/
export async function MergeTexturesAsync(name, config, scene) {
const channels = [config.red, config.green, config.blue, config.alpha];
const textureInputs = [];
const textureInputMap = []; // Maps channel index to texture input index (-1 for constants)
// Collect unique textures and validate inputs
for (let channelIndex = 0; channelIndex < 4; channelIndex++) {
const channel = channels[channelIndex];
if (channel) {
if (IsTextureInput(channel)) {
// Validate source channel
if (channel.sourceChannel < 0 || channel.sourceChannel > 3) {
throw new Error("Source channel must be between 0 and 3 (R, G, B, A)");
}
// Find or add texture to inputs
let textureIndex = textureInputs.indexOf(channel.texture);
if (textureIndex === -1) {
textureIndex = textureInputs.length;
textureInputs.push(channel.texture);
}
textureInputMap[channelIndex] = textureIndex;
}
else if (IsConstantInput(channel)) {
// Validate constant value
if (channel.value < 0 || channel.value > 1) {
throw new Error("Constant value must be between 0.0 and 1.0");
}
textureInputMap[channelIndex] = -1;
}
else {
throw new Error("Invalid channel input configuration");
}
}
else {
textureInputMap[channelIndex] = -1;
}
}
// Determine output size
let outputSize = config.outputSize;
if (!outputSize && textureInputs.length > 0) {
// Use the largest texture size
let maxSize = 0;
for (const texture of textureInputs) {
const size = texture.getSize();
const currentSize = Math.max(size.width, size.height);
if (currentSize > maxSize) {
maxSize = currentSize;
outputSize = size.width === size.height ? maxSize : size;
}
}
}
outputSize = outputSize || 512; // Fallback size
// Generate shader defines
const defines = [];
const usedTextures = new Set();
for (let channelIndex = 0; channelIndex < 4; channelIndex++) {
const channel = channels[channelIndex];
const channelName = ["RED", "GREEN", "BLUE", "ALPHA"][channelIndex];
if (channel && IsTextureInput(channel)) {
defines.push(`${channelName}_FROM_TEXTURE`);
const textureIndex = textureInputMap[channelIndex];
usedTextures.add(textureIndex);
}
}
// Add texture defines for used textures
usedTextures.forEach((textureIndex) => {
defines.push(`USE_TEXTURE${textureIndex}`);
});
// Create the procedural texture
const outputTextureOptions = {
type: 2,
format: 5,
samplingMode: 1,
generateDepthBuffer: false,
generateMipMaps: false,
shaderLanguage: scene.getEngine().isWebGPU ? 1 /* ShaderLanguage.WGSL */ : 0 /* ShaderLanguage.GLSL */,
extraInitializationsAsync: async () => {
if (scene.getEngine().isWebGPU) {
await Promise.all([import("../../ShadersWGSL/textureMerger.fragment.js")]);
}
else {
await Promise.all([import("../../Shaders/textureMerger.fragment.js")]);
}
},
};
const proceduralTexture = new ProceduralTexture(name, outputSize, _ShaderName, scene, outputTextureOptions);
proceduralTexture.refreshRate = -1; // Do not auto-refresh
// Set the defines
proceduralTexture.defines = defines.length > 0 ? "#define " + defines.join("\n#define ") + "\n" : "";
// Set up texture inputs
for (let i = 0; i < textureInputs.length; i++) {
CopyTextureTransform(textureInputs[i], proceduralTexture);
proceduralTexture.setTexture(`inputTexture${i}`, textureInputs[i]);
}
// Set up channel configuration
for (let channelIndex = 0; channelIndex < 4; channelIndex++) {
const channel = channels[channelIndex];
const channelName = ["red", "green", "blue", "alpha"][channelIndex];
if (channel && IsTextureInput(channel)) {
const textureIndex = textureInputMap[channelIndex];
proceduralTexture.setInt(`${channelName}TextureIndex`, textureIndex);
proceduralTexture.setInt(`${channelName}SourceChannel`, channel.sourceChannel);
}
else {
// Use constant value (either provided or default)
let constantValue;
if (channel && IsConstantInput(channel)) {
constantValue = channel.value;
}
else {
// Use default values: 0 for RGB, 1 for alpha
constantValue = channelIndex === 3 ? 1.0 : 0.0;
}
proceduralTexture.setFloat(`${channelName}ConstantValue`, constantValue);
}
}
return await new Promise((resolve, reject) => {
// Compile and render
proceduralTexture.executeWhenReady(() => {
try {
proceduralTexture.render();
resolve(proceduralTexture);
}
catch (error) {
reject(error instanceof Error ? error : new Error(String(error)));
}
});
});
}
/**
* @internal
* Create a texture input configuration
* @param texture The texture to read from
* @param sourceChannel The channel to read (0=R, 1=G, 2=B, 3=A)
* @returns Texture channel input configuration
*/
export function CreateTextureInput(texture, sourceChannel) {
return { texture, sourceChannel };
}
/**
* @internal
* Create a constant value input configuration
* @param value The constant value (0.0-1.0)
* @returns Constant channel input configuration
*/
export function CreateConstantInput(value) {
return { value };
}
/**
* @internal
* Create a simple RGBA channel packing configuration
* @param red Input for red channel
* @param green Input for green channel (optional, defaults to 0)
* @param blue Input for blue channel (optional, defaults to 0)
* @param alpha Input for alpha channel (optional, defaults to 1)
* @returns Texture merge configuration
*/
export function CreateRGBAConfiguration(red, green, blue, alpha) {
return { red, green, blue, alpha };
}
//# sourceMappingURL=textureMerger.js.map