diff --git a/packages/core/src/Utils.ts b/packages/core/src/Utils.ts index 8c2c8b03bf..f312853312 100644 --- a/packages/core/src/Utils.ts +++ b/packages/core/src/Utils.ts @@ -78,6 +78,12 @@ export class Utils { return relativeUrl; } + if (!/^https?:/.test(baseUrl)) { + const fileSchema = "files://"; + baseUrl = fileSchema + baseUrl; + return new URL(relativeUrl, baseUrl).href.substring(fileSchema.length); + } + return relativeUrl ? new URL(relativeUrl, baseUrl).href : baseUrl; } diff --git a/packages/core/src/asset/AssetType.ts b/packages/core/src/asset/AssetType.ts index e5d6fef91f..4f8ddab140 100644 --- a/packages/core/src/asset/AssetType.ts +++ b/packages/core/src/asset/AssetType.ts @@ -23,6 +23,8 @@ export enum AssetType { TextureCube = "TextureCube", /** Material. */ Material = "Material", + /** Shader. */ + Shader = "Shader", /** Mesh. */ Mesh = "Mesh", /** AnimationClip. */ diff --git a/packages/core/src/asset/Loader.ts b/packages/core/src/asset/Loader.ts index efe38d5311..a3bf73817b 100644 --- a/packages/core/src/asset/Loader.ts +++ b/packages/core/src/asset/Loader.ts @@ -41,5 +41,8 @@ export abstract class Loader { constructor(public readonly useCache: boolean) {} initialize?(engine: Engine, configuration: EngineConfiguration): Promise; abstract load(item: LoadItem, resourceManager: ResourceManager): AssetPromise; - request: (url: string, config: RequestConfig) => AssetPromise = request; + request(url: string, resourceManager: ResourceManager, config: RequestConfig): AssetPromise { + const remoteUrl = resourceManager._virtualPathMap[url] ?? url; + return request(remoteUrl, config); + } } diff --git a/packages/core/src/asset/ResourceManager.ts b/packages/core/src/asset/ResourceManager.ts index c3bedf1177..19550684be 100644 --- a/packages/core/src/asset/ResourceManager.ts +++ b/packages/core/src/asset/ResourceManager.ts @@ -183,12 +183,14 @@ export class ResourceManager { * @internal */ _onSubAssetSuccess(assetBaseURL: string, assetSubPath: string, value: T): void { - const subPromiseCallback = this._subAssetPromiseCallbacks[assetBaseURL]?.[assetSubPath]; + const remoteAssetBaseURL = this._virtualPathMap[assetBaseURL] ?? assetBaseURL; + + const subPromiseCallback = this._subAssetPromiseCallbacks[remoteAssetBaseURL]?.[assetSubPath]; if (subPromiseCallback) { subPromiseCallback.resolve(value); } else { // Pending - (this._subAssetPromiseCallbacks[assetBaseURL] ||= {})[assetSubPath] = { + (this._subAssetPromiseCallbacks[remoteAssetBaseURL] ||= {})[assetSubPath] = { resolvedValue: value }; } @@ -326,10 +328,7 @@ export class ResourceManager { private _loadSingleItem(itemOrURL: LoadItem | string): AssetPromise { const item = this._assignDefaultOptions(typeof itemOrURL === "string" ? { url: itemOrURL } : itemOrURL); - - // Check url mapping - const itemURL = item.url; - let url = this._virtualPathMap[itemURL] ? this._virtualPathMap[itemURL] : itemURL; + let { url } = item; // Not absolute and base url is set if (!Utils.isAbsoluteUrl(url) && this.baseUrl) url = Utils.resolveAbsoluteUrl(this.baseUrl, url); @@ -338,8 +337,11 @@ export class ResourceManager { const { assetBaseURL, queryPath } = this._parseURL(url); const paths = queryPath ? this._parseQueryPath(queryPath) : []; + // Get remote asset base url + const remoteAssetBaseURL = this._virtualPathMap[assetBaseURL] ?? assetBaseURL; + // Check cache - const cacheObject = this._assetUrlPool[assetBaseURL]; + const cacheObject = this._assetUrlPool[remoteAssetBaseURL]; if (cacheObject) { return new AssetPromise((resolve) => { resolve(this._getResolveResource(cacheObject, paths) as T); @@ -347,18 +349,18 @@ export class ResourceManager { } // Get asset url - let assetURL = assetBaseURL; + let remoteAssetURL = remoteAssetBaseURL; if (queryPath) { - assetURL += "?q=" + paths.shift(); + remoteAssetURL += "?q=" + paths.shift(); let index: string; while ((index = paths.shift())) { - assetURL += `[${index}]`; + remoteAssetURL += `[${index}]`; } } // Check is loading const loadingPromises = this._loadingPromises; - const loadingPromise = loadingPromises[assetURL]; + const loadingPromise = loadingPromises[remoteAssetURL]; if (loadingPromise) { return new AssetPromise((resolve, reject, setTaskCompleteProgress, setTaskDetailProgress) => { loadingPromise @@ -381,34 +383,40 @@ export class ResourceManager { // Check sub asset if (queryPath) { // Check whether load main asset - const mainPromise = loadingPromises[assetBaseURL] || this._loadMainAsset(loader, item, assetBaseURL); + const mainPromise = + loadingPromises[remoteAssetBaseURL] || this._loadMainAsset(loader, item, remoteAssetBaseURL, assetBaseURL); mainPromise.catch((e) => { - this._onSubAssetFail(assetBaseURL, queryPath, e); + this._onSubAssetFail(remoteAssetBaseURL, queryPath, e); }); - return this._createSubAssetPromiseCallback(assetBaseURL, assetURL, queryPath); + return this._createSubAssetPromiseCallback(remoteAssetBaseURL, remoteAssetURL, queryPath); } - return this._loadMainAsset(loader, item, assetBaseURL); + return this._loadMainAsset(loader, item, remoteAssetBaseURL, assetBaseURL); } - private _loadMainAsset(loader: Loader, item: LoadItem, assetBaseURL: string): AssetPromise { + private _loadMainAsset( + loader: Loader, + item: LoadItem, + remoteAssetBaseURL: string, + assetBaseURL: string + ): AssetPromise { item.url = assetBaseURL; const loadingPromises = this._loadingPromises; const promise = loader.load(item, this); - loadingPromises[assetBaseURL] = promise; + loadingPromises[remoteAssetBaseURL] = promise; promise.then( (resource: T) => { if (loader.useCache) { - this._addAsset(assetBaseURL, resource as EngineObject); + this._addAsset(remoteAssetBaseURL, resource as EngineObject); } - delete loadingPromises[assetBaseURL]; - this._releaseSubAssetPromiseCallback(assetBaseURL); + delete loadingPromises[remoteAssetBaseURL]; + this._releaseSubAssetPromiseCallback(remoteAssetBaseURL); }, () => { - delete loadingPromises[assetBaseURL]; - this._releaseSubAssetPromiseCallback(assetBaseURL); + delete loadingPromises[remoteAssetBaseURL]; + this._releaseSubAssetPromiseCallback(remoteAssetBaseURL); } ); @@ -416,12 +424,12 @@ export class ResourceManager { } private _createSubAssetPromiseCallback( - assetBaseURL: string, - assetURL: string, + remoteAssetBaseURL: string, + remoteAssetURL: string, assetSubPath: string ): AssetPromise { const loadingPromises = this._loadingPromises; - const subPromiseCallback = this._subAssetPromiseCallbacks[assetBaseURL]?.[assetSubPath]; + const subPromiseCallback = this._subAssetPromiseCallbacks[remoteAssetBaseURL]?.[assetSubPath]; const resolvedValue = subPromiseCallback?.resolvedValue; const rejectedValue = subPromiseCallback?.rejectedValue; @@ -438,19 +446,19 @@ export class ResourceManager { // Pending const promise = new AssetPromise((resolve, reject) => { - (this._subAssetPromiseCallbacks[assetBaseURL] ||= {})[assetSubPath] = { + (this._subAssetPromiseCallbacks[remoteAssetBaseURL] ||= {})[assetSubPath] = { resolve, reject }; }); - loadingPromises[assetURL] = promise; + loadingPromises[remoteAssetURL] = promise; promise.then( () => { - delete loadingPromises[assetURL]; + delete loadingPromises[remoteAssetURL]; }, - () => delete loadingPromises[assetURL] + () => delete loadingPromises[remoteAssetURL] ); return promise; @@ -537,15 +545,21 @@ export class ResourceManager { if (obj) { promise = Promise.resolve(obj); } else { - let url = this._editorResourceConfig[refId]?.path; - if (!url) { + const resourceConfig = this._editorResourceConfig[refId]; + if (!resourceConfig) { Logger.warn(`refId:${refId} is not find in this._editorResourceConfig.`); return Promise.resolve(null); } - url = key ? `${url}${url.indexOf("?") > -1 ? "&" : "?"}q=${key}` : url; + const remoteUrl = resourceConfig.path; + const queryPath = new URL(remoteUrl).search; + let url = resourceConfig.virtualPath + queryPath; + if (key) { + url += (url.indexOf("?") > -1 ? "&" : "?") + "q=" + key; + } + promise = this.load({ url, - type: this._editorResourceConfig[refId].type + type: resourceConfig.type }); } return promise.then((item) => (isClone ? item.clone() : item)); diff --git a/packages/loader/src/AnimationClipLoader.ts b/packages/loader/src/AnimationClipLoader.ts index b6352a3dd5..0c5cfa4521 100644 --- a/packages/loader/src/AnimationClipLoader.ts +++ b/packages/loader/src/AnimationClipLoader.ts @@ -14,7 +14,7 @@ import { decode } from "./resource-deserialize"; class AnimationClipLoader extends Loader { load(item: LoadItem, resourceManager: ResourceManager): AssetPromise { return new AssetPromise((resolve, reject) => { - this.request(item.url, { + this.request(item.url, resourceManager, { ...item, type: "arraybuffer" }) diff --git a/packages/loader/src/AnimatorControllerLoader.ts b/packages/loader/src/AnimatorControllerLoader.ts index 06728a6f7a..e420ff72e8 100644 --- a/packages/loader/src/AnimatorControllerLoader.ts +++ b/packages/loader/src/AnimatorControllerLoader.ts @@ -19,7 +19,7 @@ import { class AnimatorControllerLoader extends Loader { load(item: LoadItem, resourceManager: ResourceManager): AssetPromise { return new AssetPromise((resolve, reject) => { - this.request(item.url, { + this.request(item.url, resourceManager, { ...item, type: "json" }) diff --git a/packages/loader/src/BufferLoader.ts b/packages/loader/src/BufferLoader.ts index c4bc38e9fe..564cd8bbc1 100644 --- a/packages/loader/src/BufferLoader.ts +++ b/packages/loader/src/BufferLoader.ts @@ -1,11 +1,11 @@ -import { resourceLoader, Loader, AssetPromise, AssetType, LoadItem } from "@galacean/engine-core"; +import { resourceLoader, Loader, AssetPromise, AssetType, LoadItem, ResourceManager } from "@galacean/engine-core"; function isBase64(url) { return /^data:(.+?);base64,/.test(url); } @resourceLoader(AssetType.Buffer, ["bin", "r3bin"], false) class BufferLoader extends Loader { - load(item: LoadItem): AssetPromise { + load(item: LoadItem, resourceManager: ResourceManager): AssetPromise { const url = item.url; if (isBase64(url)) { return new AssetPromise((resolve) => { @@ -14,7 +14,7 @@ class BufferLoader extends Loader { resolve(result.buffer); }); } - return this.request(url, { + return this.request(url, resourceManager, { ...item, type: "arraybuffer" }); diff --git a/packages/loader/src/EnvLoader.ts b/packages/loader/src/EnvLoader.ts index d7c9ca7290..99ca9f7ee9 100644 --- a/packages/loader/src/EnvLoader.ts +++ b/packages/loader/src/EnvLoader.ts @@ -17,7 +17,7 @@ import { SphericalHarmonics3 } from "@galacean/engine-math"; class EnvLoader extends Loader { load(item: LoadItem, resourceManager: ResourceManager): AssetPromise { return new AssetPromise((resolve, reject) => { - this.request(item.url, { ...item, type: "arraybuffer" }) + this.request(item.url, resourceManager, { ...item, type: "arraybuffer" }) .then((arraybuffer) => { const shArray = new Float32Array(arraybuffer, 0, 27); const shByteLength = 27 * 4; diff --git a/packages/loader/src/FontLoader.ts b/packages/loader/src/FontLoader.ts index 2357ada08a..4441e80ba5 100644 --- a/packages/loader/src/FontLoader.ts +++ b/packages/loader/src/FontLoader.ts @@ -12,7 +12,7 @@ import { class FontLoader extends Loader { load(item: LoadItem, resourceManager: ResourceManager): AssetPromise { return new AssetPromise((resolve, reject) => { - this.request(item.url, { ...item, type: "json" }) + this.request(item.url, resourceManager, { ...item, type: "json" }) .then((data) => { const { fontName, fontUrl } = data; diff --git a/packages/loader/src/GLTFLoader.ts b/packages/loader/src/GLTFLoader.ts index 0707d17aac..cff590de7f 100644 --- a/packages/loader/src/GLTFLoader.ts +++ b/packages/loader/src/GLTFLoader.ts @@ -37,9 +37,8 @@ export class GLTFLoader extends Loader { } override load(item: LoadItem, resourceManager: ResourceManager): AssetPromise { - const url = item.url; const params = item.params; - const glTFResource = new GLTFResource(resourceManager.engine, url); + const glTFResource = new GLTFResource(resourceManager.engine, item.url); const context = new GLTFParserContext(glTFResource, resourceManager, { keepMeshData: false, ...params diff --git a/packages/loader/src/HDRLoader.ts b/packages/loader/src/HDRLoader.ts index 3f72080728..d9504d5ac3 100644 --- a/packages/loader/src/HDRLoader.ts +++ b/packages/loader/src/HDRLoader.ts @@ -381,7 +381,7 @@ class HDRLoader extends Loader { return new AssetPromise((resolve, reject) => { const engine = resourceManager.engine; - this.request(item.url, { ...item, type: "arraybuffer" }) + this.request(item.url, resourceManager, { ...item, type: "arraybuffer" }) .then((buffer) => { const uint8Array = new Uint8Array(buffer); const { width, height, dataPosition } = HDRLoader._parseHeader(uint8Array); diff --git a/packages/loader/src/JSONLoader.ts b/packages/loader/src/JSONLoader.ts index 364b1159b4..7942f15210 100644 --- a/packages/loader/src/JSONLoader.ts +++ b/packages/loader/src/JSONLoader.ts @@ -1,9 +1,9 @@ -import { resourceLoader, Loader, AssetPromise, AssetType, LoadItem } from "@galacean/engine-core"; +import { resourceLoader, Loader, AssetPromise, AssetType, LoadItem, ResourceManager } from "@galacean/engine-core"; @resourceLoader(AssetType.JSON, ["json"], false) class JSONLoader extends Loader { - load(item: LoadItem): AssetPromise { - return this.request(item.url, { + load(item: LoadItem, resourceManager: ResourceManager): AssetPromise { + return this.request(item.url, resourceManager, { ...item, type: "json" }); diff --git a/packages/loader/src/KTXCubeLoader.ts b/packages/loader/src/KTXCubeLoader.ts index 8e459c50a4..4a9607a776 100644 --- a/packages/loader/src/KTXCubeLoader.ts +++ b/packages/loader/src/KTXCubeLoader.ts @@ -16,7 +16,7 @@ class KTXCubeLoader extends Loader { return new AssetPromise((resolve, reject) => { Promise.all( item.urls.map((url) => - this.request(url, { + this.request(url, resourceManager, { ...item, type: "arraybuffer" }) diff --git a/packages/loader/src/KTXLoader.ts b/packages/loader/src/KTXLoader.ts index cdfa175a22..191d6c20be 100644 --- a/packages/loader/src/KTXLoader.ts +++ b/packages/loader/src/KTXLoader.ts @@ -13,7 +13,7 @@ import { parseSingleKTX } from "./compressed-texture"; export class KTXLoader extends Loader { load(item: LoadItem, resourceManager: ResourceManager): AssetPromise { return new AssetPromise((resolve, reject) => { - this.request(item.url, { + this.request(item.url, resourceManager, { ...item, type: "arraybuffer" }) diff --git a/packages/loader/src/MaterialLoader.ts b/packages/loader/src/MaterialLoader.ts index 6048f24301..c58cba8d12 100644 --- a/packages/loader/src/MaterialLoader.ts +++ b/packages/loader/src/MaterialLoader.ts @@ -1,6 +1,7 @@ import { AssetPromise, AssetType, + Engine, LoadItem, Loader, Material, @@ -10,7 +11,15 @@ import { resourceLoader } from "@galacean/engine-core"; import { Color, Vector2, Vector3, Vector4 } from "@galacean/engine-math"; -import type { IAssetRef, IColor, IMaterialSchema, IVector2, IVector3, IVector4 } from "./resource-deserialize"; +import { + MaterialLoaderType, + type IAssetRef, + type IColor, + type IMaterialSchema, + type IVector2, + type IVector3, + type IVector4 +} from "./resource-deserialize"; function parseProperty(object: Object, key: string, value: any) { if (typeof value === "object") { @@ -26,79 +35,97 @@ function parseProperty(object: Object, key: string, value: any) { class MaterialLoader extends Loader { load(item: LoadItem, resourceManager: ResourceManager): AssetPromise { return new AssetPromise((resolve, reject) => { - this.request(item.url, { + this.request(item.url, resourceManager, { ...item, type: "json" }) .then((materialSchema: IMaterialSchema) => { const engine = resourceManager.engine; - const { name, shader, shaderData, macros, renderState } = materialSchema; - const material = new Material(engine, Shader.find(shader)); - material.name = name; - - const texturePromises = new Array>(); - const materialShaderData = material.shaderData; - for (let key in shaderData) { - const { type, value } = shaderData[key]; + const { shaderRef, shader: shaderName } = materialSchema; - switch (type) { - case "Vector2": - materialShaderData.setVector2(key, new Vector2((value).x, (value).y)); - break; - case "Vector3": - materialShaderData.setVector3( - key, - new Vector3((value).x, (value).y, (value).z) - ); - break; - case "Vector4": - materialShaderData.setVector4( - key, - new Vector4((value).x, (value).y, (value).z, (value).w) - ); - break; - case "Color": - materialShaderData.setColor( - key, - new Color((value).r, (value).g, (value).b, (value).a) - ); - break; - case "Float": - materialShaderData.setFloat(key, value); - break; - case "Texture": - texturePromises.push( - // @ts-ignore - resourceManager.getResourceByRef(value).then((texture) => { - materialShaderData.setTexture(key, texture); - }) - ); - break; - case "Boolean": - materialShaderData.setInt(key, value ? 1 : 0); - break; - case "Integer": - materialShaderData.setInt(key, Number(value)); - break; - } + if (shaderRef) { + resolve( + resourceManager + // @ts-ignore + .getResourceByRef(shaderRef) + .then((shader) => this.getMaterialByShader(materialSchema, shader, engine)) + ); + } else { + // compatible with 1.2-pre version material schema + const shader = Shader.find(shaderName); + resolve(this.getMaterialByShader(materialSchema, shader, engine)); } + }) + .catch(reject); + }); + } - for (let i = 0, length = macros.length; i < length; i++) { - const { name, value } = macros[i]; - if (value == undefined) { - materialShaderData.enableMacro(name); - } else { - materialShaderData.enableMacro(name, value); - } - } + private getMaterialByShader(materialSchema: IMaterialSchema, shader: Shader, engine: Engine): Promise { + const { name, shaderData, macros, renderState } = materialSchema; - parseProperty(material, "renderState", renderState); + const material = new Material(engine, shader); + material.name = name; - return Promise.all(texturePromises).then(() => { - resolve(material); - }); - }) - .catch(reject); + const texturePromises = new Array>(); + const materialShaderData = material.shaderData; + for (let key in shaderData) { + const { type, value } = shaderData[key]; + + switch (type) { + case MaterialLoaderType.Vector2: + materialShaderData.setVector2(key, new Vector2((value).x, (value).y)); + break; + case MaterialLoaderType.Vector3: + materialShaderData.setVector3( + key, + new Vector3((value).x, (value).y, (value).z) + ); + break; + case MaterialLoaderType.Vector4: + materialShaderData.setVector4( + key, + new Vector4((value).x, (value).y, (value).z, (value).w) + ); + break; + case MaterialLoaderType.Color: + materialShaderData.setColor( + key, + new Color((value).r, (value).g, (value).b, (value).a) + ); + break; + case MaterialLoaderType.Float: + materialShaderData.setFloat(key, value); + break; + case MaterialLoaderType.Texture: + texturePromises.push( + // @ts-ignore + engine.resourceManager.getResourceByRef(value).then((texture) => { + materialShaderData.setTexture(key, texture); + }) + ); + break; + case MaterialLoaderType.Boolean: + materialShaderData.setInt(key, value ? 1 : 0); + break; + case MaterialLoaderType.Integer: + materialShaderData.setInt(key, Number(value)); + break; + } + } + + for (let i = 0, length = macros.length; i < length; i++) { + const { name, value } = macros[i]; + if (value == undefined) { + materialShaderData.enableMacro(name); + } else { + materialShaderData.enableMacro(name, value); + } + } + + parseProperty(material, "renderState", renderState); + + return Promise.all(texturePromises).then(() => { + return material; }); } } diff --git a/packages/loader/src/MeshLoader.ts b/packages/loader/src/MeshLoader.ts deleted file mode 100644 index 73ff264273..0000000000 --- a/packages/loader/src/MeshLoader.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { - resourceLoader, - Loader, - AssetPromise, - AssetType, - LoadItem, - ResourceManager, - ModelMesh -} from "@galacean/engine-core"; -import { decode } from "./resource-deserialize"; - -@resourceLoader(AssetType.Mesh, ["mesh"]) -class MeshLoader extends Loader { - load(item: LoadItem, resourceManager: ResourceManager): AssetPromise { - return new AssetPromise((resolve, reject) => { - this.request(item.url, { - ...item, - type: "arraybuffer" - }) - .then((data) => { - return decode(data, resourceManager.engine); - }) - .then((mesh) => { - resolve(mesh); - }) - .catch(reject); - }); - } -} diff --git a/packages/loader/src/PrefabLoader.ts b/packages/loader/src/PrefabLoader.ts index 8eaeb792ef..d536502092 100644 --- a/packages/loader/src/PrefabLoader.ts +++ b/packages/loader/src/PrefabLoader.ts @@ -9,7 +9,7 @@ export class PrefabLoader extends Loader { const engine = resourceManager.engine; return new AssetPromise((resolve, reject) => { - this.request(item.url, { + this.request(item.url, resourceManager, { ...item, type: "json" }).then((data) => { diff --git a/packages/loader/src/PrimitiveMeshLoader.ts b/packages/loader/src/PrimitiveMeshLoader.ts index 59d4e58863..cc85be7cf2 100644 --- a/packages/loader/src/PrimitiveMeshLoader.ts +++ b/packages/loader/src/PrimitiveMeshLoader.ts @@ -5,13 +5,15 @@ import { Loader, ModelMesh, PrimitiveMesh, + ResourceManager, resourceLoader } from "@galacean/engine-core"; @resourceLoader(AssetType.PrimitiveMesh, ["mesh"], false) class PrimitiveMeshLoader extends Loader { - load(item: LoadItem, { engine }): AssetPromise { - return this.request(item.url, { + load(item: LoadItem, resourceManager: ResourceManager): AssetPromise { + const { engine } = resourceManager; + return this.request(item.url, resourceManager, { ...item, type: "json" }).then((data) => { diff --git a/packages/loader/src/ProjectLoader.ts b/packages/loader/src/ProjectLoader.ts index 9c9c54ff12..82441d2260 100644 --- a/packages/loader/src/ProjectLoader.ts +++ b/packages/loader/src/ProjectLoader.ts @@ -14,7 +14,7 @@ class ProjectLoader extends Loader { load(item: LoadItem, resourceManager: ResourceManager): AssetPromise { const { engine } = resourceManager; return new AssetPromise((resolve, reject) => { - this.request(item.url, { ...item, type: "json" }) + this.request(item.url, resourceManager, { ...item, type: "json" }) .then((data) => { // @ts-ignore engine.resourceManager.initVirtualResources(data.files); diff --git a/packages/loader/src/SceneLoader.ts b/packages/loader/src/SceneLoader.ts index 70c851a8e6..9fab686d79 100644 --- a/packages/loader/src/SceneLoader.ts +++ b/packages/loader/src/SceneLoader.ts @@ -21,7 +21,7 @@ class SceneLoader extends Loader { load(item: LoadItem, resourceManager: ResourceManager): AssetPromise { const { engine } = resourceManager; return new AssetPromise((resolve, reject) => { - this.request(item.url, { ...item, type: "json" }) + this.request(item.url, resourceManager, { ...item, type: "json" }) .then((data) => { return SceneParser.parse(engine, data).then((scene) => { const promises = []; diff --git a/packages/loader/src/ShaderChunkLoader.ts b/packages/loader/src/ShaderChunkLoader.ts new file mode 100644 index 0000000000..aaaea4d3a4 --- /dev/null +++ b/packages/loader/src/ShaderChunkLoader.ts @@ -0,0 +1,49 @@ +import { + AssetPromise, + LoadItem, + Loader, + ResourceManager, + ShaderFactory, + resourceLoader, + // @ts-ignore + ShaderLib, + Utils +} from "@galacean/engine-core"; + +@resourceLoader("ShaderChunk", ["glsl"]) +export class ShaderChunkLoader extends Loader { + private static _shaderIncludeRegex = /\s#include\s+"([./][^\\"]+)"/gm; + + /** + * @internal + */ + static _loadChunksInCode(code: string, basePath: string, resourceManager: ResourceManager): Promise { + const shaderChunkPaths = new Array(); + const matches = code.matchAll(ShaderChunkLoader._shaderIncludeRegex); + for (const match of matches) { + const chunkPath = Utils.resolveAbsoluteUrl(basePath, match[1]); + if (!ShaderLib[chunkPath.substring(1)]) { + shaderChunkPaths.push(chunkPath); + } + } + + return Promise.all( + shaderChunkPaths.map((chunkPath) => { + return resourceManager.load({ + type: "ShaderChunk", + url: chunkPath + }); + }) + ); + } + + load(item: LoadItem, resourceManager: ResourceManager): AssetPromise { + const { url } = item; + + return this.request(url, resourceManager, { ...item, type: "text" }).then((code) => { + ShaderFactory.registerInclude(url.substring(1), code); + + return ShaderChunkLoader._loadChunksInCode(code, url, resourceManager); + }); + } +} diff --git a/packages/loader/src/ShaderLoader.ts b/packages/loader/src/ShaderLoader.ts new file mode 100644 index 0000000000..00795eec6e --- /dev/null +++ b/packages/loader/src/ShaderLoader.ts @@ -0,0 +1,38 @@ +import { + AssetPromise, + AssetType, + LoadItem, + Loader, + ResourceManager, + Shader, + resourceLoader +} from "@galacean/engine-core"; +import { ShaderChunkLoader } from "./ShaderChunkLoader"; + +@resourceLoader(AssetType.Shader, ["gs", "gsl"]) +class ShaderLoader extends Loader { + private static _builtinRegex = /^\s*\/\/\s*@builtin\s+(\w+)/; + + load(item: LoadItem, resourceManager: ResourceManager): AssetPromise { + const { url } = item; + + return this.request(url, resourceManager, { ...item, type: "text" }).then((code: string) => { + const builtinShader = this._getBuiltinShader(code); + if (builtinShader) { + return Shader.find(builtinShader); + } + + return ShaderChunkLoader._loadChunksInCode(code, url, resourceManager).then(() => { + const shader = Shader.create(code); + // @ts-ignore + shader._registerPath(url); + return shader; + }); + }); + } + + private _getBuiltinShader(code: string) { + const match = code.match(ShaderLoader._builtinRegex); + if (match && match[1]) return match[1]; + } +} diff --git a/packages/loader/src/SourceFontLoader.ts b/packages/loader/src/SourceFontLoader.ts index ee8549560b..5a86000000 100644 --- a/packages/loader/src/SourceFontLoader.ts +++ b/packages/loader/src/SourceFontLoader.ts @@ -12,7 +12,8 @@ import { class SourceFontLoader extends Loader { load(item: LoadItem, resourceManager: ResourceManager): AssetPromise { return new AssetPromise((resolve, reject) => { - const { url } = item; + // @ts-ignore + const url = resourceManager._virtualPathMap[item.url] ?? item.url; this._registerFont(url, url) .then(() => { const font = new Font(resourceManager.engine, url); diff --git a/packages/loader/src/SpriteAtlasLoader.ts b/packages/loader/src/SpriteAtlasLoader.ts index 270773143b..34424d6991 100644 --- a/packages/loader/src/SpriteAtlasLoader.ts +++ b/packages/loader/src/SpriteAtlasLoader.ts @@ -27,7 +27,7 @@ class SpriteAtlasLoader extends Loader { chainPromises[i].cancel(); } }); - const configPromise = this.request(item.url, { + const configPromise = this.request(item.url, resourceManager, { ...item, type: "json" }); diff --git a/packages/loader/src/SpriteLoader.ts b/packages/loader/src/SpriteLoader.ts index f1aae8e423..ebeca92d16 100644 --- a/packages/loader/src/SpriteLoader.ts +++ b/packages/loader/src/SpriteLoader.ts @@ -13,7 +13,7 @@ import { @resourceLoader(AssetType.Sprite, ["sprite"], false) class SpriteLoader extends Loader { load(item: LoadItem, resourceManager: ResourceManager): AssetPromise { - return this.request(item.url, { + return this.request(item.url, resourceManager, { ...item, type: "json" }).then((data) => { diff --git a/packages/loader/src/TextLoader.ts b/packages/loader/src/TextLoader.ts index 1ea1b87feb..9483042a76 100644 --- a/packages/loader/src/TextLoader.ts +++ b/packages/loader/src/TextLoader.ts @@ -1,9 +1,9 @@ -import { resourceLoader, Loader, AssetPromise, AssetType, LoadItem } from "@galacean/engine-core"; +import { resourceLoader, Loader, AssetPromise, AssetType, LoadItem, ResourceManager } from "@galacean/engine-core"; @resourceLoader(AssetType.Text, ["txt"], false) class TextLoader extends Loader { - load(item: LoadItem): AssetPromise { - return this.request(item.url, { + load(item: LoadItem, resourceManager: ResourceManager): AssetPromise { + return this.request(item.url, resourceManager, { ...item, type: "text" }); diff --git a/packages/loader/src/Texture2DLoader.ts b/packages/loader/src/Texture2DLoader.ts index 71425b9a32..9041d85f35 100644 --- a/packages/loader/src/Texture2DLoader.ts +++ b/packages/loader/src/Texture2DLoader.ts @@ -22,7 +22,7 @@ class Texture2DLoader extends Loader { ...item, type: "image" }; - this.request(url, requestConfig) + this.request(url, resourceManager, requestConfig) .onProgress(setTaskCompleteProgress, setTaskDetailProgress) .then((image) => { const { format, mipmap, anisoLevel, wrapModeU, wrapModeV, filterMode } = diff --git a/packages/loader/src/TextureCubeLoader.ts b/packages/loader/src/TextureCubeLoader.ts index 2977a2f93e..a20188b648 100644 --- a/packages/loader/src/TextureCubeLoader.ts +++ b/packages/loader/src/TextureCubeLoader.ts @@ -21,7 +21,7 @@ class TextureCubeLoader extends Loader { type: "image" }; - Promise.all(urls.map((url) => this.request(url, requestConfig))) + Promise.all(urls.map((url) => this.request(url, resourceManager, requestConfig))) .then((images) => { const { width, height } = images[0]; diff --git a/packages/loader/src/gltf/parser/GLTFBufferParser.ts b/packages/loader/src/gltf/parser/GLTFBufferParser.ts index 92222e836c..a5576df29f 100644 --- a/packages/loader/src/gltf/parser/GLTFBufferParser.ts +++ b/packages/loader/src/gltf/parser/GLTFBufferParser.ts @@ -16,9 +16,11 @@ export class GLTFBufferParser extends GLTFParser { private _parseSingleBuffer(context: GLTFParserContext, bufferInfo: IBuffer): Promise { const { glTFResource, contentRestorer } = context; const url = glTFResource.url; + // @ts-ignore + const remoteUrl = context.resourceManager._virtualPathMap[url] ?? url; const restoreBufferRequests = contentRestorer.bufferRequests; const requestConfig = { type: "arraybuffer" }; - const absoluteUrl = Utils.resolveAbsoluteUrl(url, bufferInfo.uri); + const absoluteUrl = Utils.resolveAbsoluteUrl(remoteUrl, bufferInfo.uri); restoreBufferRequests.push(new BufferRequestInfo(absoluteUrl, requestConfig)); const promise = request(absoluteUrl, requestConfig).onProgress(undefined, context._onTaskDetail); diff --git a/packages/loader/src/gltf/parser/GLTFSchemaParser.ts b/packages/loader/src/gltf/parser/GLTFSchemaParser.ts index 919b3f9921..8b43e975c0 100644 --- a/packages/loader/src/gltf/parser/GLTFSchemaParser.ts +++ b/packages/loader/src/gltf/parser/GLTFSchemaParser.ts @@ -13,10 +13,12 @@ export class GLTFSchemaParser extends GLTFParser { const url = glTFResource.url; const restoreBufferRequests = contentRestorer.bufferRequests; const requestConfig = { type: "arraybuffer" }; - return request(url, requestConfig) + // @ts-ignore + const remoteUrl = context.resourceManager._virtualPathMap[url] ?? url; + return request(remoteUrl, requestConfig) .onProgress(undefined, context._onTaskDetail) .then((buffer) => { - restoreBufferRequests.push(new BufferRequestInfo(url, requestConfig)); + restoreBufferRequests.push(new BufferRequestInfo(remoteUrl, requestConfig)); return GLTFUtils.parseGLB(context, buffer); }) .then((result) => { diff --git a/packages/loader/src/index.ts b/packages/loader/src/index.ts index b1da2b95ee..8c937a6e75 100644 --- a/packages/loader/src/index.ts +++ b/packages/loader/src/index.ts @@ -10,7 +10,6 @@ import "./JSONLoader"; import "./KTXCubeLoader"; import "./KTXLoader"; import "./MaterialLoader"; -import "./MeshLoader"; import "./PrimitiveMeshLoader"; import "./ProjectLoader"; import "./SourceFontLoader"; @@ -19,6 +18,8 @@ import "./SpriteLoader"; import "./Texture2DLoader"; import "./TextureCubeLoader"; import "./ktx2/KTX2Loader"; +import "./ShaderLoader"; +import "./ShaderChunkLoader"; export { GLTFLoader } from "./GLTFLoader"; export type { GLTFParams } from "./GLTFLoader"; diff --git a/packages/loader/src/ktx2/KTX2Loader.ts b/packages/loader/src/ktx2/KTX2Loader.ts index 511851d4bf..bd867b2027 100644 --- a/packages/loader/src/ktx2/KTX2Loader.ts +++ b/packages/loader/src/ktx2/KTX2Loader.ts @@ -221,7 +221,7 @@ export class KTX2Loader extends Loader { resourceManager: ResourceManager ): AssetPromise { return new AssetPromise((resolve, reject, setTaskCompleteProgress, setTaskDetailProgress) => { - this.request(item.url, { type: "arraybuffer" }) + this.request(item.url, resourceManager, { type: "arraybuffer" }) .onProgress(setTaskCompleteProgress, setTaskDetailProgress) .then((buffer) => KTX2Loader._parseBuffer(new Uint8Array(buffer), resourceManager.engine, item.params).then( diff --git a/packages/loader/src/resource-deserialize/index.ts b/packages/loader/src/resource-deserialize/index.ts index 67531f3e90..b739322030 100644 --- a/packages/loader/src/resource-deserialize/index.ts +++ b/packages/loader/src/resource-deserialize/index.ts @@ -26,7 +26,6 @@ export function decode(arrayBuffer: ArrayBuffer, engine: Engine): Promise export * from "./resources/schema"; export * from "./resources/scene/SceneParser"; -export * from "./resources/scene/MeshLoader"; export * from "./resources/scene/EditorTextureLoader"; export * from "./resources/parser/ParserContext"; diff --git a/packages/loader/src/resource-deserialize/resources/scene/EditorTextureLoader.ts b/packages/loader/src/resource-deserialize/resources/scene/EditorTextureLoader.ts index 5a4d2ad197..09403013a7 100644 --- a/packages/loader/src/resource-deserialize/resources/scene/EditorTextureLoader.ts +++ b/packages/loader/src/resource-deserialize/resources/scene/EditorTextureLoader.ts @@ -5,7 +5,7 @@ import { decode } from "../.."; export class EditorTextureLoader extends Loader { load(item: LoadItem, resourceManager: ResourceManager): AssetPromise { return new AssetPromise((resolve, reject) => { - this.request(item.url, { ...item, type: "arraybuffer" }) + this.request(item.url, resourceManager, { ...item, type: "arraybuffer" }) .then((data) => { decode(data, resourceManager.engine).then((texture) => { resolve(texture); diff --git a/packages/loader/src/resource-deserialize/resources/scene/MeshLoader.ts b/packages/loader/src/resource-deserialize/resources/scene/MeshLoader.ts deleted file mode 100644 index 6618847eb9..0000000000 --- a/packages/loader/src/resource-deserialize/resources/scene/MeshLoader.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { AssetPromise, Loader, LoadItem, ModelMesh, resourceLoader, ResourceManager } from "@galacean/engine-core"; -import { decode } from "../.."; - -@resourceLoader("Mesh", ["prefab"], true) -export class MeshLoader extends Loader { - load(item: LoadItem, resourceManager: ResourceManager): AssetPromise { - return new AssetPromise((resolve, reject) => { - this.request(item.url, { ...item, type: "arraybuffer" }) - .then((data) => { - decode(data, resourceManager.engine).then((mesh) => { - resolve(mesh); - }); - }) - .catch(reject); - }); - } -} diff --git a/packages/loader/src/resource-deserialize/resources/schema/MaterialSchema.ts b/packages/loader/src/resource-deserialize/resources/schema/MaterialSchema.ts index 377ea86126..f2f6012918 100644 --- a/packages/loader/src/resource-deserialize/resources/schema/MaterialSchema.ts +++ b/packages/loader/src/resource-deserialize/resources/schema/MaterialSchema.ts @@ -90,10 +90,22 @@ export interface IMaterialSchema { shader: string; shaderData: { [key: string]: { - type: "Vector2" | "Vector3" | "Vector4" | "Color" | "Float" | "Texture" | "Boolean" | "Integer"; + type: MaterialLoaderType; value: IVector3 | IVector2 | IColor | number | IAssetRef; }; }; macros: Array<{ name: string; value?: string }>; renderState: IRenderState; + shaderRef: IAssetRef; +} + +export enum MaterialLoaderType { + Vector2 = "Vector2", + Vector3 = "Vector3", + Vector4 = "Vector4", + Color = "Color", + Float = "Float", + Texture = "Texture", + Boolean = "Boolean", + Integer = "Integer" } diff --git a/packages/xr/src/loader/XRReferenceImageLoader.ts b/packages/xr/src/loader/XRReferenceImageLoader.ts index 91f39db554..a6681b8cbb 100644 --- a/packages/xr/src/loader/XRReferenceImageLoader.ts +++ b/packages/xr/src/loader/XRReferenceImageLoader.ts @@ -5,7 +5,7 @@ import { XRReferenceImage } from "../feature/trackable/image/XRReferenceImage"; export class XRReferenceImageLoader extends Loader { load(item: LoadItem, resourceManager: ResourceManager): AssetPromise { return new AssetPromise((resolve, reject) => { - this.request(item.url, { ...item, type: "arraybuffer" }) + this.request(item.url, resourceManager, { ...item, type: "arraybuffer" }) .then((data) => { decode(data, resourceManager.engine).then((referenceImage) => { resolve(referenceImage);