system: [SYSTEM]

user: 3. 3D场景设计操作的实现 为实现用户Web 3D场景设计解决方案，为用户提供涵盖网页端的三维模型展示、设计场景交互、三维零件模型编辑等功能的在线网页端产品设计，下面分别从3D交互场景的构建、零件库的加载、零件模型的添加与编辑、设计过程支持基本操作等方面介绍3D场景设计的实现工作。 3.1. 3D交互场景构建 3D交互场景为参与设计的用户提供一个在浏览器中三维实时展示以及场景中进行交互设计的环境。图2给出了本文3D交互场景的构建工作流程。首先，在网页端创建场景，并构建了支持场景中三维模型展示功能的基本组件，包括：相机、底部参照网格、光源、显示渲染器等组件的添加；其次，在基本组件的基础上，构建了支持场景中模型的鼠标选中交互、场景中物体的360度查看和模型大小、位置、旋转角度的编辑功能；最后，开启线程，实时监测场景状态变化并实时进行场景中组件的渲染显示。 实现中运用javascript网页端编程借助Three.js框架提供的类库展开对Web 3D交互场景的构建工作，其中，Three.js框架完成了WebGL (Web Graphics Library) [ 9 ] [ 10 ] 的封装，其中场景中组件的添加借助场景对象的add()方法完成。 交互场景构建工作的具体实现流程如下： 第一步：场景与场景的内部构件创建 1) 创建初始化场景，使用Three.js类库的THREE.Scene()方法创建初始化场景对象scene； 图2. 场景构建流程图 2) 相机的添加，使用类库的THREE.PerspectiveCamera()方法创建场景中的远景相机，指定场景查看角度，设置相机摆放位置position，并通过lookAt()函数设置函数观察点，最后将其添加到1)场景对象中，即完成场景中模型的添加； 3) 参照网络的添加，使用类库的THREE.GridHelper()方法创建场景网格，通过传递参数设置网格的尺寸、两条线的间隔、中心线条以及其他非中心线条的颜色，并设置网格位置，本次设置场景的中心位置坐标(0，0，0)的位置，最后将其添加到场景中。 第二步：场景中的光源的设置 1) 环境光的添加，通类库的THREE.AmbientLight(0x444444)方法创建环境光对象，其中参数为实例化好的THREE.AmbientLight对象颜色(占位符1)，本次采用灰色环境光，创建完后将环境光添加到场景中； 2) 平行光的添加，通过类库的THREE.DirectionLight(0xffeed)创建平行光对象，其中参数为平行光颜色，并设置平行光方位，调用平行光对象的set(20，20，20).normalize()进行设置，最后将平行光对象添加到场景中。 第三步：场景的渲染显示 场景的渲染在整个交互场景的构建工作中有重要的作用，决定了场景中组件以及模型的显示。使用类库的THREE.WebGLRenderer()创建渲染器对象，使用render()方法设定参数场景对象scene和相机对象camera，对场景中的构件进行渲染显示，其后设置渲染清除颜色、设置场景显示大小、场景中物体是否排序等特性，最后将渲染器中的所有元素renderer.domElement添加到页面位置进行显示，即为页面显示的场景。 第四步：设计场景用户交互事件的添加 1) 场景中物体的360度查看，查看角度随鼠标的拖拽而改变。通过THREE.OrbitControls(camera)方法进行旋转控制设置，为场景相机对象添加旋转属性，相机的查看视角可随鼠标的拖动而改变。 2) 添加零件模型的编辑事件，通过THREE.TransformControls()方法创建编辑对象，设置参数camera、renderer.domElement为场景中渲染的物体添加编辑事件，设有translate位置、rotate旋转、scale大小编辑模式，场景中的物体在该三种模式下，会根据鼠标在固定方位上的拖拽进行对应编辑，默认为translate位置模式。 3) 添加场景中物体点击捕获事件，首先为渲染器设置mousemove事件和mousedown事件。其中mousemove是事件设置鼠标捕获物体图标为手型(正常情况下为箭头)；mousedown事件通过transformControls编辑对象的attach()方法设置鼠标选中物体，选中的物体可进行场景中设计提供的所有编辑操作，通过transformControls.object获取选中物体。 其中，鼠标选中物体需要解决鼠标从二维坐标空间到三维极坐标空间的转变问题，引入类库的THREE.Raycaster()方法定义投射对象，使用类库的THREE.Vector3()设置鼠标投射向量对象，通过调用投射对象的setFromCamera(vector， camera)方法根据相机以及投射鼠标向量设置投射参数，并通过intersectObjects(objects， true)找寻投射角度与鼠标的交叉点物体，即为鼠标捕获场景中物体。 4) 添加键盘快捷键事件，为浏览器窗体对象添加键盘的“keydown”事件，并通过对应case选项，对场景中transformControls编辑对象通过setMode方法更改焦点物体的编辑模式，其中，“W”、“E”、“R”键分别对应translate位置、rotate旋转、scale大小模式。 最后，建立线程实时对场景进行渲染 完成上述交互场景的创建后，调用animate()方法对场景中的编辑操作实时显示。该方法的主要工作有：调用requestAnimationFrame方法，设置线程循环执行场景的渲染工作，包括对场景中相机的旋转角度的空间的更新、编辑物体的更新，通过调用update()函数实现，并使用renderer对象的render(scene， camera)方法进行场景中所有组件的渲染。 3.2. 产品零件库的加载 参与设计人员进入设计界面，设计界面自动进行零件库的加载显示，供用户选择。在进行协作设计前，需平台后台或生产厂商进行零件库的维护。 1) 零件的存储 零件的存储分两部分：a) 零件模型、零件简图(为在页面显示做准备)存入服务器文件区，其中零件模型包括.obj文件以及其对应的默认.mtl材质文件；b) 零件记录，如零件名称、模型名称、简图名称、零件类型等信息存入数据库中，数据库中的表数据记录跟文件区存储的模型和零件简图一一对应。 2) 零件库的加载 参与设计用户进入设计界面，页面后台写好sql语句，调用数据库访问接口，读取数据库零件库信息，获取零件模型信息、零件简图url，并将零件简图采用后台的InnerHtml方法按零件类别分别填充至设计页面零件区的不同位置，同时为零件简图添加选中载入设计场景事件，供用户进行零件模型的选择。 3.3. 3D零件模型的添加与编辑 在3D场景中进行的产品模型的拼装设计过程，为方便记录当前设计结果，参与客户端定义本地设计对象副本结构变量，来记录当前编辑产品模型的设计状态 [ 6 ] 。同时协同成员之间通过维护设计对象副本的一致性，来维护场景中设计结果的一致性 [ 7 ] [ 11 ] 。因此，用户单步操作操作完成，需要先进行本地设计对象副本的更新，然后进行协作的操作的冲突判定或同步控制。 本节零件模型的操作从场景中零件库模型的添加、STL零件模型的导入、零件模型颜色的更换三部分展开介绍。 3.3.1. 零件库模型的添加 图3给出了整个零件库模型的添加过程的具体过程。即用户点击零件区的某一零件模型，到服务器固定文件夹下读取对应.obj模型文件以及默认.mtl格式材质文件，并将合并材质后的模型文件载入到场景的随机位置中。其后，更新本地设计对象零件副本，新增信息发送到NodeJs服务器进行协作组内操作的冲突判断或是同步处理。 首先，材质的加载。通过Three.js类库的THREE.MTLLoader()方法定义材质加载对象，使用setPath(‘model/’)设置材质文件读取路径，调用材质对象方法mtlLoader.load(name，func)方法加载材质； 其次，场景中模型的载入。通过Three.js类库的THREE.OBJLoader()方法创建.obj模型文件加载对象，使用setMaterials方法为加载对象设定上一步加载材质，同样通过对象调用setPath(‘model/’)设置.obj文件的读取路径，通过调用objLoader.load(name，func，onProgress，onError)方法加载obj文件，并将加载的模型设置其在场景的位置(本次设定为场景x，y平面上的随机位置)，通过scene.add(obj)载入场景中； 图3. 零件库模型的添加流程 最后，修改设计对象副本data，定义新增零件结构newObjData并将其添加到设计对象副本data中，该结构包含零件名称、零件标志(零件库还是自行上传的STL文件)、操作用户名、零件位置、旋转角度、大小等零件属性。更改后，将新增零件信息使用socket.io即时通信类库中的socket.emit方法将零件属性发送至NodeJs服务器，供服务器进行操作冲突的判断或同步的处理。 3.3.2. STL零件模型的导入 图4给出了STL零件模型导入的具体实现过程，即设计参与者在协同进行产品设计过程中，可随时将外部零件模型导入场景中，为产品整车设计提供个性化零部件素材，并实现单用户导入协作组内客户端用户同步可现。其中，零件模型的格式限定为STL格式，该格式为CAD软件、3Dmaxs软件、Three.js Web3D接收的通用版本。 用户选择上传零件模型文件，系统先对零件格式进行验证，验证通过进行零件模型的导入工作。 第一步：读取上传文件，并完成到STL模型对象的转换。创建FileReader对象，将上传文件file传 图4. STL模型导入流程 入对象的readAsArrayBuffer(file)方法中，触发对象的加载事件，将载入的文件通过Three.js Web3D类库的STLLoader类的parse(conetents)方法将文件转换为STL模型对象； 第二步：为模型对象添加默认材质。通过Three.js [ 7 ] 类库提供方法MeshStandardMaterial()创建标准材质实例对象，然后调用类库中的THREE.Mesh()方法，以模型文件对象和材质对象作为参数，返回合并材质后的STL模型对象； 第三步：将合并材质后的STL模型对象载入场景。在载入场景前，先设置模型对象的名称name，在场景中显示的位置position (本次设定为场景中x，y平面上的随机位置)，并通过场景提供的scene.add(mesh)方法，以合并后的STL模型对象作为参数，将模型对象载入场景中； 第四步：将上传的模型文件存入服务器文件区。创建FormData格式化数据对象，将上传文件以及文件名称使用append()方法加入到formData对象中。创建XMLHttpRequest异步请求对象，并指定异步请求方式(本次为post方式)、异步请求地址。以formData格式化数据对象作为参数，传递到指定地址应用程序，应用程序定义HttpPostedFile对象来接收文件参数file及字符串对象接收文件名称filename，并使用该对象的SaveAs(‘../model/upload/’+filename)方法，传入文件保存地址，将文件保存到服务器的指定位置； 最后，更新设计对象副本data，修改后向服务器发送新增零件信息，服务器进行操作冲突的判断或同步的处理(本部分同零件库模型的加载)。 3.3.3. 零件模型颜色的更换 首先，获取页面颜色插件选中颜色rgb值，采用transformControls.object获取场景中当前操作物体；其次，采用Three.js类库中的THREE.MeshPhongMaterial()方法通过传递color颜色参数，创建颜色材质对象；最后，将object物体的material属性，设置为该颜色材质对象。颜色更换结束，更新本地设计对象数据副本，并将模型颜色以及模型名称进行封装，发送至NodeJs服务器，供服务器进行操作冲突的判断或同步的处理。 3.3.4. 场景中的零件清除 为给参与设计提供一个灵活、可回溯的设计过程，本次在3D场景中完成的产品拼装设计操作，除对产品零件模型提供载入、大小、位置、旋转角度、颜色等的设计操作外，还提供了设计场景中零件的删除、场景的清空、场景中上下步操作的回滚等功能。 1) 删除选中零件 通过transformControls对象的object属性获取场景中当前操作物体，采用场景对象的scene.remove(object)方法将选中物体从场景中移除，同时移除本地设计对象数据副本对应零件信息，将移除零件模型封装后发送至NodeJs服务器，供其进行冲突的判断或同步操作的处理。 2) 清空场景 获取本地设计对象数据副本，逐条遍历副本数据的零件信息，取出零件名称data.modelSign，通过scene.getObjectByName(data.modelSign)方法获取场景中名称对应物体，并使用scene.remove(obj)方法，以获取物体作为参数，将该物体从场景中移除。最后，清空本地设计对象副本，向NodeJs服务器发送清空场景信息，供服务器进行同步用户操作的处理工作。