Appearance
场景数据对接你的服务端
对接后端接口
这里只需要修改两处代码即可
@/views/sceneEdit/layouts/SceneHeader/index
找到 saveSceneIndexDb 这个方法
将保存至 indexedDB 中的方法改为后端接口即可
js
/**
* 保存场景到indexDb
*/
const saveSceneIndexDb = async () => {
try {
if (!store.sceneApi) {
throw new Error('场景未初始化');
}
loading.value = true;
exportProgress.value = 0;
// 清理之前的导出定时器
exportTimeouts.value.forEach((timeout) => clearTimeout(timeout));
exportTimeouts.value = [];
await new Promise((resolve) => setTimeout(resolve, 10));
loadingText.value = '准备场景数据...';
const camera = store.sceneApi.camera;
const controls = store.sceneApi.controls;
// 分离大型数据(与导出场景逻辑一致)
sceneJson =
store.sceneApi?.scene?.toJSON() as unknown as SerializedSceneData;
const { geometries, images, materials, ...remainingSceneData } = sceneJson;
// 构建场景数据对象
jsonData = {
controls: {
x: controls?.target?.x || 0,
y: controls?.target?.y || 0,
z: controls?.target?.z || 0,
},
scene: remainingSceneData,
camera: camera?.toJSON(),
weather:
toRaw(store.sceneApi?.weatherEffectsModules.weatherConfig) || null,
};
// 准备要压缩的文件列表(与导出场景逻辑一致)
files = [
{ name: 'scene.json', data: jsonData },
{ name: 'geometries.json', data: geometries },
{ name: 'images.json', data: images },
{ name: 'materials.json', data: materials },
];
// 使用Worker异步处理导出和压缩
zipBlob = await exportSceneWithWorker(
JSON.parse(JSON.stringify(files)),
// 进度回调
(progress: number, message: string) => {
console.log(`保存进度: ${progress}% - ${message}`);
loadingText.value = message;
exportProgress.value = progress;
},
// 完成回调
(blob: Blob) => {
loadingText.value = '压缩完成正在保存...';
},
// 错误回调
(error: string) => {
exportProgress.value = 0;
throw new Error('场景压缩失败: ' + error);
}
);
if (!zipBlob) {
throw new Error('场景压缩失败');
}
// 保存到 scene 存储
const sceneInfo = {
sceneBlobData: IndexDbStoreKeyPath.sceneBlobData,
zipData: zipBlob, // 直接使用Blob格式
zipSize: zipBlob.size,
zipType: 'application/zip',
timestamp: new Date().toISOString(),
};
const oldData = await indexDbStore.indexDbUtil?.get(
IndexDbStoreName.scene,
IndexDbStoreKeyPath.sceneBlobData
);
if (oldData) {
await indexDbStore.indexDbUtil?.update(IndexDbStoreName.scene, {
...oldData,
...jsonData,
});
} else {
await indexDbStore.indexDbUtil?.add(IndexDbStoreName.scene, sceneInfo);
}
// 后端接口
const res = await updateSceneData(sceneInfo)
disposeScene(newScene);
} catch (error) {
console.error('保存场景失败:', error);
return Promise.reject(error);
} finally {
return Promise.resolve();
}
};@/utils/renderScene.ts
在初始化场景时也会先获取历史数据,这里也需要换成后端接口
js
/**
* 初始化场景
* @returns Promise<boolean>
*/
init(): Promise<boolean> {
return new Promise(async (resolve, reject) => {
if (!this.container) {
reject(new Error('Container not found'));
return;
}
this.initCamera();
this.initRender();
await this.initScene();
// 获取indexDb场景数据
const loadSceneData = (await indexDbStore.indexDbUtil?.get(
IndexDbStoreName.scene,
IndexDbStoreKeyPath.sceneBlobData
)) as IndexDbSceneData;
// 通过后端接口获取
const loadSceneData = await getSceneDataInfo()
// 如果存在场景数据则加载场景数据
if (loadSceneData) {
await this.loadIndexDbSceneData(loadSceneData);//
} else {
// 如果不存在场景数据则初始化变换控制器
this.transformControlsModules.init();
}
await this.initPlaneGround(loadSceneData);
await this.initControls();
this.sceneAnimation();
this.addEvenListMouseListener();
this.onWindowResizes();
resolve(true);
});
}为什么是toJSON方式存储方式?
答:因为项目是一个场景编辑器,实现的是在一个场景中编辑多个模型,灯光,几何体,外部模型等,并且每个材质的可编辑属性值也非常多,如果采用传统的属性+属性值键值对的方式json的方式来存储。
那就意味着场景中每多一个内容和新增一个可编辑值我们都要在一个循环遍历方法中多执行一次 Three.js 模型数据修改的方法,这样的实现方式对于数据的管理和代码的维护都是灾难性的。
而使用 toJSON 则会将整个场景内容转化为 Three.js 特定的数据格式,我们只关心数据的存储和读取即可,这就意味着将减少我们很多的工作量,同时也会避免很多不必要性bug的发生率
toJSON 的方式文件体积会不会很大?
答:目前作者已经 优化处理 了保存时场景的数据大小,原 500M 大小的数据量已优化到 100M 这个比例基本上可以满足大模型和大场景内容的需求,所以无需担心