# 资源加载

  引擎通常需要加载不同的资源文件，为了统一管理所有文件的加载和读取，我们在 `vjmap3d` 里封装了统一的资源管理器，可以方便用户加载，存储和读取各种文件资源。

  ## LoaderManager

  [LoaderManager](https://vjmap.com/map3d/api/classes/LoaderManager.html) 用于加载不同的资源。

  默认情况下，`LoaderManager`通过`资源文件路径类型后缀`来根据不同的加载器来加载资源。

  [LoadManager](https://vjmap.com/map3d/api/variables/LoadManager.html) 是`LoaderManager`的单例对象`。
### 默认加载器
|文本后缀|加载器|变量|
|:---|:---|:---|
|webp|ImageLoader|imageLoader|
|jpg|ImageLoader|imageLoader|
|png|ImageLoader|imageLoader|
|jpeg|ImageLoader|imageLoader|
|obj|OBJLoader|objLoader|
|mtl|MTLLoader|mtlLoader|
|mp4|VideoLoader|videoLoader|
|webm|VideoLoader|videoLoader|
|ogg|VideoLoader|videoLoader|
|hdr|HDRLoader|hdrLoader|
|gltf|GLTFLoader|gltfLoader|
|glb|GLTFLoader|gltfLoader|
|fbx|FBXLoader|fbxLoader|
|dds|DDSLoader|ddsLoader|
|svg|SVGLoader|svgLoader|
|particle|[ParticleLoader](https://vjmap.com/map3d/api/classes/ParticleLoader.html)|particleLoader|
|img|ImageLoader|imageLoader|
|json|FileLoader|fileLoader（返回原始文本，**不会自动 `JSON.parse`**）|
|ktx2|KTX2Loader|ktx2Loader|

::: tip 提示
 如需加载其他类型的资源，请使用[插件](../plugin/loader.md)进行加载 
:::

### 常用方法
|方法|描述|
|:---|:---|
|[setPath](https://vjmap.com/map3d/api/classes/LoaderManager.html#setPath)|设置统一资源路径前缀|
|[setRequestHeader](https://vjmap.com/map3d/api/classes/LoaderManager.html#setRequestHeader)|设置请求头|
|[setResponseType](https://vjmap.com/map3d/api/classes/LoaderManager.html#setResponseType)|设置响应类型|
|[getLoader](https://vjmap.com/map3d/api/classes/LoaderManager.html#getLoader)|获取加载器|
|[load](https://vjmap.com/map3d/api/classes/LoaderManager.html#load)|加载资源|
|[register](https://vjmap.com/map3d/api/classes/LoaderManager.html#register)|注册loader|
|[hasLoaded](https://vjmap.com/map3d/api/classes/LoaderManager.html#hasLoaded)|资源是否已经加载|
|[getResource](https://vjmap.com/map3d/api/classes/LoaderManager.html#getResource)|获取url的资源|
|[getResourceStatus](https://vjmap.com/map3d/api/classes/LoaderManager.html#getResourceStatus)|获取url的资源状态|
|[getLoadDetailMap](https://vjmap.com/map3d/api/classes/LoaderManager.html#getLoadDetailMap)|获取详细资源信息|
|[setLoaderPath](https://vjmap.com/map3d/api/classes/LoaderManager.html#setLoaderPath)|设置加载路径|
|[dispose](https://vjmap.com/map3d/api/classes/LoaderManager.html#dispose)|清空缓存|
|[loadExtensionLoader](https://vjmap.com/map3d/api/classes/LoaderManager.html#loadExtensionLoader)|加载扩展的loader库|
|[loadExtensionIfcLoader](https://vjmap.com/map3d/api/classes/LoaderManager.html#loadExtensionIfcLoader)|加载扩展的IfcLoader库|

> **坑位**：`register` 注册的自定义 loader 必须实现 `setPath(path)` 方法——`getLoader()` 命中后缀后内部会调 `loader.setPath(this.path)`，缺失该方法时 `getLoader` 直接抛 `xxx.setPath is not a function`（而不是返回 null）。未知后缀才会返回 `null`/`undefined`。

### 信号事件
|事件|描述|
|:---|:---|
|beforeLoad|开始加载前|
|loading|加载中|
|detailLoading|每项加载前|
|detailLoaded|每项加载完成|
|loaded|加载完成|

通过指定加载类型进行加载
```js
let gltf = vjmap3d.LoadManager.gltfLoader;
//let gltf = vjmap3d.LoadManager.getLoader("gltf");  // 或者也可以这么写
gltf.load(env.assetsPath + "models/soldier.glb", ( gltf ) => {
    const root = gltf.scene;
    app.scene.add( root );
})
```

直接传资源路径，自动加载
```js
vjmap3d.LoadManager.load(env.assetsPath + "models/soldier.glb", false,
    (resource) => {
    // onLoad
        console.log("onLoad", resource)
        app.scene.add(resource.scene)
    },
    (event) => {
    // onProgress
        console.log("onProgress", event)
    },
    (event) => {
    // onError
        console.error(event)
    },
    "glb" // fileType: 
)
```

<mapFrame>
<pre>
let svc = new vjmap3d.Service(env.serviceUrl, env.accessToken);
let app = new vjmap3d.App(svc, {
container: "map", // 容器id
    scene: {  // 场景设置
        gridHelper: { visible: true } // 是否显示坐标网格
    }
})
let gltf = vjmap3d.LoadManager.gltfLoader;
//let gltf = vjmap3d.LoadManager.getLoader("gltf");  // 或者也可以这么写
gltf.load(env.assetsPath + "models/soldier.glb", ( gltf ) => {
    const root = gltf.scene;
    app.scene.add( root );
})
</pre>
</mapFrame>

 ## ResourceManager

  [ResourceManager](https://vjmap.com/map3d/api/classes/ResourceManager.html) 用于资源加载管理，内部通过  [loaderManager](https://vjmap.com/map3d/api/classes/LoaderManager.html) 加载资源。

  ::: tip 提示
 因为  [ResourceManager](https://vjmap.com/map3d/api/classes/ResourceManager.html) 中包含了对 [loaderManager](https://vjmap.com/map3d/api/classes/LoaderManager.html)的操作，所以一般直接通过 `ResourceManager`来加载资源。
:::

  [ResManager](https://vjmap.com/map3d/api/variables/ResManager.html) 是`ResourceManager`的单例对象`。

  ### 常用方法
|方法|描述|
|:---|:---|
|[setServiceUrlPath](https://vjmap.com/map3d/api/classes/ResourceManager.html#setServiceUrlPath)|设置服务器资源数据|
|[svrUrl](https://vjmap.com/map3d/api/classes/ResourceManager.html#svrUrl)|根据名称获取服务器端资源路径|
|[loadRes](https://vjmap.com/map3d/api/classes/ResourceManager.html#loadRes)|获取资源|
|[loadTexture](https://vjmap.com/map3d/api/classes/ResourceManager.html#loadTexture)|获取材质(默认srgb空间)|
|[loadTextureSync](https://vjmap.com/map3d/api/classes/ResourceManager.html#loadTextureSync)|获取材质(默认srgb空间)返回一个promise|
|[loadTextureLinear](https://vjmap.com/map3d/api/classes/ResourceManager.html#loadTextureLinear)|获取材质(线性空间)|
|[loadTextureLinearSync](https://vjmap.com/map3d/api/classes/ResourceManager.html#loadTextureLinearSync)|获取材质(线性空间)返回一个promise|
|[loadModelRes](https://vjmap.com/map3d/api/classes/ResourceManager.html#loadModelRes)|加载模型资源文件|
|[addModel](https://vjmap.com/map3d/api/classes/ResourceManager.html#addModel)|增加模型资源对象|
|[loadModel](https://vjmap.com/map3d/api/classes/ResourceManager.html#loadModel)|加载模型url文件|
|[loadResources](https://vjmap.com/map3d/api/classes/ResourceManager.html#loadResources)|加载资源数组|
|[clearResourceCache](https://vjmap.com/map3d/api/classes/ResourceManager.html#clearResourceCache)|清除资源缓存|
|[loadExtensionLoader](https://vjmap.com/map3d/api/classes/ResourceManager.html#loadExtensionLoader)|加载扩展的loader库|
|[loadExtensionIfcLoader](https://vjmap.com/map3d/api/classes/ResourceManager.html#loadExtensionIfcLoader)|加载扩展的IfcLoader库|
|[getLoader](https://vjmap.com/map3d/api/classes/ResourceManager.html#getLoader)|获取加载器|

加载模型
```js
let svc = new vjmap3d.Service(env.serviceUrl, env.accessToken);
let app = new vjmap3d.App(svc, {
    container: "map", // 容器id
})
let ent = await vjmap3d.ResManager.loadModel(env.assetsPath + "models/soldier.glb", {
    splitSubEntity: true,
    anchor: "front-bottom-right"
});
ent.addTo(app);
```

<mapFrame>
<pre>
let svc = new vjmap3d.Service(env.serviceUrl, env.accessToken);
let app = new vjmap3d.App(svc, {
    container: "map", // 容器id
})
let ent = await vjmap3d.ResManager.loadModel(env.assetsPath + "models/soldier.glb", {
    splitSubEntity: true,
    anchor: "front-bottom-right"
});
ent.addTo(app);
</pre>
</mapFrame>

加载材质
```js
// 同步返回 Texture 对象（图像内容异步填充），无需 await
let texture = vjmap3d.ResManager.loadTexture(env.assetsPath + "image/sensor1.png")
// 返回 Promise<Texture>，await 得到加载完成的纹理
let texture2 = await vjmap3d.ResManager.loadTextureSync(env.assetsPath + "image/sensor1.png")
```

对于有复杂步骤的加载，如`obj`需要先加载`mtl`文件做为材质，可这样实现:
```js
// 如果是obj，需要先加载mtl文件
 let materials = await vjmap3d.ResManager.loadRes(env.assetsPath + "models/table/table.mtl", true);
 materials.preload();
 let loader = vjmap3d.LoadManager.getLoader("obj");
 // 设置材质
 loader.setMaterials(materials);
 let ent = await vjmap3d.ResManager.loadModel(env.assetsPath + "models/table/table.obj", {
    size: 5,
    splitSubEntity: true,
    useCache: true
});
ent.addTo(app);
//  取消设置材质
vjmap3d.LoadManager.getLoader("obj")?.setMaterials(null);
```

<mapFrame>
<pre>
let svc = new vjmap3d.Service(env.serviceUrl, env.accessToken);
let app = new vjmap3d.App(svc, {
container: "map", // 容器id
    scene: {  // 场景设置
        gridHelper: { visible: true } // 是否显示坐标网格
    }
})
 // 如果是obj，需要先加载mtl文件
 let materials = await vjmap3d.ResManager.loadRes(env.assetsPath + "models/table/table.mtl", true);
 materials.preload();
 let loader = vjmap3d.LoadManager.getLoader("obj");
 // 设置材质
 loader.setMaterials(materials);
 let ent = await vjmap3d.ResManager.loadModel(env.assetsPath + "models/table/table.obj", {
    size: 5,
    splitSubEntity: true,
    useCache: true
});
ent.addTo(app);
//  取消设置材质
vjmap3d.LoadManager.getLoader("obj")?.setMaterials(null);
</pre>
</mapFrame>

## 应用初始资源加载

对于一个应用，如果在应用初始的时候需要加载，并且有进度条显示，可使用 [loadResources](https://vjmap.com/map3d/api/classes/SceneModule.html#loadResources) 加载资源数组

```js

const resources = [
  {
    name: "sm_car",
    url: "mesh/sm_car.gltf",
    type: "model"
  },
  {
    name: "ut_car_body_ao",
    url: "texture/t_car_body_AO.raw.jpg",
    type: "texture"
  },
  {
    name: "skybox",
    type: "hdr",
    url: "./skybox_512px.hdr",
  },
  {
    name: "algorithm",
    type: "plugin",
    url: "algorithm",
  }
];
let allResources = await app.loadResources(resources, {
    showShadertoy: false,
    showHtml: true
});
// 根据名称获取资源
let car = allResources.getResource("sm_car");
car.addTo(app);
```

<mapFrame>
<pre>
let svc = new vjmap3d.Service(env.serviceUrl, env.accessToken);
let app = new vjmap3d.App(svc, {
container: "map", // 容器id
    scene: {  // 场景设置
        gridHelper: { visible: true } // 是否显示坐标网格
    }
})
const resources = [
    {
      name: "car",
      url: env.assetsPath + "models/car.glb",
      type: "model"
    },
    {
        name: "soldier",
        url: env.assetsPath + "models/soldier.glb",
        type: "model"
    },
    {
        name: "skybox",
        type: "hdr",
        url: env.assetsPath + "skybox/skybox_512px.hdr",
      },
  ];
  let allResources = await app.loadResources(resources, {
      showShadertoy: false,
      showHtml: true
  });
 // 设置背景和环境
 app.setBackgroundEnvironment({
    background: {
        texture: allResources.getResource("skybox")
    },
    environmentSameAsBackground: false
});
// 根据名称获取资源
let soldier = allResources.getResource("soldier");
soldier.addTo(app);
let car = allResources.getResource("car");
car.position.set(2, 0, 2)
car.addTo(app)
</pre>
</mapFrame>