@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.
897 lines (892 loc) • 47.6 kB
JavaScript
import { S as ShaderStore, bH as ProcessIncludes, l as __runInitializers, C as Constants, o as SerializationHelper, q as __esDecorate, s as serialize, z as __classPrivateFieldGet, D as __classPrivateFieldSet, F as Material, as as MaterialFlags, $ as PrepareDefinesForMergedUV, ak as BindTextureMatrix, aw as MaterialDefines, J as serializeAsTexture, H as expandToProperty } from './index-MZPybX0H.esm.js';
/** This file must only contain pure code and pure imports */
const RxOption = /*#__PURE__*/ new RegExp("^([gimus]+)!");
/**
* Class that manages the plugins of a material
* @since 5.0
*/
class MaterialPluginManager {
/**
* Creates a new instance of the plugin manager
* @param material material that this manager will manage the plugins for
*/
constructor(material) {
/** @internal */
this._plugins = [];
this._activePlugins = [];
this._activePluginsForExtraEvents = [];
this._material = material;
this._scene = material.getScene();
this._engine = this._scene.getEngine();
}
/**
* @internal
*/
_addPlugin(plugin) {
for (let i = 0; i < this._plugins.length; ++i) {
if (this._plugins[i].name === plugin.name) {
return false;
}
}
if (this._material._uniformBufferLayoutBuilt) {
this._material.resetDrawCache();
this._material._createUniformBuffer();
}
if (!plugin.isCompatible(this._material.shaderLanguage)) {
// eslint-disable-next-line no-throw-literal
throw `The plugin "${plugin.name}" can't be added to the material "${this._material.name}" because the plugin is not compatible with the shader language of the material.`;
}
const pluginClassName = plugin.getClassName();
if (!MaterialPluginManager._MaterialPluginClassToMainDefine[pluginClassName]) {
MaterialPluginManager._MaterialPluginClassToMainDefine[pluginClassName] = "MATERIALPLUGIN_" + ++MaterialPluginManager._MaterialPluginCounter;
}
this._material._callbackPluginEventGeneric = (id, info) => this._handlePluginEvent(id, info);
this._plugins.push(plugin);
this._plugins.sort((a, b) => a.priority - b.priority);
this._codeInjectionPoints = {};
const defineNamesFromPlugins = {};
defineNamesFromPlugins[MaterialPluginManager._MaterialPluginClassToMainDefine[pluginClassName]] = {
type: "boolean",
default: true,
};
for (const plugin of this._plugins) {
plugin.collectDefines(defineNamesFromPlugins);
this._collectPointNames("vertex", plugin.getCustomCode("vertex", this._material.shaderLanguage));
this._collectPointNames("fragment", plugin.getCustomCode("fragment", this._material.shaderLanguage));
}
this._defineNamesFromPlugins = defineNamesFromPlugins;
return true;
}
/**
* @internal
*/
_activatePlugin(plugin) {
if (this._activePlugins.indexOf(plugin) === -1) {
this._activePlugins.push(plugin);
this._activePlugins.sort((a, b) => a.priority - b.priority);
this._material._callbackPluginEventIsReadyForSubMesh = this._handlePluginEventIsReadyForSubMesh.bind(this);
this._material._callbackPluginEventPrepareDefinesBeforeAttributes = this._handlePluginEventPrepareDefinesBeforeAttributes.bind(this);
this._material._callbackPluginEventPrepareDefines = this._handlePluginEventPrepareDefines.bind(this);
this._material._callbackPluginEventBindForSubMesh = this._handlePluginEventBindForSubMesh.bind(this);
if (plugin.registerForExtraEvents) {
this._activePluginsForExtraEvents.push(plugin);
this._activePluginsForExtraEvents.sort((a, b) => a.priority - b.priority);
this._material._callbackPluginEventHasRenderTargetTextures = this._handlePluginEventHasRenderTargetTextures.bind(this);
this._material._callbackPluginEventFillRenderTargetTextures = this._handlePluginEventFillRenderTargetTextures.bind(this);
this._material._callbackPluginEventHardBindForSubMesh = this._handlePluginEventHardBindForSubMesh.bind(this);
}
}
}
/**
* Gets a plugin from the list of plugins managed by this manager
* @param name name of the plugin
* @returns the plugin if found, else null
*/
getPlugin(name) {
for (let i = 0; i < this._plugins.length; ++i) {
if (this._plugins[i].name === name) {
return this._plugins[i];
}
}
return null;
}
_handlePluginEventIsReadyForSubMesh(eventData) {
let isReady = true;
for (const plugin of this._activePlugins) {
isReady = isReady && plugin.isReadyForSubMesh(eventData.defines, this._scene, this._engine, eventData.subMesh);
}
eventData.isReadyForSubMesh = isReady;
}
_handlePluginEventPrepareDefinesBeforeAttributes(eventData) {
for (const plugin of this._activePlugins) {
plugin.prepareDefinesBeforeAttributes(eventData.defines, this._scene, eventData.mesh);
}
}
_handlePluginEventPrepareDefines(eventData) {
for (const plugin of this._activePlugins) {
plugin.prepareDefines(eventData.defines, this._scene, eventData.mesh);
}
}
_handlePluginEventHardBindForSubMesh(eventData) {
for (const plugin of this._activePluginsForExtraEvents) {
plugin.hardBindForSubMesh(this._material._uniformBuffer, this._scene, this._engine, eventData.subMesh);
}
}
_handlePluginEventBindForSubMesh(eventData) {
for (const plugin of this._activePlugins) {
plugin.bindForSubMesh(this._material._uniformBuffer, this._scene, this._engine, eventData.subMesh);
}
}
_handlePluginEventHasRenderTargetTextures(eventData) {
let hasRenderTargetTextures = false;
for (const plugin of this._activePluginsForExtraEvents) {
hasRenderTargetTextures = plugin.hasRenderTargetTextures();
if (hasRenderTargetTextures) {
break;
}
}
eventData.hasRenderTargetTextures = hasRenderTargetTextures;
}
_handlePluginEventFillRenderTargetTextures(eventData) {
for (const plugin of this._activePluginsForExtraEvents) {
plugin.fillRenderTargetTextures(eventData.renderTargets);
}
}
_handlePluginEvent(id, info) {
switch (id) {
case 512 /* MaterialPluginEvent.GetActiveTextures */: {
const eventData = info;
for (const plugin of this._activePlugins) {
plugin.getActiveTextures(eventData.activeTextures);
}
break;
}
case 256 /* MaterialPluginEvent.GetAnimatables */: {
const eventData = info;
for (const plugin of this._activePlugins) {
plugin.getAnimatables(eventData.animatables);
}
break;
}
case 1024 /* MaterialPluginEvent.HasTexture */: {
const eventData = info;
let hasTexture = false;
for (const plugin of this._activePlugins) {
hasTexture = plugin.hasTexture(eventData.texture);
if (hasTexture) {
break;
}
}
eventData.hasTexture = hasTexture;
break;
}
case 2 /* MaterialPluginEvent.Disposed */: {
const eventData = info;
for (const plugin of this._plugins) {
plugin.dispose(eventData.forceDisposeTextures);
}
break;
}
case 4 /* MaterialPluginEvent.GetDefineNames */: {
const eventData = info;
eventData.defineNames = this._defineNamesFromPlugins;
break;
}
case 128 /* MaterialPluginEvent.PrepareEffect */: {
const eventData = info;
for (const plugin of this._activePlugins) {
eventData.fallbackRank = plugin.addFallbacks(eventData.defines, eventData.fallbacks, eventData.fallbackRank);
plugin.getAttributes(eventData.attributes, this._scene, eventData.mesh);
}
if (this._uniformList.length > 0) {
eventData.uniforms.push(...this._uniformList);
}
if (this._samplerList.length > 0) {
eventData.samplers.push(...this._samplerList);
}
if (this._uboList.length > 0) {
eventData.uniformBuffersNames.push(...this._uboList);
}
eventData.customCode = this._injectCustomCode(eventData, eventData.customCode);
break;
}
case 8 /* MaterialPluginEvent.PrepareUniformBuffer */: {
const eventData = info;
this._uboDeclaration = "";
this._vertexDeclaration = "";
this._fragmentDeclaration = "";
this._uniformList = [];
this._samplerList = [];
this._uboList = [];
const isWebGPU = this._material.shaderLanguage === 1 /* ShaderLanguage.WGSL */;
for (const plugin of this._plugins) {
const uniforms = plugin.getUniforms(this._material.shaderLanguage);
if (uniforms) {
if (uniforms.ubo) {
for (const uniform of uniforms.ubo) {
if (uniform.size && uniform.type) {
const arraySize = uniform.arraySize ?? 0;
eventData.ubo.addUniform(uniform.name, uniform.size, arraySize);
if (isWebGPU) {
let type;
switch (uniform.type) {
case "mat4":
type = "mat4x4f";
break;
case "float":
type = "f32";
break;
default:
type = `${uniform.type}f`;
break;
}
if (arraySize > 0) {
this._uboDeclaration += `uniform ${uniform.name}: array<${type}, ${arraySize}>;\n`;
}
else {
this._uboDeclaration += `uniform ${uniform.name}: ${type};\n`;
}
}
else {
this._uboDeclaration += `${uniform.type} ${uniform.name}${arraySize > 0 ? `[${arraySize}]` : ""};\n`;
}
}
this._uniformList.push(uniform.name);
}
}
if (uniforms.vertex) {
this._vertexDeclaration += uniforms.vertex + "\n";
}
if (uniforms.fragment) {
this._fragmentDeclaration += uniforms.fragment + "\n";
}
// These are uniforms which are used by the shader but not updated by the plugin directly.
// They still need to be present in the _uniformList so the Effect can determine their locations.
if (uniforms.externalUniforms) {
this._uniformList.push(...uniforms.externalUniforms);
}
}
plugin.getSamplers(this._samplerList);
plugin.getUniformBuffersNames(this._uboList);
}
break;
}
}
}
_collectPointNames(shaderType, customCode) {
if (!customCode) {
return;
}
for (const pointName in customCode) {
if (!this._codeInjectionPoints[shaderType]) {
this._codeInjectionPoints[shaderType] = {};
}
this._codeInjectionPoints[shaderType][pointName] = true;
}
}
_injectCustomCode(eventData, existingCallback) {
return (shaderType, code) => {
if (existingCallback) {
code = existingCallback(shaderType, code);
}
if (this._uboDeclaration) {
code = code.replace("#define ADDITIONAL_UBO_DECLARATION", this._uboDeclaration);
}
if (this._vertexDeclaration) {
code = code.replace("#define ADDITIONAL_VERTEX_DECLARATION", this._vertexDeclaration);
}
if (this._fragmentDeclaration) {
code = code.replace("#define ADDITIONAL_FRAGMENT_DECLARATION", this._fragmentDeclaration);
}
const points = this._codeInjectionPoints?.[shaderType];
if (!points) {
return code;
}
let processorOptions = null;
for (let pointName in points) {
let injectedCode = "";
for (const plugin of this._activePlugins) {
const shaderLanguage = this._material.shaderLanguage;
let customCode = plugin.getCustomCode(shaderType, shaderLanguage)?.[pointName];
if (!customCode) {
continue;
}
if (plugin.resolveIncludes) {
if (processorOptions === null) {
processorOptions = {
defines: [], // not used by _ProcessIncludes
indexParameters: eventData.indexParameters,
isFragment: false,
shouldUseHighPrecisionShader: this._engine._shouldUseHighPrecisionShader,
processor: undefined, // not used by _ProcessIncludes
supportsUniformBuffers: this._engine.supportsUniformBuffers,
shadersRepository: ShaderStore.GetShadersRepository(shaderLanguage),
includesShadersStore: ShaderStore.GetIncludesShadersStore(shaderLanguage),
version: undefined, // not used by _ProcessIncludes
platformName: this._engine.shaderPlatformName,
processingContext: undefined, // not used by _ProcessIncludes
isNDCHalfZRange: this._engine.isNDCHalfZRange,
useReverseDepthBuffer: this._engine.useReverseDepthBuffer,
processCodeAfterIncludes: undefined, // not used by _ProcessIncludes
};
}
processorOptions.isFragment = shaderType === "fragment";
ProcessIncludes(customCode, processorOptions, (code) => (customCode = code));
}
injectedCode += customCode + "\n";
}
if (injectedCode.length > 0) {
if (pointName.charAt(0) === "!") {
// pointName is a regular expression
pointName = pointName.substring(1);
let regexFlags = "g";
if (pointName.charAt(0) === "!") {
// no flags
regexFlags = "";
pointName = pointName.substring(1);
}
else {
// get the flag(s)
const matchOption = RxOption.exec(pointName);
if (matchOption && matchOption.length >= 2) {
regexFlags = matchOption[1];
pointName = pointName.substring(regexFlags.length + 1);
}
}
if (regexFlags.indexOf("g") < 0) {
// we force the "g" flag so that the regexp object is stateful!
regexFlags += "g";
}
const rx = new RegExp(pointName, regexFlags);
let match = rx.exec(code);
while (match !== null) {
const { index } = match;
const newCode = ReplaceRegExpSubstitutions(injectedCode, match);
code = code.substring(0, index) + newCode + code.substring(index + match[0].length);
rx.lastIndex = index + newCode.length;
match = rx.exec(code);
}
}
else {
const fullPointName = "#define " + pointName;
code = code.replace(fullPointName, "\n" + injectedCode + "\n" + fullPointName);
}
}
}
return code;
};
}
}
/** Map a plugin class name to a #define name (used in the vertex/fragment shaders as a marker of the plugin usage) */
MaterialPluginManager._MaterialPluginClassToMainDefine = {};
MaterialPluginManager._MaterialPluginCounter = 0;
/**
* Replace regex substitution patterns (e.g. $1, $2, etc.)
* @param value The replacement string
* @param match The regex match array
* @returns Value having $X substitutions replaced with the equivalent `match[X]`
*/
function ReplaceRegExpSubstitutions(value, match) {
return value.replace(/\$(\d+)/g, (group0, group1) => {
const index = Number(group1);
return index < match.length ? match[index] : "";
});
}
/** This file must only contain pure code and pure imports */
/**
* Base class for material plugins.
* @since 5.0
*/
let MaterialPluginBase = (() => {
var _a;
let _name_decorators;
let _name_initializers = [];
let _name_extraInitializers = [];
let _priority_decorators;
let _priority_initializers = [];
let _priority_extraInitializers = [];
let _resolveIncludes_decorators;
let _resolveIncludes_initializers = [];
let _resolveIncludes_extraInitializers = [];
let _registerForExtraEvents_decorators;
let _registerForExtraEvents_initializers = [];
let _registerForExtraEvents_extraInitializers = [];
return _a = class MaterialPluginBase {
/**
* Gets a boolean indicating that the plugin is compatible with a given shader language.
* @param shaderLanguage The shader language to use.
* @returns true if the plugin is compatible with the shader language
*/
isCompatible(shaderLanguage) {
switch (shaderLanguage) {
case 0 /* ShaderLanguage.GLSL */:
return true;
default:
return false;
}
}
_enable(enable) {
if (enable) {
this._pluginManager._activatePlugin(this);
}
}
/**
* Creates a new material plugin
* @param material parent material of the plugin
* @param name name of the plugin
* @param priority priority of the plugin
* @param defines list of defines used by the plugin. The value of the property is the default value for this property
* @param addToPluginList true to add the plugin to the list of plugins managed by the material plugin manager of the material (default: true)
* @param enable true to enable the plugin (it is handy if the plugin does not handle properties to switch its current activation)
* @param resolveIncludes Indicates that any #include directive in the plugin code must be replaced by the corresponding code (default: false)
*/
constructor(material, name, priority, defines, addToPluginList = true, enable = false, resolveIncludes = false) {
/**
* Defines the name of the plugin
*/
this.name = __runInitializers(this, _name_initializers, void 0);
/**
* Defines the priority of the plugin. Lower numbers run first.
*/
this.priority = (__runInitializers(this, _name_extraInitializers), __runInitializers(this, _priority_initializers, 500));
/**
* Indicates that any #include directive in the plugin code must be replaced by the corresponding code.
*/
this.resolveIncludes = (__runInitializers(this, _priority_extraInitializers), __runInitializers(this, _resolveIncludes_initializers, false));
/**
* Indicates that this plugin should be notified for the extra events (HasRenderTargetTextures / FillRenderTargetTextures / HardBindForSubMesh)
*/
this.registerForExtraEvents = (__runInitializers(this, _resolveIncludes_extraInitializers), __runInitializers(this, _registerForExtraEvents_initializers, false));
/**
* Specifies if the material plugin should be serialized, `true` to skip serialization
*/
this.doNotSerialize = (__runInitializers(this, _registerForExtraEvents_extraInitializers), false);
this._material = material;
this.name = name;
this.priority = priority;
this.resolveIncludes = resolveIncludes;
if (!material.pluginManager) {
material.pluginManager = new MaterialPluginManager(material);
material.onDisposeObservable.add(() => {
material.pluginManager = undefined;
});
}
this._pluginDefineNames = defines;
this._pluginManager = material.pluginManager;
if (addToPluginList) {
this._pluginManager._addPlugin(this);
}
if (enable) {
this._enable(true);
}
this.markAllDefinesAsDirty = material._dirtyCallbacks[Constants.MATERIAL_AllDirtyFlag];
}
/**
* Gets the current class name useful for serialization or dynamic coding.
* @returns The class name.
*/
getClassName() {
return "MaterialPluginBase";
}
/**
* Specifies that the submesh is ready to be used.
* @param _defines the list of "defines" to update.
* @param _scene defines the scene the material belongs to.
* @param _engine the engine this scene belongs to.
* @param _subMesh the submesh to check for readiness
* @returns - boolean indicating that the submesh is ready or not.
*/
isReadyForSubMesh(_defines, _scene, _engine, _subMesh) {
return true;
}
/**
* Binds the material data (this function is called even if mustRebind() returns false)
* @param _uniformBuffer defines the Uniform buffer to fill in.
* @param _scene defines the scene the material belongs to.
* @param _engine defines the engine the material belongs to.
* @param _subMesh the submesh to bind data for
*/
hardBindForSubMesh(_uniformBuffer, _scene, _engine, _subMesh) { }
/**
* Binds the material data.
* @param _uniformBuffer defines the Uniform buffer to fill in.
* @param _scene defines the scene the material belongs to.
* @param _engine the engine this scene belongs to.
* @param _subMesh the submesh to bind data for
*/
bindForSubMesh(_uniformBuffer, _scene, _engine, _subMesh) { }
/**
* Disposes the resources of the material.
* @param _forceDisposeTextures - Forces the disposal of all textures.
*/
dispose(_forceDisposeTextures) { }
/**
* Returns a list of custom shader code fragments to customize the shader.
* @param _shaderType "vertex" or "fragment"
* @param _shaderLanguage The shader language to use.
* @returns null if no code to be added, or a list of pointName =\> code.
* Note that `pointName` can also be a regular expression if it starts with a `!`.
* In that case, the string found by the regular expression (if any) will be
* replaced by the code provided.
*/
getCustomCode(_shaderType, _shaderLanguage = 0 /* ShaderLanguage.GLSL */) {
return null;
}
/**
* Collects all defines.
* @param defines The object to append to.
*/
collectDefines(defines) {
if (!this._pluginDefineNames) {
return;
}
for (const key of Object.keys(this._pluginDefineNames)) {
if (key[0] === "_") {
continue;
}
const type = typeof this._pluginDefineNames[key];
defines[key] = {
type: type === "number" ? "number" : type === "string" ? "string" : type === "boolean" ? "boolean" : "object",
default: this._pluginDefineNames[key],
};
}
}
/**
* Sets the defines for the next rendering. Called before PrepareDefinesForAttributes is called.
* @param _defines the list of "defines" to update.
* @param _scene defines the scene to the material belongs to.
* @param _mesh the mesh being rendered
*/
prepareDefinesBeforeAttributes(_defines, _scene, _mesh) { }
/**
* Sets the defines for the next rendering
* @param _defines the list of "defines" to update.
* @param _scene defines the scene to the material belongs to.
* @param _mesh the mesh being rendered
*/
prepareDefines(_defines, _scene, _mesh) { }
/**
* Checks to see if a texture is used in the material.
* @param _texture - Base texture to use.
* @returns - Boolean specifying if a texture is used in the material.
*/
hasTexture(_texture) {
return false;
}
/**
* Gets a boolean indicating that current material needs to register RTT
* @returns true if this uses a render target otherwise false.
*/
hasRenderTargetTextures() {
return false;
}
/**
* Fills the list of render target textures.
* @param _renderTargets the list of render targets to update
*/
fillRenderTargetTextures(_renderTargets) { }
/**
* Returns an array of the actively used textures.
* @param _activeTextures Array of BaseTextures
*/
getActiveTextures(_activeTextures) { }
/**
* Returns the animatable textures.
* @param _animatables Array of animatable textures.
*/
getAnimatables(_animatables) { }
/**
* Add fallbacks to the effect fallbacks list.
* @param defines defines the Base texture to use.
* @param fallbacks defines the current fallback list.
* @param currentRank defines the current fallback rank.
* @returns the new fallback rank.
*/
addFallbacks(defines, fallbacks, currentRank) {
return currentRank;
}
/**
* Gets the samplers used by the plugin.
* @param _samplers list that the sampler names should be added to.
*/
getSamplers(_samplers) { }
/**
* Gets the attributes used by the plugin.
* @param _attributes list that the attribute names should be added to.
* @param _scene the scene that the material belongs to.
* @param _mesh the mesh being rendered.
*/
getAttributes(_attributes, _scene, _mesh) { }
/**
* Gets the uniform buffers names added by the plugin.
* @param _ubos list that the ubo names should be added to.
*/
getUniformBuffersNames(_ubos) { }
/**
* Gets the description of the uniforms to add to the ubo (if engine supports ubos) or to inject directly in the vertex/fragment shaders (if engine does not support ubos)
* @param _shaderLanguage The shader language to use.
* @returns the description of the uniforms
*/
getUniforms(_shaderLanguage = 0 /* ShaderLanguage.GLSL */) {
return {};
}
/**
* Makes a duplicate of the current configuration into another one.
* @param plugin define the config where to copy the info
*/
copyTo(plugin) {
SerializationHelper.Clone(() => plugin, this);
}
/**
* Serializes this plugin configuration.
* @returns - An object with the serialized config.
*/
serialize() {
return SerializationHelper.Serialize(this);
}
/**
* Parses a plugin configuration from a serialized object.
* @param source - Serialized object.
* @param scene Defines the scene we are parsing for
* @param rootUrl Defines the rootUrl to load from
*/
parse(source, scene, rootUrl) {
SerializationHelper.Parse(() => this, source, scene, rootUrl);
}
},
(() => {
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
_name_decorators = [serialize()];
_priority_decorators = [serialize()];
_resolveIncludes_decorators = [serialize()];
_registerForExtraEvents_decorators = [serialize()];
__esDecorate(null, null, _name_decorators, { kind: "field", name: "name", static: false, private: false, access: { has: obj => "name" in obj, get: obj => obj.name, set: (obj, value) => { obj.name = value; } }, metadata: _metadata }, _name_initializers, _name_extraInitializers);
__esDecorate(null, null, _priority_decorators, { kind: "field", name: "priority", static: false, private: false, access: { has: obj => "priority" in obj, get: obj => obj.priority, set: (obj, value) => { obj.priority = value; } }, metadata: _metadata }, _priority_initializers, _priority_extraInitializers);
__esDecorate(null, null, _resolveIncludes_decorators, { kind: "field", name: "resolveIncludes", static: false, private: false, access: { has: obj => "resolveIncludes" in obj, get: obj => obj.resolveIncludes, set: (obj, value) => { obj.resolveIncludes = value; } }, metadata: _metadata }, _resolveIncludes_initializers, _resolveIncludes_extraInitializers);
__esDecorate(null, null, _registerForExtraEvents_decorators, { kind: "field", name: "registerForExtraEvents", static: false, private: false, access: { has: obj => "registerForExtraEvents" in obj, get: obj => obj.registerForExtraEvents, set: (obj, value) => { obj.registerForExtraEvents = value; } }, metadata: _metadata }, _registerForExtraEvents_initializers, _registerForExtraEvents_extraInitializers);
if (_metadata) Object.defineProperty(_a, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
})(),
_a;
})();
/**
* @internal
*/
class MaterialDetailMapDefines extends MaterialDefines {
constructor() {
super(...arguments);
this.DETAIL = false;
this.DETAILDIRECTUV = 0;
this.DETAIL_NORMALBLENDMETHOD = 0;
}
}
/**
* Plugin that implements the detail map component of a material
*
* Inspired from:
* Unity: https://docs.unity3d.com/Packages/com.unity.render-pipelines.high-definition@9.0/manual/Mask-Map-and-Detail-Map.html and https://docs.unity3d.com/Manual/StandardShaderMaterialParameterDetail.html
* Unreal: https://docs.unrealengine.com/en-US/Engine/Rendering/Materials/HowTo/DetailTexturing/index.html
* Cryengine: https://docs.cryengine.com/display/SDKDOC2/Detail+Maps
*/
let DetailMapConfiguration = (() => {
var _a, _DetailMapConfiguration_texture_accessor_storage, _DetailMapConfiguration_normalBlendMethod_accessor_storage, _DetailMapConfiguration_isEnabled_accessor_storage;
let _classSuper = MaterialPluginBase;
let _texture_decorators;
let _texture_initializers = [];
let _texture_extraInitializers = [];
let _diffuseBlendLevel_decorators;
let _diffuseBlendLevel_initializers = [];
let _diffuseBlendLevel_extraInitializers = [];
let _roughnessBlendLevel_decorators;
let _roughnessBlendLevel_initializers = [];
let _roughnessBlendLevel_extraInitializers = [];
let _bumpLevel_decorators;
let _bumpLevel_initializers = [];
let _bumpLevel_extraInitializers = [];
let _normalBlendMethod_decorators;
let _normalBlendMethod_initializers = [];
let _normalBlendMethod_extraInitializers = [];
let _isEnabled_decorators;
let _isEnabled_initializers = [];
let _isEnabled_extraInitializers = [];
return _a = class DetailMapConfiguration extends _classSuper {
/**
* The detail texture of the material.
*/
get texture() { return __classPrivateFieldGet(this, _DetailMapConfiguration_texture_accessor_storage, "f"); }
set texture(value) { __classPrivateFieldSet(this, _DetailMapConfiguration_texture_accessor_storage, value, "f"); }
/**
* The method used to blend the bump and detail normals together
*/
get normalBlendMethod() { return __classPrivateFieldGet(this, _DetailMapConfiguration_normalBlendMethod_accessor_storage, "f"); }
set normalBlendMethod(value) { __classPrivateFieldSet(this, _DetailMapConfiguration_normalBlendMethod_accessor_storage, value, "f"); }
/**
* Enable or disable the detail map on this material
*/
get isEnabled() { return __classPrivateFieldGet(this, _DetailMapConfiguration_isEnabled_accessor_storage, "f"); }
set isEnabled(value) { __classPrivateFieldSet(this, _DetailMapConfiguration_isEnabled_accessor_storage, value, "f"); }
/** @internal */
_markAllSubMeshesAsTexturesDirty() {
this._enable(this._isEnabled);
this._internalMarkAllSubMeshesAsTexturesDirty();
}
/**
* Gets a boolean indicating that the plugin is compatible with a given shader language.
* @returns true if the plugin is compatible with the shader language
*/
isCompatible() {
return true;
}
constructor(material, addToPluginList = true) {
super(material, "DetailMap", 140, new MaterialDetailMapDefines(), addToPluginList);
this._texture = null;
_DetailMapConfiguration_texture_accessor_storage.set(this, __runInitializers(this, _texture_initializers, void 0));
/**
* Defines how strongly the detail diffuse/albedo channel is blended with the regular diffuse/albedo texture
* Bigger values mean stronger blending
*/
this.diffuseBlendLevel = (__runInitializers(this, _texture_extraInitializers), __runInitializers(this, _diffuseBlendLevel_initializers, 1));
/**
* Defines how strongly the detail roughness channel is blended with the regular roughness value
* Bigger values mean stronger blending. Only used with PBR materials
*/
this.roughnessBlendLevel = (__runInitializers(this, _diffuseBlendLevel_extraInitializers), __runInitializers(this, _roughnessBlendLevel_initializers, 1));
/**
* Defines how strong the bump effect from the detail map is
* Bigger values mean stronger effect
*/
this.bumpLevel = (__runInitializers(this, _roughnessBlendLevel_extraInitializers), __runInitializers(this, _bumpLevel_initializers, 1));
this._normalBlendMethod = (__runInitializers(this, _bumpLevel_extraInitializers), Material.MATERIAL_NORMALBLENDMETHOD_WHITEOUT);
_DetailMapConfiguration_normalBlendMethod_accessor_storage.set(this, __runInitializers(this, _normalBlendMethod_initializers, void 0));
this._isEnabled = (__runInitializers(this, _normalBlendMethod_extraInitializers), false);
_DetailMapConfiguration_isEnabled_accessor_storage.set(this, __runInitializers(this, _isEnabled_initializers, false));
/** @internal */
this._internalMarkAllSubMeshesAsTexturesDirty = __runInitializers(this, _isEnabled_extraInitializers);
this._internalMarkAllSubMeshesAsTexturesDirty = material._dirtyCallbacks[Constants.MATERIAL_TextureDirtyFlag];
}
/**
* Checks whether the detail map textures are ready for the sub mesh.
* @param defines defines the material defines to inspect
* @param scene defines the scene to use for readiness checks
* @param engine defines the engine to use for readiness checks
* @returns true if the detail map is ready
*/
isReadyForSubMesh(defines, scene, engine) {
if (!this._isEnabled) {
return true;
}
if (defines._areTexturesDirty && scene.texturesEnabled) {
if (engine.getCaps().standardDerivatives && this._texture && MaterialFlags.DetailTextureEnabled) {
// Detail texture cannot be not blocking.
if (!this._texture.isReady()) {
return false;
}
}
}
return true;
}
/**
* Updates the material defines for the detail map.
* @param defines defines the material defines to update
* @param scene defines the scene to use for texture checks
*/
prepareDefines(defines, scene) {
if (this._isEnabled) {
defines.DETAIL_NORMALBLENDMETHOD = this._normalBlendMethod;
const engine = scene.getEngine();
if (defines._areTexturesDirty) {
if (engine.getCaps().standardDerivatives && this._texture && MaterialFlags.DetailTextureEnabled && this._isEnabled) {
PrepareDefinesForMergedUV(this._texture, defines, "DETAIL");
defines.DETAIL_NORMALBLENDMETHOD = this._normalBlendMethod;
}
else {
defines.DETAIL = false;
}
}
}
else {
defines.DETAIL = false;
}
}
/**
* Binds the detail map data for a sub mesh.
* @param uniformBuffer defines the uniform buffer to update
* @param scene defines the scene to use for texture binding
*/
bindForSubMesh(uniformBuffer, scene) {
if (!this._isEnabled) {
return;
}
const isFrozen = this._material.isFrozen;
if (!uniformBuffer.useUbo || !isFrozen || !uniformBuffer.isSync) {
if (this._texture && MaterialFlags.DetailTextureEnabled) {
uniformBuffer.updateFloat4("vDetailInfos", this._texture.coordinatesIndex, this.diffuseBlendLevel, this.bumpLevel, this.roughnessBlendLevel);
BindTextureMatrix(this._texture, uniformBuffer, "detail");
}
}
// Textures
if (scene.texturesEnabled) {
if (this._texture && MaterialFlags.DetailTextureEnabled) {
uniformBuffer.setTexture("detailSampler", this._texture);
}
}
}
/**
* Checks whether the detail map uses a texture.
* @param texture defines the texture to check
* @returns true if the texture is used by the detail map
*/
hasTexture(texture) {
if (this._texture === texture) {
return true;
}
return false;
}
/**
* Adds the active detail map textures.
* @param activeTextures defines the list of active textures to update
*/
getActiveTextures(activeTextures) {
if (this._texture) {
activeTextures.push(this._texture);
}
}
/**
* Adds the animatable detail map textures.
* @param animatables defines the list of animatables to update
*/
getAnimatables(animatables) {
if (this._texture && this._texture.animations && this._texture.animations.length > 0) {
animatables.push(this._texture);
}
}
/**
* Disposes the detail map textures.
* @param forceDisposeTextures defines whether to dispose the textures
*/
dispose(forceDisposeTextures) {
if (forceDisposeTextures) {
this._texture?.dispose();
}
}
getClassName() {
return "DetailMapConfiguration";
}
/**
* Adds the detail map sampler names.
* @param samplers defines the list of sampler names to update
*/
getSamplers(samplers) {
samplers.push("detailSampler");
}
getUniforms() {
return {
ubo: [
{ name: "vDetailInfos", size: 4, type: "vec4" },
{ name: "detailMatrix", size: 16, type: "mat4" },
],
};
}
},
_DetailMapConfiguration_texture_accessor_storage = new WeakMap(),
_DetailMapConfiguration_normalBlendMethod_accessor_storage = new WeakMap(),
_DetailMapConfiguration_isEnabled_accessor_storage = new WeakMap(),
(() => {
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
_texture_decorators = [serializeAsTexture("detailTexture"), expandToProperty("_markAllSubMeshesAsTexturesDirty")];
_diffuseBlendLevel_decorators = [serialize()];
_roughnessBlendLevel_decorators = [serialize()];
_bumpLevel_decorators = [serialize()];
_normalBlendMethod_decorators = [serialize(), expandToProperty("_markAllSubMeshesAsTexturesDirty")];
_isEnabled_decorators = [serialize(), expandToProperty("_markAllSubMeshesAsTexturesDirty")];
__esDecorate(_a, null, _texture_decorators, { kind: "accessor", name: "texture", static: false, private: false, access: { has: obj => "texture" in obj, get: obj => obj.texture, set: (obj, value) => { obj.texture = value; } }, metadata: _metadata }, _texture_initializers, _texture_extraInitializers);
__esDecorate(_a, null, _normalBlendMethod_decorators, { kind: "accessor", name: "normalBlendMethod", static: false, private: false, access: { has: obj => "normalBlendMethod" in obj, get: obj => obj.normalBlendMethod, set: (obj, value) => { obj.normalBlendMethod = value; } }, metadata: _metadata }, _normalBlendMethod_initializers, _normalBlendMethod_extraInitializers);
__esDecorate(_a, null, _isEnabled_decorators, { kind: "accessor", name: "isEnabled", static: false, private: false, access: { has: obj => "isEnabled" in obj, get: obj => obj.isEnabled, set: (obj, value) => { obj.isEnabled = value; } }, metadata: _metadata }, _isEnabled_initializers, _isEnabled_extraInitializers);
__esDecorate(null, null, _diffuseBlendLevel_decorators, { kind: "field", name: "diffuseBlendLevel", static: false, private: false, access: { has: obj => "diffuseBlendLevel" in obj, get: obj => obj.diffuseBlendLevel, set: (obj, value) => { obj.diffuseBlendLevel = value; } }, metadata: _metadata }, _diffuseBlendLevel_initializers, _diffuseBlendLevel_extraInitializers);
__esDecorate(null, null, _roughnessBlendLevel_decorators, { kind: "field", name: "roughnessBlendLevel", static: false, private: false, access: { has: obj => "roughnessBlendLevel" in obj, get: obj => obj.roughnessBlendLevel, set: (obj, value) => { obj.roughnessBlendLevel = value; } }, metadata: _metadata }, _roughnessBlendLevel_initializers, _roughnessBlendLevel_extraInitializers);
__esDecorate(null, null, _bumpLevel_decorators, { kind: "field", name: "bumpLevel", static: false, private: false, access: { has: obj => "bumpLevel" in obj, get: obj => obj.bumpLevel, set: (obj, value) => { obj.bumpLevel = value; } }, metadata: _metadata }, _bumpLevel_initializers, _bumpLevel_extraInitializers);
if (_metadata) Object.defineProperty(_a, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
})(),
_a;
})();
export { DetailMapConfiguration as D, MaterialPluginBase as M };
//# sourceMappingURL=material.detailMapConfiguration-CHgbrJ-3.esm.js.map