@babylonjs/viewer
Version:
The Babylon Viewer aims to simplify a specific but common Babylon.js use case: loading, viewing, and interacting with a 3D model.
1,095 lines (1,090 loc) • 44.1 kB
JavaScript
import { G as SceneComponentConstants, b as Tools, T as Texture, C as Constants, O as Observable, H as EngineStore, J as DrawWrapper, m as VertexBuffer, h as RenderTargetTexture, t as Material, u as __decorate, v as serialize, R as RegisterClass, i as Vector4, L as Logger, k as Engine, P as PostProcess, V as Vector3, _ as _WarnImport } from './index-FzOfPXLV.esm.js';
import { R as RawTexture } from './rawTexture-B2DimmQ5.esm.js';
/**
* Defines the Procedural Texture scene component responsible to manage any Procedural Texture
* in a given scene.
*/
class ProceduralTextureSceneComponent {
/**
* Creates a new instance of the component for the given scene
* @param scene Defines the scene to register the component in
*/
constructor(scene) {
/**
* The component name helpful to identify the component in the list of scene components.
*/
this.name = SceneComponentConstants.NAME_PROCEDURALTEXTURE;
this.scene = scene;
}
/**
* Registers the component in a given scene
*/
register() {
this.scene._beforeClearStage.registerStep(SceneComponentConstants.STEP_BEFORECLEAR_PROCEDURALTEXTURE, this, this._beforeClear);
}
/**
* Rebuilds the elements related to this component in case of
* context lost for instance.
*/
rebuild() {
// Nothing to do here.
}
/**
* Disposes the component and the associated resources.
*/
dispose() {
// Nothing to do here.
}
_beforeClear() {
if (this.scene.proceduralTexturesEnabled) {
Tools.StartPerformanceCounter("Procedural textures", this.scene.proceduralTextures.length > 0);
for (let proceduralIndex = 0; proceduralIndex < this.scene.proceduralTextures.length; proceduralIndex++) {
const proceduralTexture = this.scene.proceduralTextures[proceduralIndex];
if (proceduralTexture._shouldRender()) {
proceduralTexture.render();
}
}
Tools.EndPerformanceCounter("Procedural textures", this.scene.proceduralTextures.length > 0);
}
}
}
/**
* Procedural texturing is a way to programmatically create a texture. There are 2 types of procedural textures: code-only, and code that references some classic 2D images, sometimes calmpler' images.
* This is the base class of any Procedural texture and contains most of the shareable code.
* @see https://doc.babylonjs.com/features/featuresDeepDive/materials/using/proceduralTextures
*/
class ProceduralTexture extends Texture {
/**
* Gets the shader language type used to generate vertex and fragment source code.
*/
get shaderLanguage() {
return this._shaderLanguage;
}
/**
* Instantiates a new procedural texture.
* Procedural texturing is a way to programmatically create a texture. There are 2 types of procedural textures: code-only, and code that references some classic 2D images, sometimes called 'refMaps' or 'sampler' images.
* This is the base class of any Procedural texture and contains most of the shareable code.
* @see https://doc.babylonjs.com/features/featuresDeepDive/materials/using/proceduralTextures
* @param name Define the name of the texture
* @param size Define the size of the texture to create
* @param fragment Define the fragment shader to use to generate the texture or null if it is defined later:
* * object: \{ fragmentElement: "fragmentShaderCode" \}, used with shader code in script tags
* * object: \{ fragmentSource: "fragment shader code string" \}, the string contains the shader code
* * string: the string contains a name "XXX" to lookup in Effect.ShadersStore["XXXFragmentShader"]
* @param scene Define the scene the texture belongs to
* @param fallbackTexture Define a fallback texture in case there were issues to create the custom texture
* @param generateMipMaps Define if the texture should creates mip maps or not
* @param isCube Define if the texture is a cube texture or not (this will render each faces of the cube)
* @param textureType The FBO internal texture type
*/
constructor(name, size, fragment, scene, fallbackTexture = null, generateMipMaps = true, isCube = false, textureType = Constants.TEXTURETYPE_UNSIGNED_BYTE) {
super(null, scene, !generateMipMaps);
/**
* Define if the texture is enabled or not (disabled texture will not render)
*/
this.isEnabled = true;
/**
* Define if the texture must be cleared before rendering (default is true)
*/
this.autoClear = true;
/**
* Event raised when the texture is generated
*/
this.onGeneratedObservable = new Observable();
/**
* Event raised before the texture is generated
*/
this.onBeforeGenerationObservable = new Observable();
/**
* Gets or sets the node material used to create this texture (null if the texture was manually created)
*/
this.nodeMaterialSource = null;
/**
* Define the list of custom preprocessor defines used in the shader
*/
this.defines = "";
/** @internal */
this._textures = {};
this._currentRefreshId = -1;
this._frameId = -1;
this._refreshRate = 1;
this._vertexBuffers = {};
this._uniforms = new Array();
this._samplers = new Array();
this._floats = {};
this._ints = {};
this._floatsArrays = {};
this._colors3 = {};
this._colors4 = {};
this._vectors2 = {};
this._vectors3 = {};
this._vectors4 = {};
this._matrices = {};
this._fallbackTextureUsed = false;
this._cachedDefines = null;
this._contentUpdateId = -1;
this._rtWrapper = null;
if (fallbackTexture !== null && !(fallbackTexture instanceof Texture)) {
this._options = fallbackTexture;
this._fallbackTexture = fallbackTexture.fallbackTexture ?? null;
}
else {
this._options = {};
this._fallbackTexture = fallbackTexture;
}
this._shaderLanguage = this._options.shaderLanguage ?? 0 /* ShaderLanguage.GLSL */;
scene = this.getScene() || EngineStore.LastCreatedScene;
let component = scene._getComponent(SceneComponentConstants.NAME_PROCEDURALTEXTURE);
if (!component) {
component = new ProceduralTextureSceneComponent(scene);
scene._addComponent(component);
}
scene.proceduralTextures.push(this);
this._fullEngine = scene.getEngine();
this.name = name;
this.isRenderTarget = true;
this._size = size;
this._textureType = textureType;
this._generateMipMaps = generateMipMaps;
this._drawWrapper = new DrawWrapper(this._fullEngine);
this.setFragment(fragment);
const rtWrapper = this._createRtWrapper(isCube, size, generateMipMaps, textureType);
this._texture = rtWrapper.texture;
// VBO
const vertices = [];
vertices.push(1, 1);
vertices.push(-1, 1);
vertices.push(-1, -1);
vertices.push(1, -1);
this._vertexBuffers[VertexBuffer.PositionKind] = new VertexBuffer(this._fullEngine, vertices, VertexBuffer.PositionKind, false, false, 2);
this._createIndexBuffer();
}
_createRtWrapper(isCube, size, generateMipMaps, textureType) {
if (isCube) {
this._rtWrapper = this._fullEngine.createRenderTargetCubeTexture(size, {
generateMipMaps: generateMipMaps,
generateDepthBuffer: false,
generateStencilBuffer: false,
type: textureType,
...this._options,
});
this.setFloat("face", 0);
}
else {
this._rtWrapper = this._fullEngine.createRenderTargetTexture(size, {
generateMipMaps: generateMipMaps,
generateDepthBuffer: false,
generateStencilBuffer: false,
type: textureType,
...this._options,
});
if (this._rtWrapper.is3D) {
this.setFloat("layer", 0);
this.setInt("layerNum", 0);
}
}
return this._rtWrapper;
}
/**
* The effect that is created when initializing the post process.
* @returns The created effect corresponding the postprocess.
*/
getEffect() {
return this._drawWrapper.effect;
}
/**
* @internal
*/
_setEffect(effect) {
this._drawWrapper.effect = effect;
}
/**
* Gets texture content (Use this function wisely as reading from a texture can be slow)
* @returns an ArrayBufferView promise (Uint8Array or Float32Array)
*/
getContent() {
if (this._contentData && this._frameId === this._contentUpdateId) {
return this._contentData;
}
if (this._contentData) {
// eslint-disable-next-line @typescript-eslint/no-floating-promises, github/no-then
this._contentData.then((buffer) => {
this._contentData = this.readPixels(0, 0, buffer);
this._contentUpdateId = this._frameId;
});
}
else {
this._contentData = this.readPixels(0, 0);
this._contentUpdateId = this._frameId;
}
return this._contentData;
}
_createIndexBuffer() {
const engine = this._fullEngine;
// Indices
const indices = [];
indices.push(0);
indices.push(1);
indices.push(2);
indices.push(0);
indices.push(2);
indices.push(3);
this._indexBuffer = engine.createIndexBuffer(indices);
}
/** @internal */
_rebuild() {
const vb = this._vertexBuffers[VertexBuffer.PositionKind];
if (vb) {
vb._rebuild();
}
this._createIndexBuffer();
if (this.refreshRate === RenderTargetTexture.REFRESHRATE_RENDER_ONCE) {
this.refreshRate = RenderTargetTexture.REFRESHRATE_RENDER_ONCE;
}
}
/**
* Resets the texture in order to recreate its associated resources.
* This can be called in case of context loss or if you change the shader code and need to regenerate the texture with the new code
*/
reset() {
this._drawWrapper.effect?.dispose();
this._drawWrapper.effect = null;
this._cachedDefines = null;
}
_getDefines() {
return this.defines;
}
/**
* Executes a function when the texture will be ready to be drawn.
* @param func The callback to be used.
*/
executeWhenReady(func) {
if (this.isReady()) {
func(this);
return;
}
const effect = this.getEffect();
if (effect) {
effect.executeWhenCompiled(() => {
func(this);
});
}
}
/**
* Is the texture ready to be used ? (rendered at least once)
* @returns true if ready, otherwise, false.
*/
isReady() {
const engine = this._fullEngine;
if (this.nodeMaterialSource) {
return this._drawWrapper.effect.isReady();
}
if (!this._fragment) {
return false;
}
if (this._fallbackTextureUsed) {
return true;
}
if (!this._texture) {
return false;
}
const defines = this._getDefines();
if (this._drawWrapper.effect && defines === this._cachedDefines && this._drawWrapper.effect.isReady()) {
return true;
}
const shaders = {
vertex: "procedural",
fragmentElement: this._fragment.fragmentElement,
fragmentSource: this._fragment.fragmentSource,
fragment: typeof this._fragment === "string" ? this._fragment : undefined,
};
if (this._cachedDefines !== defines) {
this._cachedDefines = defines;
this._drawWrapper.effect = engine.createEffect(shaders, [VertexBuffer.PositionKind], this._uniforms, this._samplers, defines, undefined, undefined, () => {
this._rtWrapper?.dispose();
this._rtWrapper = this._texture = null;
if (this._fallbackTexture) {
this._texture = this._fallbackTexture._texture;
if (this._texture) {
this._texture.incrementReferences();
}
}
this._fallbackTextureUsed = true;
}, undefined, this._shaderLanguage, async () => {
if (this._options.extraInitializationsAsync) {
if (this.shaderLanguage === 1 /* ShaderLanguage.WGSL */) {
await Promise.all([import('./procedural.vertex-BQn90HXs.esm.js'), this._options.extraInitializationsAsync()]);
}
else {
await Promise.all([import('./procedural.vertex-DyTU7-MG.esm.js'), this._options.extraInitializationsAsync()]);
}
}
else {
if (this.shaderLanguage === 1 /* ShaderLanguage.WGSL */) {
await import('./procedural.vertex-BQn90HXs.esm.js');
}
else {
await import('./procedural.vertex-DyTU7-MG.esm.js');
}
}
});
}
return this._drawWrapper.effect.isReady();
}
/**
* Resets the refresh counter of the texture and start bak from scratch.
* Could be useful to regenerate the texture if it is setup to render only once.
*/
resetRefreshCounter() {
this._currentRefreshId = -1;
}
/**
* Set the fragment shader to use in order to render the texture.
* @param fragment This can be set to a path (into the shader store) or to a json object containing a fragmentElement property.
*/
setFragment(fragment) {
this._fragment = fragment;
}
/**
* Define the refresh rate of the texture or the rendering frequency.
* Use 0 to render just once, 1 to render on every frame, 2 to render every two frames and so on...
*/
get refreshRate() {
return this._refreshRate;
}
set refreshRate(value) {
this._refreshRate = value;
this.resetRefreshCounter();
}
/** @internal */
_shouldRender() {
if (!this.isEnabled || !this.isReady() || !this._texture) {
if (this._texture) {
this._texture.isReady = false;
}
return false;
}
if (this._fallbackTextureUsed) {
return false;
}
if (this._currentRefreshId === -1) {
// At least render once
this._currentRefreshId = 1;
this._frameId++;
return true;
}
if (this.refreshRate === this._currentRefreshId) {
this._currentRefreshId = 1;
this._frameId++;
return true;
}
this._currentRefreshId++;
return false;
}
/**
* Get the size the texture is rendering at.
* @returns the size (on cube texture it is always squared)
*/
getRenderSize() {
return this._size;
}
/**
* Resize the texture to new value.
* @param size Define the new size the texture should have
* @param generateMipMaps Define whether the new texture should create mip maps
*/
resize(size, generateMipMaps) {
if (this._fallbackTextureUsed || !this._rtWrapper || !this._texture) {
return;
}
const isCube = this._texture.isCube;
this._rtWrapper.dispose();
const rtWrapper = this._createRtWrapper(isCube, size, generateMipMaps, this._textureType);
this._texture = rtWrapper.texture;
// Update properties
this._size = size;
this._generateMipMaps = generateMipMaps;
}
_checkUniform(uniformName) {
if (this._uniforms.indexOf(uniformName) === -1) {
this._uniforms.push(uniformName);
}
}
/**
* Set a texture in the shader program used to render.
* @param name Define the name of the uniform samplers as defined in the shader
* @param texture Define the texture to bind to this sampler
* @returns the texture itself allowing "fluent" like uniform updates
*/
setTexture(name, texture) {
if (this._samplers.indexOf(name) === -1) {
this._samplers.push(name);
}
this._textures[name] = texture;
return this;
}
/**
* Set a float in the shader.
* @param name Define the name of the uniform as defined in the shader
* @param value Define the value to give to the uniform
* @returns the texture itself allowing "fluent" like uniform updates
*/
setFloat(name, value) {
this._checkUniform(name);
this._floats[name] = value;
return this;
}
/**
* Set a int in the shader.
* @param name Define the name of the uniform as defined in the shader
* @param value Define the value to give to the uniform
* @returns the texture itself allowing "fluent" like uniform updates
*/
setInt(name, value) {
this._checkUniform(name);
this._ints[name] = value;
return this;
}
/**
* Set an array of floats in the shader.
* @param name Define the name of the uniform as defined in the shader
* @param value Define the value to give to the uniform
* @returns the texture itself allowing "fluent" like uniform updates
*/
setFloats(name, value) {
this._checkUniform(name);
this._floatsArrays[name] = value;
return this;
}
/**
* Set a vec3 in the shader from a Color3.
* @param name Define the name of the uniform as defined in the shader
* @param value Define the value to give to the uniform
* @returns the texture itself allowing "fluent" like uniform updates
*/
setColor3(name, value) {
this._checkUniform(name);
this._colors3[name] = value;
return this;
}
/**
* Set a vec4 in the shader from a Color4.
* @param name Define the name of the uniform as defined in the shader
* @param value Define the value to give to the uniform
* @returns the texture itself allowing "fluent" like uniform updates
*/
setColor4(name, value) {
this._checkUniform(name);
this._colors4[name] = value;
return this;
}
/**
* Set a vec2 in the shader from a Vector2.
* @param name Define the name of the uniform as defined in the shader
* @param value Define the value to give to the uniform
* @returns the texture itself allowing "fluent" like uniform updates
*/
setVector2(name, value) {
this._checkUniform(name);
this._vectors2[name] = value;
return this;
}
/**
* Set a vec3 in the shader from a Vector3.
* @param name Define the name of the uniform as defined in the shader
* @param value Define the value to give to the uniform
* @returns the texture itself allowing "fluent" like uniform updates
*/
setVector3(name, value) {
this._checkUniform(name);
this._vectors3[name] = value;
return this;
}
/**
* Set a vec4 in the shader from a Vector4.
* @param name Define the name of the uniform as defined in the shader
* @param value Define the value to give to the uniform
* @returns the texture itself allowing "fluent" like uniform updates
*/
setVector4(name, value) {
this._checkUniform(name);
this._vectors4[name] = value;
return this;
}
/**
* Set a mat4 in the shader from a MAtrix.
* @param name Define the name of the uniform as defined in the shader
* @param value Define the value to give to the uniform
* @returns the texture itself allowing "fluent" like uniform updates
*/
setMatrix(name, value) {
this._checkUniform(name);
this._matrices[name] = value;
return this;
}
/**
* Render the texture to its associated render target.
* @param useCameraPostProcess Define if camera post process should be applied to the texture
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
render(useCameraPostProcess) {
const scene = this.getScene();
if (!scene) {
return;
}
const engine = this._fullEngine;
// Render
engine.enableEffect(this._drawWrapper);
this.onBeforeGenerationObservable.notifyObservers(this);
engine.setState(false);
if (!this.nodeMaterialSource) {
// Texture
for (const name in this._textures) {
this._drawWrapper.effect.setTexture(name, this._textures[name]);
}
// Float
for (const name in this._ints) {
this._drawWrapper.effect.setInt(name, this._ints[name]);
}
// Float
for (const name in this._floats) {
this._drawWrapper.effect.setFloat(name, this._floats[name]);
}
// Floats
for (const name in this._floatsArrays) {
this._drawWrapper.effect.setArray(name, this._floatsArrays[name]);
}
// Color3
for (const name in this._colors3) {
this._drawWrapper.effect.setColor3(name, this._colors3[name]);
}
// Color4
for (const name in this._colors4) {
const color = this._colors4[name];
this._drawWrapper.effect.setFloat4(name, color.r, color.g, color.b, color.a);
}
// Vector2
for (const name in this._vectors2) {
this._drawWrapper.effect.setVector2(name, this._vectors2[name]);
}
// Vector3
for (const name in this._vectors3) {
this._drawWrapper.effect.setVector3(name, this._vectors3[name]);
}
// Vector4
for (const name in this._vectors4) {
this._drawWrapper.effect.setVector4(name, this._vectors4[name]);
}
// Matrix
for (const name in this._matrices) {
this._drawWrapper.effect.setMatrix(name, this._matrices[name]);
}
}
if (!this._texture || !this._rtWrapper) {
return;
}
engine._debugPushGroup?.(`procedural texture generation for ${this.name}`, 1);
const viewPort = engine.currentViewport;
if (this.isCube) {
for (let face = 0; face < 6; face++) {
engine.bindFramebuffer(this._rtWrapper, face, undefined, undefined, true);
// VBOs
engine.bindBuffers(this._vertexBuffers, this._indexBuffer, this._drawWrapper.effect);
this._drawWrapper.effect.setFloat("face", face);
// Clear
if (this.autoClear) {
engine.clear(scene.clearColor, true, false, false);
}
// Draw order
engine.drawElementsType(Material.TriangleFillMode, 0, 6);
// Unbind and restore viewport
engine.unBindFramebuffer(this._rtWrapper, true);
}
}
else {
let numLayers = 1;
if (this._rtWrapper.is3D) {
numLayers = this._rtWrapper.depth;
}
else if (this._rtWrapper.is2DArray) {
numLayers = this._rtWrapper.layers;
}
for (let layer = 0; layer < numLayers; layer++) {
engine.bindFramebuffer(this._rtWrapper, 0, undefined, undefined, true, 0, layer);
// VBOs
engine.bindBuffers(this._vertexBuffers, this._indexBuffer, this._drawWrapper.effect);
if (this._rtWrapper.is3D || this._rtWrapper.is2DArray) {
this._drawWrapper.effect?.setFloat("layer", numLayers !== 1 ? layer / (numLayers - 1) : 0);
this._drawWrapper.effect?.setInt("layerNum", layer);
for (const name in this._textures) {
this._drawWrapper.effect.setTexture(name, this._textures[name]);
}
}
// Clear
if (this.autoClear) {
engine.clear(scene.clearColor, true, false, false);
}
// Draw order
engine.drawElementsType(Material.TriangleFillMode, 0, 6);
// Unbind and restore viewport
engine.unBindFramebuffer(this._rtWrapper, !this._generateMipMaps);
}
}
if (viewPort) {
engine.setViewport(viewPort);
}
// Mipmaps
if (this.isCube) {
engine.generateMipMapsForCubemap(this._texture, true);
}
engine._debugPopGroup?.(1);
if (this.onGenerated) {
this.onGenerated();
}
this.onGeneratedObservable.notifyObservers(this);
}
/**
* Clone the texture.
* @returns the cloned texture
*/
clone() {
const textureSize = this.getSize();
const newTexture = new ProceduralTexture(this.name, textureSize.width, this._fragment, this.getScene(), this._fallbackTexture, this._generateMipMaps);
// Base texture
newTexture.hasAlpha = this.hasAlpha;
newTexture.level = this.level;
// RenderTarget Texture
newTexture.coordinatesMode = this.coordinatesMode;
return newTexture;
}
/**
* Dispose the texture and release its associated resources.
*/
dispose() {
const scene = this.getScene();
if (!scene) {
return;
}
const index = scene.proceduralTextures.indexOf(this);
if (index >= 0) {
scene.proceduralTextures.splice(index, 1);
}
const vertexBuffer = this._vertexBuffers[VertexBuffer.PositionKind];
if (vertexBuffer) {
vertexBuffer.dispose();
this._vertexBuffers[VertexBuffer.PositionKind] = null;
}
if (this._indexBuffer && this._fullEngine._releaseBuffer(this._indexBuffer)) {
this._indexBuffer = null;
}
this.onGeneratedObservable.clear();
this.onBeforeGenerationObservable.clear();
super.dispose();
}
}
__decorate([
serialize()
], ProceduralTexture.prototype, "isEnabled", void 0);
__decorate([
serialize()
], ProceduralTexture.prototype, "autoClear", void 0);
__decorate([
serialize()
], ProceduralTexture.prototype, "_generateMipMaps", void 0);
__decorate([
serialize()
], ProceduralTexture.prototype, "_size", void 0);
__decorate([
serialize()
], ProceduralTexture.prototype, "refreshRate", null);
RegisterClass("BABYLON.ProceduralTexture", ProceduralTexture);
/**
* Build cdf maps to be used for IBL importance sampling.
*/
class IblCdfGenerator {
/**
* Returns whether the CDF renderer is supported by the current engine
*/
get isSupported() {
const engine = EngineStore.LastCreatedEngine;
if (!engine) {
return false;
}
return engine.getCaps().texelFetch;
}
/**
* Gets the IBL source texture being used by the CDF renderer
*/
get iblSource() {
return this._iblSource;
}
/**
* Sets the IBL source texture to be used by the CDF renderer.
* This will trigger recreation of the CDF assets.
*/
set iblSource(source) {
if (this._iblSource === source) {
return;
}
this._disposeTextures();
this._iblSource = source;
if (!source) {
return;
}
if (source.isCube) {
if (source.isReadyOrNotBlocking()) {
this._recreateAssetsFromNewIbl();
}
else {
source.onLoadObservable.addOnce(this._recreateAssetsFromNewIbl.bind(this, source));
}
}
else {
if (source.isReadyOrNotBlocking()) {
this._recreateAssetsFromNewIbl();
}
else {
source.onLoadObservable.addOnce(this._recreateAssetsFromNewIbl.bind(this, source));
}
}
}
_recreateAssetsFromNewIbl() {
if (this._debugPass) {
this._debugPass.dispose();
}
this._createTextures();
if (this._debugPass) {
// Recreate the debug pass because of the new textures
this._createDebugPass();
}
}
/**
* Return the cumulative distribution function (CDF) texture
* @returns Return the cumulative distribution function (CDF) texture
*/
getIcdfTexture() {
return this._icdfPT ? this._icdfPT : this._dummyTexture;
}
/**
* Sets params that control the position and scaling of the debug display on the screen.
* @param x Screen X offset of the debug display (0-1)
* @param y Screen Y offset of the debug display (0-1)
* @param widthScale X scale of the debug display (0-1)
* @param heightScale Y scale of the debug display (0-1)
*/
setDebugDisplayParams(x, y, widthScale, heightScale) {
this._debugSizeParams.set(x, y, widthScale, heightScale);
}
/**
* The name of the debug pass post process
*/
get debugPassName() {
return this._debugPassName;
}
/**
* Gets the debug pass post process
* @returns The post process
*/
getDebugPassPP() {
if (!this._debugPass) {
this._createDebugPass();
}
return this._debugPass;
}
/**
* Instanciates the CDF renderer
* @param sceneOrEngine Scene to attach to
* @returns The CDF renderer
*/
constructor(sceneOrEngine) {
this._cachedDominantDirection = null;
/** Enable the debug view for this pass */
this.debugEnabled = false;
this._debugSizeParams = new Vector4(0.0, 0.0, 1.0, 1.0);
this._debugPassName = "CDF Debug";
/**
* Observable that triggers when the CDF renderer is ready
*/
this.onGeneratedObservable = new Observable();
if (sceneOrEngine) {
if (IblCdfGenerator._IsScene(sceneOrEngine)) {
this._scene = sceneOrEngine;
}
else {
this._engine = sceneOrEngine;
}
}
else {
this._scene = EngineStore.LastCreatedScene;
}
if (this._scene) {
this._engine = this._scene.getEngine();
}
if (!this.isSupported) {
Logger.Warn("CDF renderer is not supported by the current engine.");
return;
}
const blackPixels = new Uint16Array([0, 0, 0, 255]);
this._dummyTexture = new RawTexture(blackPixels, 1, 1, Engine.TEXTUREFORMAT_RGBA, sceneOrEngine, false, false, undefined, Constants.TEXTURETYPE_HALF_FLOAT);
if (this._scene) {
IblCdfGenerator._SceneComponentInitialization(this._scene);
}
}
_createTextures() {
const size = this._iblSource ? { width: this._iblSource.getSize().width, height: this._iblSource.getSize().height } : { width: 1, height: 1 };
if (!this._iblSource) {
this._iblSource = RawTexture.CreateRTexture(new Uint8Array([255]), 1, 1, this._engine, false, false, Constants.TEXTURE_NEAREST_SAMPLINGMODE, Constants.TEXTURETYPE_UNSIGNED_BYTE);
this._iblSource.name = "Placeholder IBL Source";
}
if (this._iblSource.isCube) {
size.width *= 4;
size.height *= 2;
// Force the resolution to be a power of 2 because we rely on the
// auto-mipmap generation for the scaled luminance texture to produce
// a 1x1 mip that represents the true average pixel intensity of the IBL.
size.width = 1 << Math.floor(Math.log2(size.width));
size.height = 1 << Math.floor(Math.log2(size.height));
}
const isWebGPU = this._engine.isWebGPU;
// Create CDF maps (Cumulative Distribution Function) to assist in importance sampling
const cdfOptions = {
generateDepthBuffer: false,
generateMipMaps: false,
format: Constants.TEXTUREFORMAT_R,
type: Constants.TEXTURETYPE_FLOAT,
samplingMode: Constants.TEXTURE_NEAREST_SAMPLINGMODE,
shaderLanguage: isWebGPU ? 1 /* ShaderLanguage.WGSL */ : 0 /* ShaderLanguage.GLSL */,
gammaSpace: false,
extraInitializationsAsync: async () => {
if (isWebGPU) {
await Promise.all([import('./iblCdfx.fragment-teWJwKzQ.esm.js'), import('./iblCdfy.fragment-BNOqHu5p.esm.js'), import('./iblScaledLuminance.fragment-BVAfc7JV.esm.js')]);
}
else {
await Promise.all([import('./iblCdfx.fragment-BrA4_9Jl.esm.js'), import('./iblCdfy.fragment-B-BIVri7.esm.js'), import('./iblScaledLuminance.fragment-ChcjscID.esm.js')]);
}
},
};
const icdfOptions = {
generateDepthBuffer: false,
generateMipMaps: false,
format: Constants.TEXTUREFORMAT_RGBA,
type: Constants.TEXTURETYPE_HALF_FLOAT,
samplingMode: Constants.TEXTURE_NEAREST_SAMPLINGMODE,
shaderLanguage: isWebGPU ? 1 /* ShaderLanguage.WGSL */ : 0 /* ShaderLanguage.GLSL */,
gammaSpace: false,
extraInitializationsAsync: async () => {
if (isWebGPU) {
await Promise.all([import('./iblIcdf.fragment-B54Y7Dpi.esm.js'), import('./iblDominantDirection.fragment-BT2pAv4o.esm.js')]);
}
else {
await Promise.all([import('./iblIcdf.fragment-dTblvOA-.esm.js'), import('./iblDominantDirection.fragment-CuvmHKW7.esm.js')]);
}
},
};
this._cdfyPT = new ProceduralTexture("cdfyTexture", { width: size.width, height: size.height + 1 }, "iblCdfy", this._scene, cdfOptions, false, false);
this._cdfyPT.autoClear = false;
this._cdfyPT.setTexture("iblSource", this._iblSource);
this._cdfyPT.setInt("iblHeight", size.height);
this._cdfyPT.wrapV = Constants.TEXTURE_CLAMP_ADDRESSMODE;
this._cdfyPT.refreshRate = 0;
if (this._iblSource.isCube) {
this._cdfyPT.defines = "#define IBL_USE_CUBE_MAP\n";
}
this._cdfxPT = new ProceduralTexture("cdfxTexture", { width: size.width + 1, height: 1 }, "iblCdfx", this._scene, cdfOptions, false, false);
this._cdfxPT.autoClear = false;
this._cdfxPT.setTexture("cdfy", this._cdfyPT);
this._cdfxPT.refreshRate = 0;
this._cdfxPT.wrapU = Constants.TEXTURE_CLAMP_ADDRESSMODE;
this._scaledLuminancePT = new ProceduralTexture("iblScaledLuminance", { width: size.width, height: size.height }, "iblScaledLuminance", this._scene, { ...cdfOptions, samplingMode: Constants.TEXTURE_TRILINEAR_SAMPLINGMODE, generateMipMaps: true }, true, false);
this._scaledLuminancePT.autoClear = false;
this._scaledLuminancePT.setTexture("iblSource", this._iblSource);
this._scaledLuminancePT.setInt("iblHeight", size.height);
this._scaledLuminancePT.setInt("iblWidth", size.width);
this._scaledLuminancePT.refreshRate = 0;
if (this._iblSource.isCube) {
this._scaledLuminancePT.defines = "#define IBL_USE_CUBE_MAP\n";
}
this._icdfPT = new ProceduralTexture("icdfTexture", { width: size.width, height: size.height }, "iblIcdf", this._scene, icdfOptions, false, false);
this._icdfPT.autoClear = false;
this._icdfPT.setTexture("cdfy", this._cdfyPT);
this._icdfPT.setTexture("cdfx", this._cdfxPT);
this._icdfPT.setTexture("iblSource", this._iblSource);
this._icdfPT.setTexture("scaledLuminanceSampler", this._scaledLuminancePT);
this._icdfPT.refreshRate = 0;
this._icdfPT.wrapV = Constants.TEXTURE_CLAMP_ADDRESSMODE;
this._icdfPT.wrapU = Constants.TEXTURE_CLAMP_ADDRESSMODE;
if (this._iblSource.isCube) {
this._icdfPT.defines = "#define IBL_USE_CUBE_MAP\n";
}
// Once the textures are generated, notify that they are ready to use.
this._icdfPT.onGeneratedObservable.addOnce(() => {
this.onGeneratedObservable.notifyObservers();
});
this._dominantDirectionPT = new ProceduralTexture("iblDominantDirection", { width: 1, height: 1 }, "iblDominantDirection", this._scene, icdfOptions, false, false);
this._dominantDirectionPT.autoClear = false;
this._dominantDirectionPT.setTexture("icdfSampler", this._icdfPT);
this._dominantDirectionPT.refreshRate = 0;
this._dominantDirectionPT.defines = "#define NUM_SAMPLES 32u\n";
}
_disposeTextures() {
this._cdfyPT?.dispose();
this._cdfxPT?.dispose();
this._icdfPT?.dispose();
this._scaledLuminancePT?.dispose();
this._dominantDirectionPT?.dispose();
}
_createDebugPass() {
if (this._debugPass) {
this._debugPass.dispose();
}
const isWebGPU = this._engine.isWebGPU;
const debugOptions = {
width: this._engine.getRenderWidth(),
height: this._engine.getRenderHeight(),
samplingMode: Texture.BILINEAR_SAMPLINGMODE,
engine: this._engine,
textureType: Constants.TEXTURETYPE_UNSIGNED_BYTE,
uniforms: ["sizeParams"],
samplers: ["cdfy", "icdf", "cdfx", "iblSource"],
defines: this._iblSource?.isCube ? "#define IBL_USE_CUBE_MAP\n" : "",
shaderLanguage: isWebGPU ? 1 /* ShaderLanguage.WGSL */ : 0 /* ShaderLanguage.GLSL */,
extraInitializations: (useWebGPU, list) => {
if (useWebGPU) {
list.push(import('./iblCdfDebug.fragment-CCQimo9f.esm.js'));
}
else {
list.push(import('./iblCdfDebug.fragment-D8ts0eZ0.esm.js'));
}
},
};
this._debugPass = new PostProcess(this._debugPassName, "iblCdfDebug", debugOptions);
const debugEffect = this._debugPass.getEffect();
if (debugEffect) {
debugEffect.defines = this._iblSource?.isCube ? "#define IBL_USE_CUBE_MAP\n" : "";
}
if (this._iblSource?.isCube) {
this._debugPass.updateEffect("#define IBL_USE_CUBE_MAP\n");
}
this._debugPass.onApplyObservable.add((effect) => {
effect.setTexture("cdfy", this._cdfyPT);
effect.setTexture("icdf", this._icdfPT);
effect.setTexture("cdfx", this._cdfxPT);
effect.setTexture("iblSource", this._iblSource);
effect.setFloat4("sizeParams", this._debugSizeParams.x, this._debugSizeParams.y, this._debugSizeParams.z, this._debugSizeParams.w);
});
}
/**
* Checks if the CDF renderer is ready
* @returns true if the CDF renderer is ready
*/
isReady() {
return (this._iblSource &&
this._iblSource.name !== "Placeholder IBL Source" &&
this._iblSource.isReady() &&
this._cdfyPT &&
this._cdfyPT.isReady() &&
this._icdfPT &&
this._icdfPT.isReady() &&
this._cdfxPT &&
this._cdfxPT.isReady() &&
this._scaledLuminancePT &&
this._scaledLuminancePT.isReady());
}
/**
* Explicitly trigger generation of CDF maps when they are ready to render.
* @returns Promise that resolves when the CDF maps are rendered.
*/
// eslint-disable-next-line @typescript-eslint/naming-convention, @typescript-eslint/promise-function-async
renderWhenReady() {
this._cachedDominantDirection = null;
// Once the textures are generated, notify that they are ready to use.
this._icdfPT.onGeneratedObservable.addOnce(() => {
this.onGeneratedObservable.notifyObservers();
});
const promises = [];
const renderTargets = [this._cdfyPT, this._cdfxPT, this._scaledLuminancePT, this._icdfPT];
for (const target of renderTargets) {
promises.push(new Promise((resolve) => {
if (target.isReady()) {
resolve();
}
else {
target.getEffect().executeWhenCompiled(() => {
resolve();
});
}
}));
}
// eslint-disable-next-line github/no-then
return Promise.all(promises).then(() => {
for (const target of renderTargets) {
target.render();
}
});
}
/**
* Finds the average direction of the highest intensity areas of the IBL source
* @returns Async promise that resolves to the dominant direction of the IBL source
*/
// eslint-disable-next-line @typescript-eslint/promise-function-async
findDominantDirection() {
if (this._cachedDominantDirection) {
return Promise.resolve(this._cachedDominantDirection);
}
return new Promise((resolve) => {
this._dominantDirectionPT.onGeneratedObservable.addOnce(() => {
const data = new Float32Array(4);
// eslint-disable-next-line @typescript-eslint/no-floating-promises, github/no-then
this._dominantDirectionPT.readPixels(0, 0, data, true).then(() => {
const dominantDirection = new Vector3(data[0], data[1], data[2]);
this._cachedDominantDirection = dominantDirection;
resolve(dominantDirection);
});
});
if (this.isReady()) {
if (this._dominantDirectionPT.isReady()) {
this._dominantDirectionPT.render();
}
else {
this._dominantDirectionPT.getEffect().executeWhenCompiled(() => {
this._dominantDirectionPT.render();
});
}
}
else {
this.onGeneratedObservable.addOnce(() => {
if (this._dominantDirectionPT.isReady()) {
this._dominantDirectionPT.render();
}
else {
this._dominantDirectionPT.getEffect().executeWhenCompiled(() => {
this._dominantDirectionPT.render();
});
}
});
}
});
}
/**
* Disposes the CDF renderer and associated resources
*/
dispose() {
this._disposeTextures();
this._dummyTexture.dispose();
if (this._debugPass) {
this._debugPass.dispose();
}
this.onGeneratedObservable.clear();
}
static _IsScene(sceneOrEngine) {
return sceneOrEngine.getClassName() === "Scene";
}
}
/**
* @internal
*/
IblCdfGenerator._SceneComponentInitialization = (_) => {
throw _WarnImport("IblCdfGeneratorSceneComponentSceneComponent");
};
export { IblCdfGenerator as I, ProceduralTexture as P };
//# sourceMappingURL=iblCdfGenerator-BDPVKn79.esm.js.map