UNPKG

@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.

292 lines 11 kB
import { Texture } from "../../../Materials/Textures/texture.js"; import { RegisterClass } from "../../../Misc/typeStore.js"; import { NodeParticleBlockConnectionPointTypes } from "../Enums/nodeParticleBlockConnectionPointTypes.js"; import { NodeParticleBlock } from "../nodeParticleBlock.js"; import { TextureTools } from "../../../Misc/textureTools.js"; /** * Block used to provide a texture for particles in a particle system */ export class ParticleTextureSourceBlock extends NodeParticleBlock { /** * Gets or sets the URL of the texture to be used by this block. */ get url() { return this._url; } set url(value) { if (this._url === value) { return; } this._cachedData = null; this._url = value; this._textureDataUrl = ""; this._sourceTexture = null; } /** * Gets or sets the data URL of the texture to be used by this block. * This is a base64 encoded string representing the texture data. */ get textureDataUrl() { return this._textureDataUrl; } set textureDataUrl(value) { if (this._textureDataUrl === value) { return; } this._cachedData = null; this._textureDataUrl = value; this._url = ""; this._sourceTexture = null; } /** * Gets the texture directly set on this block. * This value will not be serialized. */ get sourceTexture() { return this._sourceTexture; } /** * Directly sets the texture to be used by this block. * This value will not be serialized. */ set sourceTexture(value) { if (this._sourceTexture === value) { return; } this._cachedData = null; this._sourceTexture = value; this._url = value.url || ""; this._textureDataUrl = ""; } /** * Gets the texture set on this block. * This value will not be serialized. */ get texture() { return this.sourceTexture; } /** * Sets the texture to be used by this block. * This value will not be serialized. */ set texture(value) { this.sourceTexture = value; } /** * Create a new ParticleTextureSourceBlock * @param name defines the block name */ constructor(name) { super(name); this._url = ""; this._textureDataUrl = ""; this._sourceTexture = null; this._cachedData = null; this._clonedTextures = []; /** * Gets or sets the strenght of the flow map effect */ this.invertY = true; /** * Indicates if the texture data should be serialized as a base64 string. */ this.serializedCachedData = false; this.registerOutput("texture", NodeParticleBlockConnectionPointTypes.Texture); } /** * Gets the current class name * @returns the class name */ getClassName() { return "ParticleTextureSourceBlock"; } /** * Gets the texture output component */ get textureOutput() { return this._outputs[0]; } /** * Gets the texture content as a promise * @returns a promise that resolves to the texture content, including width, height, and pixel data */ async extractTextureContentAsync() { if (!this.textureOutput._storedValue && !this._sourceTexture) { return null; } if (this._cachedData) { return this._cachedData; } const texture = this.textureOutput._storedValue || this._sourceTexture; return await new Promise((resolve, reject) => { if (!texture.isReady()) { texture.onLoadObservable.addOnce(async () => { try { this._cachedData = await this.extractTextureContentAsync(); resolve(this._cachedData); } catch (e) { // eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors reject(e); } }); return; } const size = texture.getSize(); if (texture.getContent) { const proceduralTexture = texture; proceduralTexture .getContent() // eslint-disable-next-line github/no-then ?.then((data) => { this._cachedData = { width: size.width, height: size.height, data: new Uint8ClampedArray(data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength)), }; resolve(this._cachedData); }) // eslint-disable-next-line github/no-then .catch(reject); } else { TextureTools.GetTextureDataAsync(texture, size.width, size.height) // eslint-disable-next-line github/no-then .then((data) => { this._cachedData = { width: size.width, height: size.height, data: new Uint8ClampedArray(data), }; texture.dispose(); resolve(this._cachedData); }) // eslint-disable-next-line github/no-then .catch(reject); } }); } /** * Builds the block * @param state defines the current build state */ _build(state) { if (this._sourceTexture) { // The same NodeParticleSystemSet can be built into multiple scenes/engines // (original system scene, editor preview scene). // Textures are engine-specific, so we need to handle cross-engine cases. const sourceScene = this._sourceTexture.getScene?.(); const sourceEngine = sourceScene?.getEngine?.(); const targetEngine = state.scene.getEngine(); if (sourceEngine && sourceEngine !== targetEngine) { // Cross-engine: recreate texture from URL if available, preserving invertY const url = this._sourceTexture.url || this._url; if (url) { const invertY = this._sourceTexture.invertY ?? this.invertY; const tex = new Texture(url, state.scene, undefined, invertY); this._copyTextureProperties(this._sourceTexture, tex); this._clonedTextures.push(tex); this.textureOutput._storedValue = tex; return; } // No URL available - use the source texture directly as fallback // This may not render correctly but avoids breaking completely this.textureOutput._storedValue = this._sourceTexture; return; } // Same engine: clone works correctly and preserves all properties const cloned = this._sourceTexture.clone(); if (cloned) { this._clonedTextures.push(cloned); this.textureOutput._storedValue = cloned; } else { this.textureOutput._storedValue = this._sourceTexture; } return; } if (!this._textureDataUrl && !this._url) { this.textureOutput._storedValue = null; return; } if (this._textureDataUrl) { const tex = new Texture(this._textureDataUrl, state.scene, undefined, this.invertY); this._clonedTextures.push(tex); this.textureOutput._storedValue = tex; return; } const tex = new Texture(this._url, state.scene, undefined, this.invertY); this._clonedTextures.push(tex); this.textureOutput._storedValue = tex; } /** * Serializes this block * @returns the serialization object */ serialize() { const serializationObject = super.serialize(); serializationObject.url = this.url; serializationObject.serializedCachedData = this.serializedCachedData; serializationObject.invertY = this.invertY; if (this.serializedCachedData) { serializationObject.textureDataUrl = this.textureDataUrl; } return serializationObject; } /** * Deserializes this block from a serialization object * @param serializationObject the serialization object */ _deserialize(serializationObject) { super._deserialize(serializationObject); this.url = serializationObject.url; this.serializedCachedData = !!serializationObject.serializedCachedData; this.invertY = !!serializationObject.invertY; if (serializationObject.textureDataUrl) { this.textureDataUrl = serializationObject.textureDataUrl; } } /** * Disposes the block and its associated resources */ dispose() { // Dispose all cloned textures we created for (const tex of this._clonedTextures) { tex.dispose(); } this._clonedTextures = []; this.textureOutput._storedValue = null; // Never dispose _sourceTexture - it's owned by the caller super.dispose(); } /** * Copies texture properties from source to target texture * @param source - The source texture to copy properties from * @param target - The target texture to copy properties to */ _copyTextureProperties(source, target) { // BaseTexture properties target.hasAlpha = source.hasAlpha; target.level = source.level; target.coordinatesIndex = source.coordinatesIndex; target.coordinatesMode = source.coordinatesMode; target.wrapU = source.wrapU; target.wrapV = source.wrapV; target.wrapR = source.wrapR; target.anisotropicFilteringLevel = source.anisotropicFilteringLevel; // Texture-specific properties (if both are Texture instances) const sourceTexture = source; const targetTexture = target; if (sourceTexture.uOffset !== undefined && targetTexture.uOffset !== undefined) { targetTexture.uOffset = sourceTexture.uOffset; targetTexture.vOffset = sourceTexture.vOffset; targetTexture.uScale = sourceTexture.uScale; targetTexture.vScale = sourceTexture.vScale; targetTexture.uAng = sourceTexture.uAng; targetTexture.vAng = sourceTexture.vAng; targetTexture.wAng = sourceTexture.wAng; } } } RegisterClass("BABYLON.ParticleTextureSourceBlock", ParticleTextureSourceBlock); //# sourceMappingURL=particleSourceTextureBlock.js.map