内容简介:最近和一家公司在谈一个项目合作,他们公司主要是做油田相关设备的,比如油罐车、压力车、泵车等。我的印象中只要和石油相关的企业,就感觉和钱挨得好近,:smile: 。他们老板看了我们公司的三维产品后,大为赞叹。 惊呼,我们油田的管理最好也能上一套这样的三维系统。
最近和一家公司在谈一个项目合作,他们公司主要是做油田相关设备的,比如油罐车、压力车、泵车等。
我的印象中只要和石油相关的企业,就感觉和钱挨得好近,:smile: 。
他们老板看了我们公司的三维产品后,大为赞叹。 惊呼,我们油田的管理最好也能上一套这样的三维系统。
油田行业的三维可视化项目,我们之前没有做过相关的行业,但是在三维可视化方面,我们经验还是挺多的,比如数据中心、医院、学校等三维可视化项目,还包括智慧园区、智慧城市、智慧小镇的方向的等三维可视化。
下面先上几张三维可视化的图瞅瞅:
虽然我们没有直接做过油田的三维可视化,但是有了以上三维方案的技术积累,这事做起来就不会太难。
其实客户的需求,并不是就某个油田场景进行三维可视化的场景搭建。而是要做一个油田三维的布局工具,通过布局工具,可以自由搭建不同的油田场景。
这比直接搭建一个三维的场景要难许多。
所谓万事开头难,难在不开头。 天下事有难易乎,干就完了。
在商务人员和客户确立合同,正式立项后, 我们的设计小姐姐,开发小哥哥,都各司其职,下边就讲一下项目的大概内容。
搭建模型库
第一步要做的就是建模,设计组使用3D建模 工具 3d max或者c4d 进行油田设备模型的建模。建模后,导出后缀为obj或者gltf格式文件,这两种格式是我们三维渲染引擎支持的最好的文件格式。
建模后的所有模型文件,最终会放到后端的模型库,模型库的管理目录如下图所示:
加载模型
加载模型可使用引擎模型的加载函数进行模型加载,比如obj模型加载,示例代码如下:
new mono.OBJMTLLoader().load( 'yaliche.obj', 'yaliche.mtl', '', (node)=> { node.type = 'obj'; box.addByDescendant(node); }, );
上面加载了一个压力车的模型,加载模型是一个异步的过程,所以会有一个回调函数,加载完成之后,在回调函数中,把模型文件生成的三维对象加入到场景容器box之中,加入之后,场景中就会显示我们的三维对象,如下图所示:
搭建编辑器框架
在和设计组、开发组一起探讨之后,我们编辑器的框架和视图初步设计出来了,大致样子如下:
视图左上角是我们的logo,上方是工具栏。左侧分为场景区和组件区,场景区是创建三维场景的列表,组件区主要是模型列表,同时还有些echarts图表组件。
中间部分是三维场景呈现区。
对于这个页面布局,我想不用做太多技术上的阐述,基本上会一点前端开发的人员都可以实现类似的效果。
<div> <div id="leftTreeWrap"> <div> <div id="leftTree"> <div> <div> 场景 </div> </div> <div> 组件 </div> <div id="modelGroupWrap"> <!-- <div id="groundWrap"> <h2><span>场景模型</span></h2> <div id="groundTree"> <div></div> </div> </div> --> </div> </div> </div> </div> <div> <div> <div></div> </div> <div tabindex="0"> <canvas id="monoCanvas"></canvas> </div> </div>
左侧边栏包括两个部分,一个是场景列表,一个是模型列表。场景列表是树组件,模型列表是手风琴组件,如下图所示:
模型列表的创建过程是这样的,首先从后端获取所有的模型:
getComponentTree({ params: { owner: user } }, '同步云端组件树失败').then((res) => { if (res) { const treeData = res.data.data; treeData.forEach(({ data }) => { this.appendModelBtn(data, true); }); // this.renderTreeDom(res.data.data); } });
通过每个模型创建模型对于的button,函数是appendModelBtn,如下:
appendModelBtn(modelData, isNew) { const domWrap = this.groupDom[modelData.group]; if (!domWrap) { console.log('缺少该类型对应的组', modelData.group); if (modelData.category === 'skyBox') { modelData.isNew = true; skyData.push({ modelData }); } } else { domWrap.querySelector('.tree-wrap').appendChild(this.createModelBtnDom(modelData, isNew)); } }
需要注意的,每个模型按钮都需要有drag and drop的功能。在模型按钮上需要监听drag 或者dragstart事件,这个被封装到一个独立的类Dragger.js里面,在该类中专门处理了dragstart事件:
addDragger(parent, subClass, option) { parent.addEventListener('dragstart', (e) => { let target = null; // 拿到冒泡的所有元素 const path = eventPath(e); for (let i = 0; i < path.length; i += 1) { if (path[i].classList && path[i].classList.contains(subClass)) { target = path[i]; break; } } ... }
中间区域是三维呈现区域。 首先创建一个Network3D对象,Network3D对象是封装的三维呈现页面,其底层是由canvas组成的,并使用webgl技术进行三维渲染。下面是创建Network3D的代码:
const network = new mono.Network3D(box, null, 'monoCanvas'); network.mode = 'editor'; window.network = network; // todo this.network = network; network.bindApp(this); network.setRenderSelectFunction(() => false); make.Default.path = './static/myModellib/'; network.setClearColor(0, 0, 0); network.setClearAlpha(0);
创建对象之后,让network可以和中间区域的大小自适应:
mono.Utils.autoAdjustNetworkBounds( network, document.querySelector('.app'), 'clientWidth', 'clientHeight', );
其中network上的box对象用于管理要加载的三维对象模型。前面说过在模型列表上增加了drag事件,模型列表上的模型,通过拖拽可以添加到network对象上去,因此在network上面也需要添加对应的事件来添加对象:
onup: (e) => { if (!this.sceneTree.senceId && !window.debug) { layui.layer.msg('请先创建或选择场景', { time: 2000, }); return; } // 鼠标不在画布内的时候不创建 if (isPosInCanvas(network, e)) { network.createElement({ e, configString, senceId: this.sceneTree.senceId, }); } },
当模型从左侧模型列表拖拽到network对象后,鼠标mouseup事件后,创建模型实例:
network.createElement({ e, configString, senceId: this.sceneTree.senceId, });
到目前为止,已经完成了整个模型列表加载,模型拖拽创建模型实例的过程。 比如最终通过拖拽的油田场景如下所示:
在3d场景中,需要调整三维模型的位置、旋转角度和缩放比例,可以通过属性面板来调整:
也可以通过三维编辑功能直接在三维场景中对模型进行调整标记,要使用调整编辑功能,只需要加入如下这行代码即可:
const editInteraction = new mono.EditInteraction(network); editInteraction.setScaleable(false); editInteraction.setRotateable(false); editInteraction.setTranslateable(true); editInteraction.setDefaultMode(''); network.setInteractions([...network.getInteractions(), editInteraction]);
EditInteraction类 用于调整模型的位置、旋转角度和缩放比例。 通过键盘可以调整EditInteraction当前要调整的是那个属性:
case 82: // r 在有选中element时,切换操作为旋转 if (this.network.getIsMonoElement(this.network.currComponent)) { const editInteraction = this.network.getInteractions()[2]; editInteraction.setScaleable(false); editInteraction.setRotateable(true); editInteraction.setTranslateable(false); } break; case 84: // t 在有选中element时,切换操作为移动 if (this.network.getIsMonoElement(this.network.currComponent)) { const editInteraction = this.network.getInteractions()[2]; editInteraction.setScaleable(false); editInteraction.setRotateable(false); editInteraction.setTranslateable(true); } break; case 89: // y 在有选中element时,切换操作为缩放 if (this.network.getIsMonoElement(this.network.currComponent)) { const editInteraction = this.network.getInteractions()[2]; editInteraction.setScaleable(true); editInteraction.setRotateable(false); editInteraction.setTranslateable(false); } break;
r键切换为旋转角度的调整:
t键切换为位置的调整:
y键切换为缩放的调整:
拖拽创造场景之后,每个对象还可以进行实时数据的对接,对接后呈现的效果如下:
在完成场景的创建和数据的对接之后,便可以发布场景,点击工具栏的预览按钮,即可以完成场景的发布和预览。上一张最终发布的效果图如下:
有兴趣获取编辑器的,请发邮件到:
terry.tan@servasoft.com
欢迎关注公众号“ITman彪叔”。彪叔,拥有10多年开发经验,现任公司系统架构师、技术总监、技术培训师、职业规划师。在计算机图形学、WebGL、前端可视化方面有深入研究。对程序员思维能力训练和培训、 程序员 职业规划有浓厚兴趣。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- “智慧油田”导入“极简”基因 华北油田网络准入认证“焕新生”
- 遇见大数据可视化:来做一个数据可视化报表
- 遇见大数据可视化: 未来已来,变革中的数据可视化
- Python 数据可视化 2018:数据可视化库为什么这么多?
- 数据可视化设计(1)情感化设计指导可视化设计理念
- 基于WebGL架构的ThingJS可视化平台—城市地下管线3D可视化
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
复杂网络理论及其应用
汪小帆、李翔、陈关荣 / 清华大学出版社 / 2006 / 45.00元
国内首部复杂网络专著 【图书目录】 第1章 引论 1.1 引言 1.2 复杂网络研究简史 1.3 基本概念 1.4 本书内容简介 参考文献 第2章 网络拓扑基本模型及其性质 2.1 引言 2.2 规则网络 2.3 随机图 2.4 小世界网络模型 2.5 无标度网络模型 ......一起来看看 《复杂网络理论及其应用》 这本书的介绍吧!