3D 资源一般与 Engine 挂钩,我们使用挂载在 Engine 实例中的 ResourceManager 管理与加载资源。
推荐用脚本组件的方式加载资源。load 方法即可传入 url,也可以传入 loadItem,也可以传入数组表示批量加载。
export class ResourceScript extends Script {
async onAwake() {
const gltf = await this.engine.resourceManager.load("test.gltf");
this.entity.addChild(gltf.defaultSceneRoot);
}
}
加载队列可传入一组 LoadItem 数组,或一组 url,返回结果是按顺序排列的加载好的资源队列。
const [texture2D, glTFResource] = await this.engine.resourceManager.load(["a.png", "b.gltf"]);
调用加载队列可以得到一个 AssetPromise 对象,可以使用 onProgress 获取加载进度。
this.engine.resourceManager.load(["a.png", "b.gltf"]).onProgress((progress: number)=>{
console.log(`当前加载进度为 ${progress}`);
})
ResourceManager
对象中有 cancelNotLoaded 方法,可以通过调用此方法取消未加载完成的资源。传入 url 会取消特定的 url 的资源加载。
// 取消所有未加载完的资源。
this.engine.resourceManager.cancelNotLoaded();
// 取消特定的 url 资源加载。
this.engine.resourceManager.cancelNotLoaded("test.gltf");
注意:目前取消加载未完成资源会抛出异常。
为了避免重复加载资源,当资源被加载完成之后,会被缓存在 ResourceManager
内。缓存本身会占用内存和显存,当开发者不再需要缓存的内容时,需要手动去释放缓存的内容。
注意:资源之间是相互依赖的。
例如下图展示的实体包含 MeshRenderer 组件,依赖于 Material, Material
可能被多个 MeshRenderer
引用,如果释放 Material
,那么引用此的其他 MeshRenderer
则会找不到该 Material
而报错。
注意:JavaScript 无法追踪对象的引用。 一般在 JavaScript 等弱类型语言中,是没有提供给开发者内存管理的功能的,所有对象的内存都是通过垃圾回收机制来管理,你没有办法去判断对象什么时候会被释放,所以没有析构函数(destructor)去调用引用资源的释放。
ResourceManager
提供了一套基于引用计数的资源释放,需要开发者手动调用 gc:
engine.resourceManager.gc();
更多纹理相关文档可查阅纹理资源。
const texture2D = await this.engine.resourceManager.load("test.png");
加载器会使用 png、jpg 等后缀作为判断是 Texture2D 的依据,若使用 cdn 地址不带后缀,需要使用 type 去指定加载类型。例如
this.engine.resourceManager.load({url: "test", type: AssetType.Texture2D});
更多纹理相关文档可查阅纹理资源。
const textureCube = await this.engine.resourceManager
.load<TextureCubeMap>({
urls: [
"/static/env/papermill/specular/specular_right_0.jpg",
"/static/env/papermill/specular/specular_left_0.jpg",
"/static/env/papermill/specular/specular_top_0.jpg",
"/static/env/papermill/specular/specular_bottom_0.jpg",
"/static/env/papermill/specular/specular_front_0.jpg",
"/static/env/papermill/specular/specular_back_0.jpg"
],
type: AssetType.TextureCube
})
TextureCubeMap 使用六张图片作为原始资源,用 urls 传递六张图片链接,type 使用 AssetType.TextureCube 。
更多压缩纹理相关文档可查阅压缩纹理。
const compressedTexture2D = await this.engine.resourceManager.load("test.ktx");
压缩纹理后缀一般为 ktx
,使用时需注意平台支持的压缩纹理格式。压缩纹理加载后得到的也是 Texture2D 。
压缩的立方体纹理的加载和一般的立方体纹理加载不一样,是单独的一个二进制文件路径,而不需要 6 张图片的文件路径,但是需要指定为类型为 AssetType.KTXCube。因为 ResourceManager 无法根据后缀识别需要使用哪种特定类型的 Loader。
const compressedTextureCube = await this.engine.resourceManager.load({url: "test.ktx", type: AssetType.KTXCube});
glTF 资源如上面例子,得到的是一个 GLTFResource 资源。加载成功后,会得到 glTF 里包含的 Scene、Entity、Texture、Material 和 AnimationClip。
用户也可以自定义加载器来加载自定义的资源:
@resourceLoader(FBX, ["fbx"])
export class FBXLoader extends Loader<FBXResource> {
load(item: LoadItem, resourceManager: ResourceManager): AssetPromise<FBXResource> {
return new AssetPromise((resolve, reject)=> {
...
})
}
}
- 通过 @resourceLoader 装饰器标注为 ResourceLoader,传入类型枚举和被解析的资源后缀名。上面的例子
FBX
是类型枚举,["fbx"]
是被解析资源的后缀名。 - 重写 load 方法,
load
方法会传入loadItem
和resourceManager
,loadItem
包含了加载的基信息,resourceManager
可以帮助加载其他引用资源。 - 返回 AssetPromise 对象,
resolve
解析后的资源结果,例如 FBX 返回特定的FBXResource
。 - 若报错则
reject
错误。