多级外轮廓与内部构件分离
- 示例描述与操作指南
- 应用场景
- 示例效果展示
- 实现步骤
- 第一步 提取模型外轮廓
- 第二步 获取多级外轮廓构件列表
- 第三步 构建组移动动画
示例描述与操作指南
多级外轮廓与内部构件分离示例,用于展示同一模型下不同外轮廓级别所提取出来的外轮廓构件集之间的差异,以及不同级别下哪些构件将被剔除。通过 提取模型外轮廓接口将输入 BIM 模型的建筑外轮廓提取出来后,调用获取模型外轮廓构件集接口获取对应级别外轮廓构件集列表。用户将完整代码下载下来后,将已经提取过所有级别外轮廓的模型 id 和用户个人账户 devcode 替换上去,即可展示自有模型。
应用场景
模型外轮廓主要应用于大场景、大建筑群加载需求中,例如在智慧园区、智慧城市的展示与应用中,大部分的场景加载都是用白模代替 BIM 模型,严重影响了美观与3D模拟的真实性,但是如果将全部模型加载至界面中,又会导致大量不可见构件的加载从而影响使用效率,建筑外轮廓构件集则解决了上述问题,既将模型轻量化展示,又可以满足大量建筑同时展示的需求。此外,不同级别的外轮廓构件集可以满足不同的展示需求。一级轮廓足以满足外部形态查看的基本需求,随着对模型展示外部精细度的需求增加,二级轮廓和三级轮廓会展示更加完整的构件集。
示例效果展示
实现步骤
第一步 提取模型外轮廓
通过调用接口,发起提取模型外轮廓的请求。具体接口,在页面下端相关接口中展示。
// 提取1级外轮廓构件
fetch(`${origin}/models/${fileKey}/outlines/!parse?level=1&enforce=true&devcode=${devcode}`);
// 提取2级外轮廓构件
fetch(`${origin}/models/${fileKey}/outlines/!parse?level=2&enforce=true&devcode=${devcode}`);
// 提取3级外轮廓构件
fetch(`${origin}/models/${fileKey}/outlines/!parse?level=3&enforce=true&devcode=${devcode}`);
第二步 获取多级外轮廓构件列表
通过多次调用提取模型外轮廓接口,将三级外轮廓提取出来。具体接口在页面下方相关接口中展示。
// 数据接口:获取模型外轮廓。获取该模型的三级外轮廓后,计算出2级比1级多的构件,3级比2级多的构件以及3级比1级多的构件
const getOutlineComponents = (id) => {
return getOutlineComponentByLevel(id, 1)
.then((result1) => {
outComponents1 = result1.data;
return getOutlineComponentByLevel(id, 2)
})
.then((result2) => {
outComponents2 = result2.data;
return getOutlineComponentByLevel(id, 3)
})
.then((result3) => {
outComponents3 = result3.data;
moreComponents2 = outComponents2.diff(outComponents1)
moreComponents3 = outComponents3.diff(outComponents1).diff(outComponents2)
moreComponents31 = outComponents3.diff(outComponents1)
})
}
// 根据层级获取模型外轮廓构件 数据接口
const getOutlineComponentByLevel = (id, level) => {
return fetch(`${op.host}/models/${id}/outlines?devcode=${devcode}&level=${level}`)
.then(response => response.json())
}
第三步 构建组移动动画
依赖 tweenjs,对外轮廓构件集和差异构件创建移动动画效果。
// 添加模型构件移动动画,依赖tweenjs做动画,项目地址 https://github.com/sole/tween.js
const addTween = (level) => {
viewer3D.flyTo(splitView); // 飞跃到模型分离视角
let posSrc = {pos: 1}; // 定义动画更新要改变的变量
let tobeMovedComponents = []; // 定义动画构件
forbiddenButtton(level); // 禁用按钮
const model = viewer3D.getViewerImpl().getModel(modelKey);
const components = model.getAllComponents();
componentsToOrigin(components, true); // 所有模型构件回到初始位置
// 每次更新需要调用 TWEEN.update函数
const animate = () => {
viewer3D.render();
TWEEN.update();
requestAnimationFrame(animate);
}
// tween对象更新的回调函数
const onUpdate = () => {
tobeMovedComponents.forEach(item => {
const matrixWorld = viewer3D.getViewerImpl().modelManager.getComponent(item)[0].mesh.matrixWorld;
const matrix = viewer3D.getViewerImpl().modelManager.getComponent(item)[0].mesh.matrix;
const positionWorld = new THREE.Vector3();
const position = new THREE.Vector3();
positionWorld.setFromMatrixPosition(matrixWorld);
position.setFromMatrixPosition(matrix);
positionWorld.y = position.y - 50000 * (1 - posSrc.pos);;
matrixWorld.setPosition(positionWorld);
});
};
// 实例化一个tween对象,设置它的参数posSrc在3秒内从1减到0
const tween = new TWEEN.Tween(posSrc)
.to({pos: 0}, 1000)
.onUpdate(onUpdate) // 更新时执行的函数
.easing(TWEEN.Easing.Sinusoidal.InOut) // posSrc变化的规律
.start(); // 开始执行此动画
switch (level) {
case 1:
mode1 = true;
tobeMovedComponents = outComponents1;
break;
case 2:
mode2 = true;
if (mode1) {
tobeMovedComponents = moreComponents2;
componentsToOrigin(outComponents1, false);
} else {
tobeMovedComponents = outComponents2;
}
break;
default:
if (mode2) {
tobeMovedComponents = moreComponents3;
componentsToOrigin(outComponents2, false);
} else if (mode1) {
tobeMovedComponents = moreComponents31;
componentsToOrigin(outComponents1, false);
} else {
tobeMovedComponents = outComponents3;
}
tween.onComplete(() => {
backToOrigin();
});
}
animate(); // 开始动画
}