Files
YiJieVue2TV/src/components/DMT/indexVR.vue
2026-06-09 08:36:43 +08:00

1252 lines
43 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div>
<div id="viewPort" :style="{height: containerHeight,width: containerWidth}"></div>
<div ref="modelProcess" style="position: absolute;top: 10px;left: 2px;color: white">正在加载模型0%</div>
<div style="display: none">
<audio controls loop id="alarm">
<source src="./libs/alarm.mp3" type="audio/mpeg">
</audio>
<audio controls id="clash">
<source src="./libs/alarm.mp3" type="audio/mpeg">
</audio>
</div>
<div>
</div>
<!-- <div class="viewOperationParent" id="viewOperationParent">-->
<!-- <div style="display:inline;text-align: center;background: #074492" id="viewOperationChild">-->
<!-- <div style="text-align: center">-->
<!-- &lt;!&ndash; <el-checkbox v-model="checked" style="margin-right: 5px;color: #fff">设定</el-checkbox>&ndash;&gt;-->
<!-- <el-button v-for="(item, index) in cameraData" :id="'cameraViewBtn' + item.焦点代码" :key=index style="height: 36px;white-space:normal;">{{ item.焦点说明 }}</el-button>-->
<!-- <button id="setView" style="width: 20px;height: 20px;padding: 0;margin: 0;background: transparent;border: none;cursor: pointer;"></button>-->
<!-- </div>-->
<!-- </div>-->
<!-- </div>-->
</div>
</template>
<script>
import * as THREE from 'three'
import Stats from 'three/examples/jsm/libs/stats.module.js'
import { VRButton } from './VR/VRButton.js'
import { XRControllerModelFactory } from 'three/examples/jsm/webxr/XRControllerModelFactory.js'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader'
import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader'
import axios from 'axios'
import $ from 'jquery'
import * as TWEEN from 'three/examples/jsm/libs/tween.module.js'
import { DmtControl } from './js/DmtControl.js'
import { connect } from './js/websocketConnect.js'
import { connectMqtt } from './js/MqttConnect.js'
import { PlaySoundCommand } from './js/PlaySoundCommand.js'
// import { Vector3 } from 'three'
// THREE.Object3D.DefaultUp = new Vector3(0, 0, 1)
let controller1, controller2
let controllerGrip1, controllerGrip2
const group = new THREE.Group()
let marker, floor, baseReferenceSpace
// const modelList = ['Cell3-转台', 'R-2000iC-210F-robot1', 'R-2000iC-210F-robot4', 'R-2000iC-270F-robot2', 'R-2000iC-270F-robot3', 'robot1底座', 'robot2-夹爪1台', 'robot2-夹爪2-台',
// 'robot2-底座', 'robot3-底座', '加工件-存放台A', '加工件-存放台B', '加工件-存放台C', '安全防护', '辊道-1', '辊道', '平面']
const modelList = ['R-2000iC-210F-robot1', 'robot1底座', 'robot2-夹爪1台', 'robot2-夹爪2-台',
'robot2-底座', 'robot3-底座', '平面']
let INTERSECTION
const tempMatrix = new THREE.Matrix4()
let stats
let container, clock, scene, camera, axesHelper, renderer, Plane
let modelCount = 0
const editor = {
clock: null,
robotInfo: {
'B3FD789E-E018-4006-91AD-4824830E6557': {
joint: {
'J1': 999.99,
'J2': 999.99,
'J3': 999.99,
'J4': 999.99,
'J5': 999.99,
'J6': 999.99
},
tcp: {
'tx': 999.99,
'ty': 999.99,
'tz': 999.99,
'rx': 999.99,
'ry': 999.99,
'rz': 999.99
},
speed: 100,
alarm: [
{
'AlarmID': 2,
'AlarmNumber': 78,
'CauseAlarmID': 0,
'CauseAlarmNumber': 0,
'Severity': 0,
'Time': '2020-03-12T15:34:18',
'AlarmMessage': 'FILE-078 ラヤカッアクキンヘ?ノ',
'CauseAlarmMessage': '',
'SeverityMessage': 'WARN'
},
{
'AlarmID': 2,
'AlarmNumber': 77,
'CauseAlarmID': 0,
'CauseAlarmNumber': 0,
'Severity': 0,
'Time': '2020-03-12T15:30:12',
'AlarmMessage': 'FILE-077 ラヤカッアクキンソェハシ (FRA:\\)',
'CauseAlarmMessage': '',
'SeverityMessage': 'WARN'
},
{
'AlarmID': 76,
'AlarmNumber': 125,
'CauseAlarmID': 0,
'CauseAlarmNumber': 0,
'Severity': 34,
'Time': '2020-03-12T15:27:06',
'AlarmMessage': 'DNET-125 エモカッヨ眞ャスモマミヨテ: Bd 2',
'CauseAlarmMessage': '',
'SeverityMessage': 'PAUSE.G'
}
]
},
'A769F461-4011-46A4-BBC2-2AFB39EFD9C2': {
joint: {
'J1': 888.88,
'J2': 888.88,
'J3': 888.88,
'J4': 888.88,
'J5': 888.88,
'J6': 888.88
},
tcp: {
'tx': 888.88,
'ty': 888.88,
'tz': 888.88,
'rx': 888.88,
'ry': 888.88,
'rz': 888.88
},
speed: 99,
alarm: [
{
'AlarmID': 2,
'AlarmNumber': 78,
'CauseAlarmID': 0,
'CauseAlarmNumber': 0,
'Severity': 0,
'Time': '2020-03-12T15:34:18',
'AlarmMessage': 'FILE-078 ラヤカッアクキンヘ?ノ',
'CauseAlarmMessage': '',
'SeverityMessage': 'WARN'
},
{
'AlarmID': 2,
'AlarmNumber': 77,
'CauseAlarmID': 0,
'CauseAlarmNumber': 0,
'Severity': 0,
'Time': '2020-03-12T15:30:12',
'AlarmMessage': 'FILE-077 ラヤカッアクキンソェハシ (FRA:\\)',
'CauseAlarmMessage': '',
'SeverityMessage': 'WARN'
},
{
'AlarmID': 76,
'AlarmNumber': 125,
'CauseAlarmID': 0,
'CauseAlarmNumber': 0,
'Severity': 34,
'Time': '2020-03-12T15:27:06',
'AlarmMessage': 'DNET-125 エモカッヨ眞ャスモマミヨテ: Bd 2',
'CauseAlarmMessage': '',
'SeverityMessage': 'PAUSE.G'
}
]
},
'04A6D8F9-7019-4C92-8845-B3CFDDF4A433': {
joint: {
'J1': 777.77,
'J2': 777.77,
'J3': 777.77,
'J4': 777.77,
'J5': 777.77,
'J6': 777.77
},
tcp: {
'tx': 777.77,
'ty': 777.77,
'tz': 777.77,
'rx': 777.77,
'ry': 777.77,
'rz': 777.77
},
speed: 98,
alarm: [
{
'AlarmID': 2,
'AlarmNumber': 78,
'CauseAlarmID': 0,
'CauseAlarmNumber': 0,
'Severity': 0,
'Time': '2020-03-12T15:34:18',
'AlarmMessage': 'FILE-078 ラヤカッアクキンヘ?ノ',
'CauseAlarmMessage': '',
'SeverityMessage': 'WARN'
},
{
'AlarmID': 2,
'AlarmNumber': 77,
'CauseAlarmID': 0,
'CauseAlarmNumber': 0,
'Severity': 0,
'Time': '2020-03-12T15:30:12',
'AlarmMessage': 'FILE-077 ラヤカッアクキンソェハシ (FRA:\\)',
'CauseAlarmMessage': '',
'SeverityMessage': 'WARN'
},
{
'AlarmID': 76,
'AlarmNumber': 125,
'CauseAlarmID': 0,
'CauseAlarmNumber': 0,
'Severity': 34,
'Time': '2020-03-12T15:27:06',
'AlarmMessage': 'DNET-125 エモカッヨ眞ャスモマミヨテ: Bd 2',
'CauseAlarmMessage': '',
'SeverityMessage': 'PAUSE.G'
}
]
},
'B2BF5C42-B6ED-46D8-A7C3-046968D39805': {
joint: {
'J1': 666.66,
'J2': 666.66,
'J3': 666.66,
'J4': 666.66,
'J5': 666.66,
'J6': 666.66
},
tcp: {
'tx': 666.66,
'ty': 666.66,
'tz': 666.66,
'rx': 666.66,
'ry': 666.66,
'rz': 666.66
},
speed: 97,
alarm: [
{
'AlarmID': 2,
'AlarmNumber': 78,
'CauseAlarmID': 0,
'CauseAlarmNumber': 0,
'Severity': 0,
'Time': '2020-03-12T15:34:18',
'AlarmMessage': 'FILE-078 ラヤカッアクキンヘ?ノ',
'CauseAlarmMessage': '',
'SeverityMessage': 'WARN'
},
{
'AlarmID': 2,
'AlarmNumber': 77,
'CauseAlarmID': 0,
'CauseAlarmNumber': 0,
'Severity': 0,
'Time': '2020-03-12T15:30:12',
'AlarmMessage': 'FILE-077 ラヤカッアクキンソェハシ (FRA:\\)',
'CauseAlarmMessage': '',
'SeverityMessage': 'WARN'
},
{
'AlarmID': 76,
'AlarmNumber': 125,
'CauseAlarmID': 0,
'CauseAlarmNumber': 0,
'Severity': 34,
'Time': '2020-03-12T15:27:06',
'AlarmMessage': 'DNET-125 エモカッヨ眞ャスモマミヨテ: Bd 2',
'CauseAlarmMessage': '',
'SeverityMessage': 'PAUSE.G'
}
]
}
},
showTagList: [{
title: '工件QY16KC',
ModelId: 'A113AD62-9CE7-4ACF-8D0C-8E8A597A8768',
object: null
}],
ip: '',
WeldLight1: '',
WeldLightVisible1: false,
WeldLight2: '',
WeldLightVisible2: false,
scene: null,
objectIdUUidMap: new Map(),
opname: '',
connectType: 1,
BSport: '',
IsAllowConnect: true,
service: axios.create({
baseURL: './submit/MESCommonBase.ashx',
timeout: 1500000 // 请求超时时间
}),
selected: null,
socket: null,
mqttClient: {
connected: false
},
ProjectCode: null,
sceneConfig: null,
resourcesTree: null,
attachments: null,
resources: null,
cameraState: null,
cameraStateAll: [],
isOk: false,
objectIdSet: new Map(),
animOperations: null,
CraftList: {},
moveTrigger: null,
camera: null,
cameraPosition: 10,
currentButton: 1,
cameraControls: null,
TagActList: {}, // 存放tag对应的类型
worker: {}, // 播放工艺的线程
RotationWorker: {}, // 自传工艺的线程
ModelRotateWorker: {}, // 自传工艺的的状态数据
rotateSpeed: 5 * Math.PI / 180,
getObjectById: null, // 方法 根据ModelId获取模型
solveAttachModel: null, // 方法 处理attach问题
removeObject: null, // 方法 移除模型
setModelWireFrame: null, // 方法 设置线框
setTransparent: null, // 方法 设置透明
showMessage: null, // 方法 显示消息
playSoundCommand: new PlaySoundCommand(), // 方法 警告
attachList: new Map(), // 记录attach列表
objMaterial: new Map(),
WareHouseList: [],
WareHouse: new Map(), // 立库库存表
WareHouseBasicModel: new Map(), // 立库库存基础 零件模型需要命名为warehousepart1-100 立库定位模型需要命名为warehouse1000-1009
ProtocolIdentifiers: {
Robot_7: 'Robot_7',
Device_3: 'Device_3',
Agv_3: 'Agv_3',
Device_1: 'Device_1',
FirstPosition: 'FirstPosition',
WareHousePart: 'WareHousePart',
Robot_6: 'Robot_6',
RobotTcp: 'RobotTcp',
DrawPath: 'DrawPath',
ReLoadData: 'ReLoadData',
PlayAct: 'PlayAct',
CraftCurrOperation: 'CraftCurrOperation',
Position: 'Position',
Select: 'Select',
DragMove: 'DragMove',
ModelTransparent: 'ModelTransparent',
Visible: 'Visible',
Frames: 'Frames',
ModelRotation: 'ModelRotation',
GoHome: 'GoHome',
GetJointAxisInfo: 'GetJointAxisInfo',
GetDeviceBaseTool: 'GetDeviceBaseTool',
GetJointValue: 'GetJointValue',
C_B_ImportModel: 'C_B_ImportModel',
SendURDF: 'SendURDF',
SendDeviceJson: 'SendDeviceJson',
GetDeviceJson: 'GetDeviceJson',
SendMotionInfo: 'SendMotionInfo',
C_B_ImportLayout: 'C_B_ImportLayout',
GetPoseLocation: 'GetPoseLocation',
C_B_SetProjectCode: 'C_B_SetProjectCode',
C_B_TagIDTrigger: 'C_B_TagIDTrigger',
C_B_ReLoad_TagIDHandler: 'C_B_ReLoad_TagIDHandler',
C_B_SetCurrSignalList: 'C_B_SetCurrSignalList',
SendClearModelInfo: 'SendClearModelInfo',
C_B_SaveProjectSimulationData: 'C_B_SaveProjectSimulationData',
C_B_GetProjectSimulationData: 'C_B_GetProjectSimulationData',
C_B_KinematicsEditing: 'C_B_KinematicsEditing',
C_B_SaveModel: 'C_B_SaveModel',
C_B_ObjectAlarm: 'C_B_ObjectAlarm',
JointMoveTo: 'JointMoveTo' // DT|JointMoveTo|modelCode|{"J1":"1","J2":"2","J3":"3","J4":"4","J5":"5","J6":"6","J7":"7","J8":"8","J9":"9"}
},
craftPlaySolveType: {
visible: 'visible',
rotation: 'rotation',
craft: 'craft',
signalwrite: 'signalwrite',
wait: 'wait',
FrameData: 'FrameData',
attach: 'attach',
warning: 'warning'
},
FrameOperation: {
playCraft: true,
visibleObject: true,
rotationObject: true,
writeSignal: true,
waitSignal: true,
attach: true,
warning: true
},
signalSolveType: {
visible: 'visible',
rotation: 'rotation',
craft: 'craft',
targetLocation: 'targetlocation',
attach: 'attach',
warning: 'warning'
}
}
// eslint-disable-next-line no-unused-vars
let ModelListTransParentList = []
const ModelList = []
const ModelListId = []
// eslint-disable-next-line no-unused-vars
const timer = null
// eslint-disable-next-line no-unused-vars
const lastIntersectModel = null
// eslint-disable-next-line no-unused-vars
const onUpPosition = new THREE.Vector2()
var raycaster = new THREE.Raycaster()
var mouse = new THREE.Vector2()
window.editor = editor
export default {
name: 'DemoVR',
props: {
containerHeight: {
type: String,
default: '100vh'
},
containerWidth: {
type: String,
default: '100vw'
}
},
mounted() {
editor.objectIdSet = new Map()
editor.objMaterial = new Map()
editor.attachList = new Map()
this.getConfig().then(res => {
if (Object.hasOwnProperty.call(res, 'robotList')) {
editor.robotList = res.robotList
}
if (Object.hasOwnProperty.call(res, 'WareHouseList')) {
editor.WareHouseList = res.WareHouseList
}
if (Object.hasOwnProperty.call(res, 'transParentList')) {
ModelListTransParentList = res.transParentList
}
if (Object.hasOwnProperty.call(res, 'connectType')) {
editor.connectType = res.connectType
}
editor.mqttHost = res.MQTTHost
editor.mqttPort = res.MQTTPort
editor.ip = res.webSocketIp
editor.opname = res.webSocketOpName
editor.BSport = res.webSocketBSport
editor.rootIp = res.NodeRootIp
this.getProjectConfig()
})
},
methods: {
getConfig() {
return new Promise((resolve, reject) => {
axios.get('./DMT_WEB/config/config.json').then(res => {
const configData = res.data
resolve(configData)
}).catch(err => {
reject(err)
})
})
},
/**
* 获取项目配置文件,用于初始隐藏模型
*/
getProjectConfig() {
const P1 = new Promise(function(resolve, reject) {
axios({
method: 'get',
url: './DMT_WEB/models/DTDATA.json'
}).then((res) => {
editor.animOperations = {}
editor.moveTrigger = {}
res.data.forEach(n => {
Object.keys(n).forEach(function(key) {
/**
* 获取播放数据并解析数据
*/
if (key === 'OperationTree') {
n[key].forEach(CraftData => {
editor.CraftList[CraftData.CraftCode] = {}
editor.CraftList[CraftData.CraftCode]['CraftName'] = CraftData.CraftName
editor.CraftList[CraftData.CraftCode]['Varieties'] = CraftData.Varieties
editor.CraftList[CraftData.CraftCode]['CurrPlay'] = CraftData.CurrPlay
if (CraftData.CraftPlayData.length < 2) {
editor.animOperations[CraftData.CraftCode] = ''
} else {
editor.animOperations[CraftData.CraftCode] = JSON.parse(CraftData.CraftPlayData)
}
editor.moveTrigger[CraftData.CraftCode] = {
moveStatus: 0,
myVarSetInterval: null,
doAnimation: false,
currentFrame: 0,
pausedFrame: 0,
animOperations: editor.animOperations[CraftData.CraftCode],
simPaceTime: 20,
moveStatusPosition: 0
}
})
}
/**
* HandlerType: 1:显隐,2:自转,3行为
* HandlerCode: 模型或者工艺代码
* HandlerValue: 显隐0/1 ,自传:正负转速&axis,行为暂停播放0/1
* TagValue:信号值
*/
if (key === 'TagTriggerHandler') {
n[key].forEach(TagActListValue => {
if (editor.TagActList[TagActListValue.TagID]) {
editor.TagActList[TagActListValue.TagID].push({
HandlerType: TagActListValue.HandlerType,
HandlerCode: TagActListValue.HandlerCode,
HandlerValue: TagActListValue.HandlerValue,
TagValue: TagActListValue.TagValue,
JointType: TagActListValue.JointType,
Axis: TagActListValue.Axis,
Flip: TagActListValue.Flip,
Value: TagActListValue.Value,
ValueFactor: TagActListValue.ValueFactor
})
} else {
editor.TagActList[TagActListValue.TagID] = []
editor.TagActList[TagActListValue.TagID].push({
HandlerType: TagActListValue.HandlerType,
HandlerCode: TagActListValue.HandlerCode,
HandlerValue: TagActListValue.HandlerValue,
TagValue: TagActListValue.TagValue,
JointType: TagActListValue.JointType,
Axis: TagActListValue.Axis,
Flip: TagActListValue.Flip,
Value: TagActListValue.Value,
ValueFactor: TagActListValue.ValueFactor
})
}
})
}
})
})
resolve(1)
})
})
const P2 = new Promise(function(resolve, reject) {
axios({
method: 'get',
url: './DMT_WEB/models/RobotPlan_Process.json'
}).then((res) => {
editor.sceneConfig = res.data.sceneConfig // 场景配置
editor.attachments = res.data.attachments // 附加物体
editor.resources = res.data.resources // 场景模型资源
editor.resourcesTree = res.data.resourcesTree // 模型树
editor.cameraState = res.data.cameraState // 场景相机位置
editor.ProjectCode = res.data.ProjectCode // 项目代码
editor.resourcesTree.forEach(res => {
if (res.treeType === 'OriginModel') {
ModelList.push(res.name)
}
})
editor.resources.forEach(res => {
editor.objectIdUUidMap.set(res.id, res)
})
editor.attachments.forEach(res => {
editor.attachList.set(res.RootId, res.attachList)
})
resolve(1)
}).catch((error) => { console.log(error) })
})
Promise.all([P1, P2]).then(res => {
this.init()
// this.getCameraData()
})
},
/**
* @name: ZhangShun
* @date: 2022/9/24
* @explain: 初始三维化场景
*/
init() {
clock = new THREE.Clock()
editor.clock = clock
const scope = this
container = document.getElementById('viewPort')
stats = new Stats()
container.appendChild(stats.dom)
scene = this.initScene()
editor.scene = scene
camera = this.initCamera()
editor.camera = camera
group.rotation.x = -0.5 * Math.PI
group.position.y = -0.4
scene.add(group)
axesHelper = new THREE.AxesHelper(1)
scene.add(axesHelper)
scene.add(new THREE.HemisphereLight(0x606060, 0x404040))
const light = new THREE.DirectionalLight(0xffffff)
light.position.set(1, 1, 1).normalize()
scene.add(light)
marker = new THREE.Mesh(
new THREE.CircleGeometry(0.25, 32).rotateX(-Math.PI / 2),
new THREE.MeshBasicMaterial({ color: 0x808080 })
)
scene.add(marker)
raycaster = new THREE.Raycaster()
renderer = new THREE.WebGLRenderer({
antialias: true,
alpha: true,
logarithmicDepthBuffer: true,
preserveDrawingBuffer: true
})
renderer.setPixelRatio(window.devicePixelRatio)
renderer.setSize(container.clientWidth, container.clientHeight)
renderer.outputEncoding = THREE.sRGBEncoding
renderer.setClearColor(0x666666, 0)
renderer.toneMapping = THREE.ACESFilmicToneMapping
renderer.toneMappingExposure = 0.8
renderer.shadowMap.enabled = true
renderer.hadowMapEnabled = true
renderer.shadowMap.type = THREE.PCFSoftShadowMap
renderer.textureEncoding = THREE.sRGBEncoding
renderer.xr.addEventListener('sessionstart', function() {
baseReferenceSpace = renderer.xr.getReferenceSpace()
})
renderer.xr.enabled = true
container.appendChild(renderer.domElement)
window.addEventListener('resize', this.onWindowResize, false)
editor.showMessage = function(message, type, time = 2000) {
scope.$message({
message: message,
type: type,
duration: time,
center: true
})
}
editor.getObjectById = function(ModelId) {
let currentModel = null
const currentModelData = editor.objectIdSet.get(ModelId)
if (currentModelData) currentModel = currentModelData.object
return currentModel
}
editor.solveAttachModel = function(ModelId, attach = true) {
const attachList = editor.attachList
attachList.forEach(function(value, key) {
const RootModel = editor.getObjectById(key)
if (RootModel) {
for (const attachData of value) {
const attachModel = editor.getObjectById(attachData.ModelId)
let parentObject = null
attachData['ParentId'] ? parentObject = editor.getObjectById(attachData.ParentId) : parentObject = editor.scene
if (attachModel && parentObject) {
if (attach) {
RootModel.attach(attachModel)
} else {
parentObject.attach(attachModel)
}
}
}
}
})
}
editor.removeObject = function(object) {
if (object === null) return
if (object.parent === null) return
object.parent.remove(object)
}
editor.setModelWireFrame = function(currentObject, WireframeValue) {
currentObject.traverse(obj => {
if (obj instanceof THREE.Mesh) {
obj.material = editor.objMaterial.get(obj.id).clone()
obj.material['wireframe'] = WireframeValue
obj.material.needsUpdate = true
}
})
}
editor.setTransparent = function(ModelId, Opacity = 0.3) {
new Promise((resolve, reject) => {
const TransModel = editor.getObjectById(ModelId)
if (!TransModel) {
console.warn('模型不存在setTransparent')
reject(false)
} else {
TransModel.traverse(obj => {
if (obj.material) {
const material_yuan = editor.objMaterial.get(obj.id).clone()
obj.material = material_yuan
if (Opacity !== 1) {
obj.material = new THREE.MeshStandardMaterial({
color: material_yuan.color,
side: 2,
transparent: true,
opacity: Opacity
})
}
}
})
resolve(true)
}
})
}
// region 手柄控制器的设定
controller1 = renderer.xr.getController(0)
controller1.addEventListener('selectstart', this.onSelectStart)
controller1.addEventListener('selectend', this.onSelectEnd)
controller1.addEventListener('connected', function(event) {
this.add(scope.buildController(event.data))
})
controller1.addEventListener('disconnected', function() {
this.remove(this.children[ 0 ])
})
scene.add(controller1)
controller2 = renderer.xr.getController(1)
controller2.addEventListener('selectstart', this.onSelectStart)
controller2.addEventListener('selectend', this.onSelectEnd)
controller2.addEventListener('connected', function(event) {
this.add(scope.buildController(event.data))
})
controller2.addEventListener('disconnected', function() {
this.remove(this.children[ 0 ])
})
scene.add(controller2)
// The XRControllerModelFactory will automatically fetch controller models
// that match what the user is holding as closely as possible. The models
// should be attached to the object returned from getControllerGrip in
// order to match the orientation of the held device.
const controllerModelFactory = new XRControllerModelFactory()
controllerGrip1 = renderer.xr.getControllerGrip(0)
controllerGrip1.add(controllerModelFactory.createControllerModel(controllerGrip1))
scene.add(controllerGrip1)
controllerGrip2 = renderer.xr.getControllerGrip(1)
controllerGrip2.add(controllerModelFactory.createControllerModel(controllerGrip2))
scene.add(controllerGrip2)
// #endregion
// region 相机控制器的设定
editor.cameraControls = new DmtControl(editor.camera, renderer.domElement)
editor.cameraControls.listenToKeyEvents(window) // optional
editor.cameraControls.update()
// endregion
// region 加载模型
const pmremGenerator = new THREE.PMREMGenerator(renderer)
pmremGenerator.compileEquirectangularShader()
const loader = new GLTFLoader().setPath('./DMT_WEB/models/')
const dracoLoader = new DRACOLoader()
new RGBELoader().setDataType(THREE.FloatType)
.load('./DMT_WEB/assets/venice_sunset_1k.hdr', function(texture) {
scene.environment = pmremGenerator.fromEquirectangular(texture).texture
scene.background = new THREE.Color('#081f48')
texture.dispose()
pmremGenerator.dispose()
})
dracoLoader.setDecoderPath('./DMT_WEB/draco/')
loader.setDRACOLoader(dracoLoader)
for (let i = 0; i < ModelList.length; i++) {
loader.load(ModelList[i] + '.glb', function(gltf) {
const currentModel = gltf.scene.children[0]
if (currentModel.name === '平面') {
floor = currentModel
}
currentModel.traverse(child => {
if (child['material']) {
child['material'].metalness = 1
editor.objMaterial.set(child.id, child['material'])
}
})
group.add(currentModel)
scope.setObjectByConfig(currentModel)
ModelListId.push(gltf.scene.id)
modelCount += 1
scope.$refs.modelProcess.innerText = '正在加载模型' + (modelCount / ModelList.length * 100).toFixed(2) + '%'
if (modelCount === ModelList.length) {
container.appendChild(VRButton.createButton(renderer))
scope.$refs.modelProcess.style.display = 'none'
scope.lookObject(scene)
scope.setModelSates()
scope.connectService()
}
}, (xhr) => {
// eslint-disable-next-line handle-callback-err
}, (error) => {
modelCount += 1
console.log(`加载模型${ModelList[i].ModelName}出错!`)
if (modelCount === ModelList.length) {
container.appendChild(VRButton.createButton(renderer))
scope.$refs.modelProcess.style.display = 'none'
scope.lookObject(scene)
scope.setModelSates()
scope.connectService()
}
})
}
// endregion
this.animate()
this.render()
},
/**
* @name: ZhangShun
* @date: 2022/9/24
* @explain: 初始化场景
*/
initScene() {
const sceneInit = new THREE.Scene()
sceneInit.background = new THREE.Color(0x505050)
return sceneInit
},
/**
* @name: ZhangShun
* @date: 2022/9/24
* @explain: 初始化相机
*/
initCamera() {
const cameraInit = new THREE.PerspectiveCamera(50, container.clientWidth / container.clientHeight, 0.01, 10000)
cameraInit.position.set(editor.cameraPosition, editor.cameraPosition, editor.cameraPosition)
cameraInit.position.set(5, 5, 9)
return cameraInit
},
/**
* @name: ZhangShun
* @date: 2022/9/24
* @explain: 手柄扳机键按下
*/
onSelectStart(event) {
console.log('onSelectStart', event)
event.target.userData.isSelecting = true
},
/**
* @name: ZhangShun
* @date: 2022/9/24
* @explain: 手柄扳机键松开
*/
onSelectEnd(event) {
console.log('onSelectEnd', event)
event.target.userData.isSelecting = false
if (INTERSECTION) {
const offsetPosition = { x: -INTERSECTION.x, y: -INTERSECTION.y, z: -INTERSECTION.z, w: 1 }
const offsetRotation = new THREE.Quaternion()
// eslint-disable-next-line no-undef
const transform = new XRRigidTransform(offsetPosition, offsetRotation)
const teleportSpaceOffset = baseReferenceSpace.getOffsetReferenceSpace(transform)
renderer.xr.setReferenceSpace(teleportSpaceOffset)
}
},
/**
* 创建手柄控制器
*
*/
buildController(data) {
let geometry, material
switch (data.targetRayMode) {
case 'tracked-pointer':
geometry = new THREE.BufferGeometry()
geometry.setAttribute('position', new THREE.Float32BufferAttribute([0, 0, 0, 0, 0, -1], 3))
geometry.setAttribute('color', new THREE.Float32BufferAttribute([0.5, 0.5, 0.5, 0, 0, 0], 3))
material = new THREE.LineBasicMaterial({ vertexColors: true, blending: THREE.AdditiveBlending })
return new THREE.Line(geometry, material)
case 'gaze':
geometry = new THREE.RingGeometry(0.02, 0.04, 32).translate(0, 0, -1)
material = new THREE.MeshBasicMaterial({ opacity: 0.5, transparent: true })
return new THREE.Mesh(geometry, material)
}
},
/**
* @name: ZhangShun
* @date: 2022/9/24
* @explain: 自适应窗口尺寸
*/
onWindowResize() {
camera.aspect = container.clientWidth / container.clientHeight
camera.updateProjectionMatrix()
renderer.setSize(container.clientWidth, container.clientHeight)
},
animate() {
// this.render()
// requestAnimationFrame(this.animate)
renderer.setAnimationLoop(this.render)
},
render() {
TWEEN.update()
renderer.render(scene, camera)
this.vrTeleport()
stats.update()
},
/**
* @name: ZhangShun
* @date: 2022/9/24
* @explain: VR传送瞬移功能
*/
vrTeleport() {
INTERSECTION = undefined
if (controller1.userData.isSelecting === true) {
tempMatrix.identity().extractRotation(controller1.matrixWorld)
raycaster.ray.origin.setFromMatrixPosition(controller1.matrixWorld)
raycaster.ray.direction.set(0, 0, -1).applyMatrix4(tempMatrix)
const intersects = raycaster.intersectObjects([floor])
if (intersects.length > 0) {
INTERSECTION = intersects[ 0 ].point
}
} else if (controller2.userData.isSelecting === true) {
tempMatrix.identity().extractRotation(controller2.matrixWorld)
raycaster.ray.origin.setFromMatrixPosition(controller2.matrixWorld)
raycaster.ray.direction.set(0, 0, -1).applyMatrix4(tempMatrix)
const intersects = raycaster.intersectObjects([floor])
if (intersects.length > 0) {
INTERSECTION = intersects[ 0 ].point
}
}
if (INTERSECTION) {
marker.position.copy(INTERSECTION)
marker.position.y += 0.01
}
marker.visible = INTERSECTION !== undefined
},
// region 自定义功能
/**
* 根据有配置文件设置模型初始化属性
* @param object
*/
setObjectByConfig(object) {
const objectId = object.userData['ModelId']
const objectResource = editor.objectIdUUidMap.get(objectId)
if (objectResource) {
object.position.copy(
new THREE.Vector3(parseFloat(objectResource.location.position.x),
parseFloat(objectResource.location.position.y),
parseFloat(objectResource.location.position.z)
))
object.quaternion.copy(
new THREE.Quaternion(parseFloat(objectResource.location.quaternion.x),
parseFloat(objectResource.location.quaternion.y),
parseFloat(objectResource.location.quaternion.z),
parseFloat(objectResource.location.quaternion.w)
))
object.visible = Boolean(parseInt(objectResource.visible))
}
},
/**
* 连接方式选择
*/
connectService() {
switch (editor.connectType) {
case 1:
connect(editor)
break
case 2:
connectMqtt(editor)
break
default:
break
}
},
drawWeldLight() {
const geometry = new THREE.BufferGeometry()
const vertices = []
const textureLoader = new THREE.TextureLoader()
const sprite1 = textureLoader.load('./css/lensflare0.png')
const x = 0
const y = 0.3
const z = -0.07
const materials = []
vertices.push(x, y, z)
geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3))
const parameters = [
[[1.0, 1, 1], sprite1, 50]
]
const sprite = parameters[ 0 ][ 1 ]
const size = parameters[ 0 ][ 2 ]
materials[ 0 ] = new THREE.PointsMaterial({ size: size, map: sprite, blending: THREE.AdditiveBlending, depthTest: false, transparent: true })
return new THREE.Points(geometry, materials[0])
},
/**
* 解析模型
* @param object
*/
lookObject(object) {
object.traverse(function(child) {
if (!child) return
// 避免modelId重复
if (Object.hasOwnProperty.call(child.userData, 'ModelId')) {
if (editor.objectIdSet.has(child.userData['ModelId'])) {
child.userData['ModelId'] = child.uuid
editor.objectIdSet.set(child.uuid, {
ModelId: child.id,
Name: child.name,
position: JSON.parse(JSON.stringify(child.position)),
rotation: JSON.parse(JSON.stringify(child.rotation)),
quaternion: JSON.parse(JSON.stringify(child.quaternion)),
object: child,
matrix: child.matrix.clone()
})
} else {
editor.objectIdSet.set(child.userData['ModelId'], {
ModelId: child.id,
Name: child.name,
position: JSON.parse(JSON.stringify(child.position)),
rotation: JSON.parse(JSON.stringify(child.rotation)),
quaternion: JSON.parse(JSON.stringify(child.quaternion)),
object: child,
matrix: child.matrix.clone()
})
}
}
})
for (const robotListInfo in editor.robotList) {
for (const info of editor.robotList[robotListInfo]) {
info.object = editor.getObjectById(info.ModelId)
info.matrix = info.object ? info.object.matrix.clone() : null
info.lastValue = null
}
}
},
/**
* 获取预置焦点
*/
getCameraData() {
var param = []
param[0] = ['ProjectCode', editor.ProjectCode]
var Data = this.CreateData('3', 'select * from dbo.[基础数据_焦点预置]', param)
this.ExecDatabase(Data).then(response => {
this.cameraData = response.data
// this.setViewCamera()
})
},
/**
* 点击后 到达预置焦点
*/
setViewCamera() {
if (this.cameraData.length === 0) return
const scope = this
const viewOperation = document.getElementById('viewOperationParent')
const viewOperationChild = document.getElementById('viewOperationChild')
viewOperation.onmouseover = function() {
viewOperationChild.style.display = 'block'
}
viewOperation.onmouseout = function() {
viewOperationChild.style.display = 'none'
}
setTimeout(() => {
for (const info of this.cameraData) {
$('#cameraViewBtn' + info.焦点代码).on('click', function(e) {
const data = JSON.parse(info.焦点位置)
const target = data.target
const position = data.position
const zoom0 = data.zoom0
const id = info.焦点代码
$('#cameraViewBtn' + info.焦点代码).css({ 'background': '#00893d', 'color': '#fff' })
editor.cameraControls.resetState(target, position, zoom0)
for (const infoColor of scope.cameraData) {
if (id !== infoColor.焦点代码) {
$('#cameraViewBtn' + infoColor.焦点代码).css({ 'background': '#fff', 'color': '#606266' })
}
}
})
}
}, 1000)
},
getMousePosition(dom, x, y) {
const rect = dom.getBoundingClientRect()
return [(x - rect.left) / rect.width, (y - rect.top) / rect.height]
},
ondblclick(event) {
const array = this.getMousePosition(container, event.clientX, event.clientY)
onUpPosition.fromArray(array)
let intersects = this.getIntersects(onUpPosition, editor.scene.children)
intersects = intersects.filter(res => {
return res.object.visible
})
if (intersects.length > 0) {
const rootObjId = intersects[0].object.userData['RootId']
editor.selected = editor.getObjectById(rootObjId)
this.sendClickModel(editor.selected)
} else {
editor.selected = null
this.sendClickModel(null)
}
},
onMouseMove(event) {
// const array = this.getMousePosition(container, event.clientX, event.clientY)
// onUpPosition.fromArray(array)
// if (!timer) {
// timer = setTimeout(() => {
// let intersects = this.getIntersects(onUpPosition, editor.scene.children)
// intersects = intersects.filter(res => {
// return res.object.visible
// })
// if (intersects.length > 0) {
// if (lastIntersectModel !== intersects[0]) {
// if (['6E90DC77-8E67-46CF-8B70-319D62E748E3', '2ED26E76-CD2C-4325-8DBA-7B54D9A4B81D'].indexOf(intersects[0].object.userData['ModelId']) !== -1) {
// const rootObjId = intersects[0].object.userData['RootId']
// editor.selected = editor.getObjectById(rootObjId)
// } else {
// editor.selected = null
// }
// }
// } else {
// editor.selected = null
// }
// clearTimeout(timer)
// timer = null
// }, 200)
// }
// updateTag(editor.selected, 'showText')
// updateTag(part2, 'showText2')
},
getIntersects(point, objects) {
mouse.set((point.x * 2) - 1, -(point.y * 2) + 1)
raycaster.setFromCamera(mouse, camera)
return raycaster.intersectObjects(objects, true)
},
sendCameraState(state) {
this.$emit('changeCameraState', state)// 自定义事件 传递值“子向父组件传值”
},
sendClickModel(object) {
},
updatePosition() {
if (editor.selected) {
const targetPosition = editor.selected.getWorldPosition(new THREE.Vector3()).clone().project(camera)
const halfWidth = window.innerWidth / 2
const halfHeight = window.innerHeight / 2
const position = {
left: targetPosition.x * halfWidth + halfWidth - 200,
top: -targetPosition.y * halfHeight + halfHeight - 500
}
this.$emit('clickModel', editor.selected, position)// 自定义事件 传递值“子向父组件传值”
}
},
updateLabelInfoJoint(ModelId, robotData) {
for (const robotDatum of Object.keys(robotData)) {
editor.robotInfo[ModelId]['joint'][robotDatum] = robotData[robotDatum]
if (this.robotInfo.id === ModelId) {
this.robotInfo['joint'][robotDatum] = robotData[robotDatum]
}
}
// if (this.robotInfo.id === ModelId) {
// console.log(robotData)
// for (const robotDatum of Object.keys(robotData)) {
// this.robotInfo['joint'][robotDatum] = robotData[robotDatum]
// }
// }
},
updateLabelInfoSpeedPose(ModelId, tcpData, speedData, alarmInfo) {
for (const robotDatum of Object.keys(tcpData)) {
editor.robotInfo[ModelId]['tcp'][robotDatum] = tcpData[robotDatum]
if (this.robotInfo.id === ModelId) {
this.robotInfo['tcp'][robotDatum] = tcpData[robotDatum]
}
}
editor.robotInfo[ModelId]['speed'] = speedData
editor.robotInfo[ModelId]['alarm'] = alarmInfo
if (this.robotInfo.id === ModelId) {
this.robotInfo['speed'] = speedData
this.robotInfo['alarm'] = alarmInfo
}
},
/**
* 设定模型初始状态
*/
setModelSates() {
// 模型的显示隐藏
editor.resourcesTree.forEach(res => {
if (res.visible === '0') {
if (editor.getObjectById(res.id)) {
editor.getObjectById(res.id).visible = !!(parseInt(res.visible))
}
}
})
const treeRes = editor.resourcesTree
for (const treeResValue of treeRes) {
if (treeResValue.treeType !== 'folder') {
const currentModel = editor.getObjectById(treeResValue.id)
if (Object.hasOwnProperty.call(treeResValue, 'WireFrame') && treeResValue.WireFrame) {
editor.setModelWireFrame(currentModel, true)
}
if (Object.hasOwnProperty.call(treeResValue, 'transParent') && treeResValue.transParent) {
editor.setTransparent(treeResValue.id, 0.3)
}
if (currentModel) currentModel.visible = !!parseInt(treeResValue.visible)
}
}
},
/**
* 设定场景初始状态
* @param type
*/
setSceneStates(type) {
switch (type) {
case '0':
scene.background = new THREE.Color('#dcdcdc')
break
case '1':
scene.background = new THREE.Color(editor.sceneConfig.RenderColorModel.color)
break
case '2':
if (editor.sceneConfig.RenderColorModel.image === '') {
editor.scene.background = new THREE.Color(editor.sceneConfig.RenderColorModel.color)
return
}
new THREE.TextureLoader().load(editor.sceneConfig.RenderColorModel.image === '', function(texture) {
scene.background = texture
texture.dispose()
})
break
default:
scene.background = new THREE.Color('#dcdcdc')
break
}
}
// endregion
}
}
</script>
<style scoped>
</style>