资源

优诺科技-ThingJS官网
优诺科技-CampusBuilder下载
CampusBuilder使用手册
CampusBuilder使用教程(视频,快捷键,常见问题)
CampusBuilder文档中心
优诺科技-Thingjs在线开发工具
ThingJS入门教程
ThingJS API-在线开发参考手册
ThingJS离线开发包使用说明书
ThingJS离线部署包使用说明书

ThingJS开发3D可视化系统步骤

ThingJS - 图1

CampusBuilder(以下简称CB)-3D场景搭建工具

概述

使用CB搭建场景可以依托ThingJS提供的3D模型库,或者上传自定义模型。CB搭建的3D场景会自动同步到ThingJS平台同名账户下(可在在线开发工具-资源-我的园区中看到)

电脑配置要求

系统:Windows 7及以上
CPU:双核 CPU2.8GHz及以上
内存:1G及以上
显卡:最低GTX650,推荐GTX960及以上
硬盘:300GB及以上
3D模型创建对显卡有一定的要求,请务必使用独立显卡,集成显卡会造成使用的过程中有卡顿现象。

CampusBuilder 与 ThingJS的联系

在CB中创建的物体,只有在编辑了UserID、Name或者自定义属性之后,导入到ThingJS中才能成为独立管理的对象,被程序读取或修改。并且CB中UserID和Name与ThingJS中的对象有对应关系。

数据对接

ajax

在ThingJS的在线开发环境中内置了JQuery库,可以直接使用JQuery封装的Ajax方法进行数据对接。
注:如果在ThingJS在线环境中请求用户自己服务器上的静态资源数据或数据服务,或者其他网站的数据服务,就需要解决跨域问题(服务端需要配置response响应头的Access-Control-Allow-Origin属性)。如果是访问用户上传到ThingJS网站的静态JSON数据资源则不存在跨域问题。

集成到React项目中(iframe引入)

使用ThingJS进行离线开发,需要按照 ThingJS离线开发包使用说明书 安装离线开发包,并在插入秘钥U盘的情况下,在CB客户端进行ThingJS第三方调试。
image.png
image.png

实现一个三维停车场场景

效果展示

车辆管理.png

第一步:使用CampusBuilder导入参考图(最好是CAD平面图),设置比例和中心点。

利用ThingJS提供的模型,搭建停车场场景,包括但不限于墙,地面,停车位,减速带,警示牌,闸机,根据实际情况调整各个模型的属性。场景搭建的差不多了之后,要给需要程序读取或控制的模型加上UserID,比如在停车场这个场景里需要程序读取或控制的是一个个停车位,所以需要给每个停车位的UserID属性都输入值。

第二步:在ThingJS在线开发工具中创建自己的项目。

  1. 创建App对象并加载场景

    1. var app = new THING.App({
    2. // 场景地址
    3. "url": "/api/scene/f2837fb2825733ec2f34aaa9",
    4. //背景设置
    5. "skyBox" : "BlueSky"
    6. });
  2. 动态创建车辆(在指定的停车位渲染指定的车) ```javascript // 停车位假数据示例 var spaceData = [ {

    1. id: 'parkingSpace01', // 这个id需要跟CampusBuilder中的UserID一致
    2. carModelUrl: '/api/models/A2425FF719264EA8A74440115640184A/0/gltf/' // 模型地址

    }, {

    1. id: 'parkingSpace11',
    2. carModelUrl: '/api/models/54728749A90F43CC874CE869B5028F65/0/gltf/'

    }, {

    1. id: 'parkingSpace16',
    2. carModelUrl: '/api/models/91FACB0F809240539A0143BC468A82ED/0/gltf/'

    } ];

function renderCars (spaceData) { var parkingSpace = null; var car = null; spaceData.forEach((item)=>{ if (item.carModelUrl.length > 0) { // 获取停车位对象 parkingSpace = app.query(#${item.id}); // 创建车辆对象 car = app.create({ type: ‘Thing’, // 对象类型 url: item.carModelUrl, // 模型地址 position: parkingSpace[0].position, // 位置 angles: parkingSpace[0].angles, // 缩放 scale: parkingSpace[0].scale, // 旋转角度 complete: function () { console.log(‘thing created: ‘ + this.id); } }); } }); };

// 场景加载事件 app.on(‘load’, function(){ // 加载场景之后执行 renderCars(spaceData); });

  1. 3. 渲染小车沿路径移动动画
  2. 实现效果:小车沿着固定路径循环移动。路径坐标点数组和模型position数组可以使用 ThingJS在线开发-工具-拾取坐标工具,在场景中使用鼠标点击获取坐标数据。
  3. ```javascript
  4. function renderMovingCar () {
  5. var moveCar = app.create({
  6. type: 'Thing',
  7. url: '/api/models/D12DF3998C9149D5A9B44652AB78BD99/0/gltf/',
  8. position: [6.63, 0, 2.051],
  9. scale:[0.184,0.461,0.22], // 这里的缩放数据是在CB里面查看的停车位的缩放数据,可根据实际情况调整
  10. complete: function () {
  11. // console.log('thing created: ' + this.id);
  12. var path = [[ 6.93, 0, 2.08], [ -4.743, 0.1, 1.971],[ -4.407, 0.01, -7.752]];
  13. moveCar.movePath({
  14. orientToPath: true, // 物体移动时沿向路径方向
  15. path: path, // 路径坐标点数组
  16. time: 15 * 1000, // 路径总时间 毫秒
  17. delayTime: 500, // 延时 0.5s 执行
  18. lerpType: null, // 插值类型(默认为线性插值)此处设置为不插值
  19. loopType: 'repeat', // 循环类型 'no':不循环 'repeat': 循环 'pingpong': '来回不断循环'
  20. });
  21. }
  22. });
  23. };
  1. 渲染闸机杆抬起放下动画

实现效果:小车进入停车场前,入口闸机杆抬起,进入后入口闸机杆落下;小车离开前,出口闸机杆抬起,离开后出口闸机杆落下。特别说明:由于ThingJS提供的闸机杆模型的轴心点在中间,旋转的时候是绕着轴心点去旋转,如果旋转角度过大(比如90度),闸机杆与闸机就脱离了,所以在这我设置的旋转角度为20度。如果想要修改模型的轴心点,需要在3DMax里面去修改模型。
image.png

  1. function renderMovingCar () {
  2. var turnstile01 = app.query('#turnstile01'); // 在CB中给闸机杆编写UserID,使用query获取到闸机杆对象
  3. var turnstile02 = app.query('#turnstile02');
  4. var moveCar = app.create({
  5. type: 'Thing',
  6. url: '/api/models/D12DF3998C9149D5A9B44652AB78BD99/0/gltf/',
  7. position: [6.63, 0, 2.051],
  8. scale:[0.184,0.461,0.22],
  9. complete: function () {
  10. // console.log('thing created: ' + this.id);
  11. var path = [[ 6.93, 0, 2.08], [ -4.743, 0.1, 1.971],[ -4.407, 0.01, -7.752]];
  12. moveCar.movePath({
  13. orientToPath: true, // 物体移动时沿向路径方向
  14. path: path, // 路径坐标点数组
  15. time: 15 * 1000, // 路径总时间 毫秒
  16. delayTime: 500, // 延时 0.5s 执行
  17. lerpType: null, // 插值类型(默认为线性插值)此处设置为不插值
  18. loopType: 'repeat', // 循环类型
  19. update: function(e) {
  20. // 监听小车的移动过程(移动数据为0-1的浮点数)
  21. var numStr = e.progress.toString();
  22. var index = numStr.indexOf('.');
  23. // 在这里处理移动数据时,需要注意力度,如果截取的小数点后的位数过大的话,有可能监听不到,亲测截取小数点后两位是刚刚好的。如果截取小数点后三位,在场景初始加载时有很大概率监听不到。
  24. var result = numStr.slice(0, index + 3);
  25. if (Number(result) === 0) {
  26. // console.log(e.progress + `----` + result);
  27. turnstile01[0].rotateTo({
  28. // 这个角度需要在CB中多尝试,如果对坐标系的概念比较模糊的话
  29. angles: [0, 90, 20],
  30. time: 2000,
  31. complete: function () {
  32. turnstile01[0].rotateTo({
  33. angles: [0, 90, 0],
  34. time: 1000,
  35. complete: function () {
  36. // console.log('rotateto completed');
  37. }
  38. });
  39. }
  40. });
  41. }
  42. if (Number(result) === 0.7) {
  43. // console.log(e.progress + `----` + result);
  44. turnstile02[0].rotateTo({
  45. angles: [0, 0, 20],
  46. time: 5000,
  47. complete: function () {
  48. turnstile02[0].rotateTo({
  49. angles: [0, 0, 0],
  50. time: 1000,
  51. complete: function () {
  52. // console.log('rotateto completed');
  53. }
  54. });
  55. }
  56. });
  57. }
  58. }
  59. });
  60. }
  61. });
  62. };
  63. // 加载场景之后执行
  64. app.on('load', function(){
  65. renderCars(spaceData);
  66. renderMovingCar();
  67. });

第三步:对接数据

使用感受

  • CB工具使用起来还不是特别的顺手,尤其是建立墙体的时候,有些拐弯的地方,不好控制,把握不好的话,两面墙之间会有窟窿
  • 如果程序需要读取的物体比较多,比如说停车场的停车位比较多的话,在CB里面去编写UserID是很费时间的,因为要一个一个地去编写UserID
  • 目前搭建场景时使用的模型都是ThingJS官方提供的,如果遇到需要修改模型的问题,比如说停车场场景里的闸机杆的轴心点需要改,我们是没有办法改的,所以如果没有建模相关的专业人才的话,效果的实现还是有一些受限的

image.png

  • 如果想要把ThingJS搭建的场景集成到现有的数字孪生系统中,不管是在线还是离线,都需要使用Iframe引入的方式进行集成。如果没有正式部署,使用iframe引入场景背景中是有”Thingjs by uinnova”水印的。