UNPKG

playcanvas

Version:

Open-source WebGL/WebGPU 3D engine for the web

230 lines (229 loc) 7.99 kB
import { Debug } from "../../core/debug.js"; import { http } from "../../platform/net/http.js"; import { standardMaterialCubemapParameters, standardMaterialTextureParameters } from "../../scene/materials/standard-material-parameters.js"; import { AssetReference } from "../asset/asset-reference.js"; import { JsonStandardMaterialParser } from "../parsers/material/json-standard-material.js"; import { ResourceHandler } from "./handler.js"; import { getBuiltInTexture } from "../../platform/graphics/built-in-textures.js"; const PLACEHOLDER_MAP = { aoMap: "white", aoDetailMap: "white", diffuseMap: "gray", diffuseDetailMap: "gray", specularMap: "gray", specularityFactorMap: "white", metalnessMap: "black", glossMap: "gray", sheenMap: "black", sheenGlossMap: "gray", clearCoatMap: "black", clearCoatGlossMap: "gray", clearCoatNormalMap: "normal", refractionMap: "white", emissiveMap: "gray", normalMap: "normal", normalDetailMap: "normal", heightMap: "gray", opacityMap: "gray", sphereMap: "gray", lightMap: "white", thicknessMap: "black", iridescenceMap: "black", iridescenceThicknessMap: "black", envAtlas: "black", anisotropyMap: "black" }; class MaterialHandler extends ResourceHandler { /** * Create a new MaterialHandler instance. * * @param {AppBase} app - The running {@link AppBase}. * @ignore */ constructor(app) { super(app, "material"); this._assets = app.assets; this._device = app.graphicsDevice; this._parser = new JsonStandardMaterialParser(); } load(url, callback) { if (typeof url === "string") { url = { load: url, original: url }; } http.get(url.load, { retry: this.maxRetries > 0, maxRetries: this.maxRetries }, (err, response) => { if (!err) { if (callback) { response._engine = true; callback(null, response); } } else { if (callback) { callback(`Error loading material: ${url.original} [${err}]`); } } }); } open(url, data) { const material = this._parser.parse(data); if (data._engine) { material._data = data; delete data._engine; } return material; } patch(asset, assets) { if (asset.resource._data) { asset._data = asset.resource._data; delete asset.resource._data; } asset.data.name = asset.name; asset.resource.name = asset.name; this._bindAndAssignAssets(asset, assets); asset.off("unload", this._onAssetUnload, this); asset.on("unload", this._onAssetUnload, this); } _onAssetUnload(asset) { delete asset.data.parameters; delete asset.data.chunks; delete asset.data.name; } _assignTexture(parameterName, materialAsset, texture) { materialAsset.resource[parameterName] = texture; } // returns the correct placeholder texture for the texture parameter _getPlaceholderTexture(parameterName) { const placeholder = PLACEHOLDER_MAP[parameterName]; Debug.assert(placeholder, `No placeholder texture found for parameter: ${parameterName}`); return getBuiltInTexture(this._device, placeholder); } // assign a placeholder texture while waiting for one to load _assignPlaceholderTexture(parameterName, materialAsset) { materialAsset.resource[parameterName] = this._getPlaceholderTexture(parameterName); } _onTextureLoad(parameterName, materialAsset, textureAsset) { this._assignTexture(parameterName, materialAsset, textureAsset.resource); materialAsset.resource.update(); } _onTextureAdd(parameterName, materialAsset, textureAsset) { this._assets.load(textureAsset); } _onTextureRemoveOrUnload(parameterName, materialAsset, textureAsset) { const material = materialAsset.resource; if (material) { if (materialAsset.resource[parameterName] === textureAsset.resource) { this._assignPlaceholderTexture(parameterName, materialAsset); material.update(); } } } _assignCubemap(parameterName, materialAsset, textures) { materialAsset.resource[parameterName] = textures[0]; if (parameterName === "cubeMap") { const prefiltered = textures.slice(1); if (prefiltered.every((t) => t)) { materialAsset.resource.prefilteredCubemaps = prefiltered; } else if (prefiltered[0]) { materialAsset.resource.envAtlas = prefiltered[0]; } } } _onCubemapLoad(parameterName, materialAsset, cubemapAsset) { this._assignCubemap(parameterName, materialAsset, cubemapAsset.resources); this._parser.initialize(materialAsset.resource, materialAsset.data); } _onCubemapAdd(parameterName, materialAsset, cubemapAsset) { this._assets.load(cubemapAsset); } _onCubemapRemoveOrUnload(parameterName, materialAsset, cubemapAsset) { const material = materialAsset.resource; if (materialAsset.data.prefilteredCubeMap128 === cubemapAsset.resources[1]) { this._assignCubemap(parameterName, materialAsset, [null, null, null, null, null, null, null]); material.update(); } } _bindAndAssignAssets(materialAsset, assets) { const data = this._parser.migrate(materialAsset.data); const material = materialAsset.resource; const pathMapping = data.mappingFormat === "path"; const TEXTURES = standardMaterialTextureParameters; let i, name, assetReference; for (i = 0; i < TEXTURES.length; i++) { name = TEXTURES[i]; assetReference = material._assetReferences[name]; const dataAssetId = data[name]; const materialTexture = material[name]; const isPlaceHolderTexture = materialTexture === this._getPlaceholderTexture(name); const dataValidated = data.validated; if (dataAssetId && (!materialTexture || !dataValidated || isPlaceHolderTexture)) { if (!assetReference) { assetReference = new AssetReference(name, materialAsset, assets, { load: this._onTextureLoad, add: this._onTextureAdd, remove: this._onTextureRemoveOrUnload, unload: this._onTextureRemoveOrUnload }, this); material._assetReferences[name] = assetReference; } if (pathMapping) { assetReference.url = materialAsset.getAbsoluteUrl(dataAssetId); } else { assetReference.id = dataAssetId; } if (assetReference.asset) { if (assetReference.asset.resource) { this._assignTexture(name, materialAsset, assetReference.asset.resource); } else { this._assignPlaceholderTexture(name, materialAsset); } assets.load(assetReference.asset); } } else { if (assetReference) { if (pathMapping) { assetReference.url = null; } else { assetReference.id = null; } } else { } } } const CUBEMAPS = standardMaterialCubemapParameters; for (i = 0; i < CUBEMAPS.length; i++) { name = CUBEMAPS[i]; assetReference = material._assetReferences[name]; if (data[name] && !materialAsset.data.prefilteredCubeMap128) { if (!assetReference) { assetReference = new AssetReference(name, materialAsset, assets, { load: this._onCubemapLoad, add: this._onCubemapAdd, remove: this._onCubemapRemoveOrUnload, unload: this._onCubemapRemoveOrUnload }, this); material._assetReferences[name] = assetReference; } if (pathMapping) { assetReference.url = data[name]; } else { assetReference.id = data[name]; } if (assetReference.asset) { if (assetReference.asset.loaded) { this._assignCubemap(name, materialAsset, assetReference.asset.resources); } assets.load(assetReference.asset); } } } this._parser.initialize(material, data); } } export { MaterialHandler };