Skip to content

Commit

Permalink
Add loader for Shader and ShaderChunk asset (#2027)
Browse files Browse the repository at this point in the history
* feat: add loader for shader asset
  • Loading branch information
Sway007 authored Nov 6, 2024
1 parent 808b603 commit a831b41
Show file tree
Hide file tree
Showing 37 changed files with 292 additions and 181 deletions.
6 changes: 6 additions & 0 deletions packages/core/src/Utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
2 changes: 2 additions & 0 deletions packages/core/src/asset/AssetType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ export enum AssetType {
TextureCube = "TextureCube",
/** Material. */
Material = "Material",
/** Shader. */
Shader = "Shader",
/** Mesh. */
Mesh = "Mesh",
/** AnimationClip. */
Expand Down
5 changes: 4 additions & 1 deletion packages/core/src/asset/Loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,8 @@ export abstract class Loader<T> {
constructor(public readonly useCache: boolean) {}
initialize?(engine: Engine, configuration: EngineConfiguration): Promise<void>;
abstract load(item: LoadItem, resourceManager: ResourceManager): AssetPromise<T>;
request: <U>(url: string, config: RequestConfig) => AssetPromise<U> = request;
request<U>(url: string, resourceManager: ResourceManager, config: RequestConfig): AssetPromise<U> {
const remoteUrl = resourceManager._virtualPathMap[url] ?? url;
return request(remoteUrl, config);
}
}
80 changes: 47 additions & 33 deletions packages/core/src/asset/ResourceManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,12 +183,14 @@ export class ResourceManager {
* @internal
*/
_onSubAssetSuccess<T>(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
};
}
Expand Down Expand Up @@ -326,10 +328,7 @@ export class ResourceManager {

private _loadSingleItem<T>(itemOrURL: LoadItem | string): AssetPromise<T> {
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);
Expand All @@ -338,27 +337,30 @@ 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);
});
}

// 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
Expand All @@ -381,47 +383,53 @@ 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<T>(assetBaseURL, assetURL, queryPath);
return this._createSubAssetPromiseCallback<T>(remoteAssetBaseURL, remoteAssetURL, queryPath);
}

return this._loadMainAsset(loader, item, assetBaseURL);
return this._loadMainAsset(loader, item, remoteAssetBaseURL, assetBaseURL);
}

private _loadMainAsset<T>(loader: Loader<T>, item: LoadItem, assetBaseURL: string): AssetPromise<T> {
private _loadMainAsset<T>(
loader: Loader<T>,
item: LoadItem,
remoteAssetBaseURL: string,
assetBaseURL: string
): AssetPromise<T> {
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);
}
);

return promise;
}

private _createSubAssetPromiseCallback<T>(
assetBaseURL: string,
assetURL: string,
remoteAssetBaseURL: string,
remoteAssetURL: string,
assetSubPath: string
): AssetPromise<T> {
const loadingPromises = this._loadingPromises;
const subPromiseCallback = this._subAssetPromiseCallbacks[assetBaseURL]?.[assetSubPath];
const subPromiseCallback = this._subAssetPromiseCallbacks[remoteAssetBaseURL]?.[assetSubPath];
const resolvedValue = subPromiseCallback?.resolvedValue;
const rejectedValue = subPromiseCallback?.rejectedValue;

Expand All @@ -438,19 +446,19 @@ export class ResourceManager {

// Pending
const promise = new AssetPromise<T>((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;
Expand Down Expand Up @@ -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<any>({
url,
type: this._editorResourceConfig[refId].type
type: resourceConfig.type
});
}
return promise.then((item) => (isClone ? item.clone() : item));
Expand Down
2 changes: 1 addition & 1 deletion packages/loader/src/AnimationClipLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { decode } from "./resource-deserialize";
class AnimationClipLoader extends Loader<AnimationClip> {
load(item: LoadItem, resourceManager: ResourceManager): AssetPromise<AnimationClip> {
return new AssetPromise((resolve, reject) => {
this.request<any>(item.url, {
this.request<any>(item.url, resourceManager, {
...item,
type: "arraybuffer"
})
Expand Down
2 changes: 1 addition & 1 deletion packages/loader/src/AnimatorControllerLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
class AnimatorControllerLoader extends Loader<AnimatorController> {
load(item: LoadItem, resourceManager: ResourceManager): AssetPromise<AnimatorController> {
return new AssetPromise((resolve, reject) => {
this.request<any>(item.url, {
this.request<any>(item.url, resourceManager, {
...item,
type: "json"
})
Expand Down
6 changes: 3 additions & 3 deletions packages/loader/src/BufferLoader.ts
Original file line number Diff line number Diff line change
@@ -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<ArrayBuffer> {
load(item: LoadItem): AssetPromise<ArrayBuffer> {
load(item: LoadItem, resourceManager: ResourceManager): AssetPromise<ArrayBuffer> {
const url = item.url;
if (isBase64(url)) {
return new AssetPromise((resolve) => {
Expand All @@ -14,7 +14,7 @@ class BufferLoader extends Loader<ArrayBuffer> {
resolve(result.buffer);
});
}
return this.request(url, {
return this.request(url, resourceManager, {
...item,
type: "arraybuffer"
});
Expand Down
2 changes: 1 addition & 1 deletion packages/loader/src/EnvLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { SphericalHarmonics3 } from "@galacean/engine-math";
class EnvLoader extends Loader<AmbientLight> {
load(item: LoadItem, resourceManager: ResourceManager): AssetPromise<AmbientLight> {
return new AssetPromise((resolve, reject) => {
this.request<ArrayBuffer>(item.url, { ...item, type: "arraybuffer" })
this.request<ArrayBuffer>(item.url, resourceManager, { ...item, type: "arraybuffer" })
.then((arraybuffer) => {
const shArray = new Float32Array(arraybuffer, 0, 27);
const shByteLength = 27 * 4;
Expand Down
2 changes: 1 addition & 1 deletion packages/loader/src/FontLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
class FontLoader extends Loader<Font> {
load(item: LoadItem, resourceManager: ResourceManager): AssetPromise<Font> {
return new AssetPromise((resolve, reject) => {
this.request<any>(item.url, { ...item, type: "json" })
this.request<any>(item.url, resourceManager, { ...item, type: "json" })
.then((data) => {
const { fontName, fontUrl } = data;

Expand Down
3 changes: 1 addition & 2 deletions packages/loader/src/GLTFLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,8 @@ export class GLTFLoader extends Loader<GLTFResource> {
}

override load(item: LoadItem, resourceManager: ResourceManager): AssetPromise<GLTFResource> {
const url = item.url;
const params = <GLTFParams>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
Expand Down
2 changes: 1 addition & 1 deletion packages/loader/src/HDRLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,7 @@ class HDRLoader extends Loader<TextureCube> {
return new AssetPromise((resolve, reject) => {
const engine = resourceManager.engine;

this.request<ArrayBuffer>(item.url, { ...item, type: "arraybuffer" })
this.request<ArrayBuffer>(item.url, resourceManager, { ...item, type: "arraybuffer" })
.then((buffer) => {
const uint8Array = new Uint8Array(buffer);
const { width, height, dataPosition } = HDRLoader._parseHeader(uint8Array);
Expand Down
6 changes: 3 additions & 3 deletions packages/loader/src/JSONLoader.ts
Original file line number Diff line number Diff line change
@@ -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<string> {
load(item: LoadItem): AssetPromise<string> {
return this.request(item.url, {
load(item: LoadItem, resourceManager: ResourceManager): AssetPromise<string> {
return this.request(item.url, resourceManager, {
...item,
type: "json"
});
Expand Down
2 changes: 1 addition & 1 deletion packages/loader/src/KTXCubeLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class KTXCubeLoader extends Loader<TextureCube> {
return new AssetPromise((resolve, reject) => {
Promise.all(
item.urls.map((url) =>
this.request<ArrayBuffer>(url, {
this.request<ArrayBuffer>(url, resourceManager, {
...item,
type: "arraybuffer"
})
Expand Down
2 changes: 1 addition & 1 deletion packages/loader/src/KTXLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { parseSingleKTX } from "./compressed-texture";
export class KTXLoader extends Loader<Texture2D> {
load(item: LoadItem, resourceManager: ResourceManager): AssetPromise<Texture2D> {
return new AssetPromise((resolve, reject) => {
this.request<ArrayBuffer>(item.url, {
this.request<ArrayBuffer>(item.url, resourceManager, {
...item,
type: "arraybuffer"
})
Expand Down
Loading

0 comments on commit a831b41

Please sign in to comment.