CourseChapter3d.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541
  1. <!-- 实现具体3d逻辑使用 -->
  2. <script setup lang="ts">
  3. import CourseChapter3dMain from '@/components/student/CourseChapter3dMain.vue';
  4. import { threeWorld } from "@/stores/threeWorld.ts";
  5. import { courseChapter3dShow } from "@/stores/courseChapter3dShow.ts";
  6. import { ref, watch } from "vue";
  7. const props = defineProps({
  8. config : {
  9. type: Object
  10. },
  11. // 获取当前学生任务ID缺陷列表
  12. studentTaskIdList : {
  13. type: Object
  14. },
  15. });
  16. // 定义发射给父组件的方法
  17. const emits = defineEmits([
  18. // 上一步是否显示
  19. 'tipsBtnsUpOpen',
  20. // 下一步是否显示
  21. 'tipsBtnsDownOpen',
  22. ]);
  23. // setInterval(function() {
  24. // console.log(
  25. // "3d场景参数", props.config
  26. // );
  27. // }, 1000);
  28. // 【开发的时候】3d场景,iframe地址
  29. let threeWorldUrl = "3dMain/index.html#/main?id=zidingyi";
  30. // babylon 对象
  31. let BABYLON : any = null;
  32. // // 【发布的时候】3d场景,iframe地址
  33. // // 配置文件地址
  34. // // let commonconfig = "https://www.3dyzt.com/eduTrain/3dMain/model/";
  35. // let commonconfig = "https://www.3dyzt.com/eduTrain/3dMain/model2/";// 正式
  36. // // let commonconfig = "https://www.3dyzt.com/eduTrain/3dMain/model3/";// 临时测试
  37. // // 模型地址
  38. // // let commonmodel = "https://www.3dyzt.com/eduTrain/3dMain/model/";
  39. // let commonmodel = "https://www.3dyzt.com/eduTrain/3dMain/model2/";// 正式
  40. // // let commonmodel = "https://www.3dyzt.com/eduTrain/3dMain/model3/";// 临时测试
  41. // // 发布后的地址
  42. // let threeWorldUrl = "https://www.3dyzt.com/main/index.html#/main?id=zidingyi&user=zidingyi&commonconfig=" + commonconfig + "&commonmodel=" + commonmodel;
  43. /**
  44. * 上一步是否显示
  45. * open true - 显示, false - 隐藏
  46. */
  47. const tipsBtnsUpOpenEvent = (open : Boolean) => {
  48. emits('tipsBtnsUpOpen', open);
  49. }
  50. /**
  51. * 下一步是否显示
  52. * open true - 显示, false - 隐藏
  53. */
  54. const tipsBtnsDownOpenEvent = (open : Boolean) => {
  55. emits('tipsBtnsDownOpen', open);
  56. }
  57. // 记录当前的流程
  58. let thisFlowPath : any = null;
  59. // 记录最后一次播放完成的状态
  60. let thisFlowPathEnd : any = null;
  61. /**
  62. * 监听发送参数变化,流程完成的时候
  63. */
  64. watch(
  65. () => courseChapter3dShow().show.showEndViewBool,
  66. (newVal, oldVal) => {
  67. // console.log(
  68. // newVal, oldVal,
  69. // props.config
  70. // );
  71. // 如果没有加载完成,则不触发后面的逻辑
  72. if (courseChapter3dShow().show.showEndViewBool == false) {
  73. return;
  74. }
  75. if (thisFlowPathEnd != thisFlowPath) {
  76. thisFlowPathEnd = thisFlowPath;
  77. }
  78. }
  79. );
  80. /**
  81. * 监听发送参数变化,来控制切换3d的逻辑
  82. */
  83. watch(
  84. () => props.config,
  85. (newVal, oldVal) => {
  86. // console.log(
  87. // newVal, oldVal,
  88. // props.config
  89. // );
  90. // 如果没有加载完成,则不触发后面的逻辑
  91. if (threeWorld().loadSuccess == false) {
  92. return;
  93. }
  94. typeEvent(props.config?.threeDimensionalConfig);
  95. }
  96. );
  97. /**
  98. * 回调加载百分比
  99. */
  100. const callbackLoadPercentageEvent = (json: { loadPercentage: any; }) => {
  101. // console.log("回调加载百分比", json)
  102. const loadPercentage = parseFloat(json["loadPercentage"]);
  103. // 加载小于百分比
  104. if (loadPercentage < 100) {
  105. // ##############################【这里只做控制】隐藏其他按钮界面等
  106. threeWorld().loadSuccess = false;
  107. goPlayCallbackEventMain();
  108. return
  109. }
  110. }
  111. /**
  112. * 回调加载完成
  113. */
  114. const callbackLoadEndEvent = (json : { 'roleChoiceOpenBool' : true }) => {
  115. // console.log("回调加载完成", json)
  116. // 此时是显示角色的界面
  117. if (json["roleChoiceOpenBool"] === true) {
  118. // ############################## 隐藏其他按钮界面等
  119. threeWorld().loadSuccess = false;
  120. return;
  121. }
  122. // ############################## 这个时候,是真正的进入3d画面了,才显示其他按钮界面等
  123. threeWorld().loadSuccess = true;
  124. }
  125. /**
  126. * 回调距离范围内
  127. */
  128. const callbackDistanceYesEvent = (json : any) => {
  129. // console.log("回调距离范围内", json);
  130. }
  131. /**
  132. * 回调距离范围外
  133. */
  134. const callbackDistanceNoEvent = (json : any) => {
  135. // console.log("回调距离范围外", json);
  136. }
  137. /**
  138. * 回调视角切换完成
  139. */
  140. const callbackCameraVisualAngleJsonToLookEvent = (json : any) => {
  141. // console.log("视角触发完成", json);
  142. }
  143. /**
  144. * 记录出生点
  145. */
  146. const birthPointInit = () => {
  147. courseChapter3dShow().show.birthPoint = {};
  148. // @ts-ignore
  149. for (let i = 0; i < threeWorld().obj.newIframe.objIframe.ChengGuangYuanJing.CommonBirthplace.find().list.length; i++) {
  150. // @ts-ignore
  151. let json = threeWorld().obj.newIframe.objIframe.ChengGuangYuanJing.CommonBirthplace.find().list[i];
  152. // console.log(
  153. // "json", json
  154. // );
  155. // @ts-ignore
  156. courseChapter3dShow().show.birthPoint[json.name] = json;
  157. }
  158. // console.log(
  159. // "courseChapter3dShow().show.birthPoint = {};",
  160. // courseChapter3dShow().show.birthPoint
  161. // );
  162. }
  163. /**
  164. * 记录3d里配置的移动速度
  165. */
  166. const carMoveSpeedInitEvent = () => {
  167. // // @ts-ignore
  168. // let objRoleConfig = threeWorld().obj.newIframe.objIframe.ChengGuangYuanJing.Role.find().roleCodeAllConfig[
  169. // // @ts-ignore
  170. // threeWorld().obj.newIframe.objIframe.ChengGuangYuanJing.Role.find().roleCode
  171. // ];
  172. // @ts-ignore
  173. let roleConfig = threeWorld().obj.newIframe.objIframe.ChengGuangYuanJing.CommonVal.find().roleConfig;
  174. courseChapter3dShow().show.carMoveSpeed = parseFloat("" + roleConfig.speedMove);
  175. // console.log(
  176. // "roleConfig",
  177. // roleConfig,
  178. // "courseChapter3dShow().show.carMoveSpeed",
  179. // courseChapter3dShow().show.carMoveSpeed
  180. // );
  181. }
  182. setInterval(function() {
  183. // 如果没有加载完成,则不触发后面的逻辑
  184. if (threeWorld().loadSuccess == false) {
  185. return;
  186. }
  187. // 更新时间
  188. var cur = new Date(); // 创建当前日期对象 cur
  189. var years = cur.getFullYear(); // 从日期对象cur中取得年数
  190. var months = cur.getMonth() + 1; // 取得月数
  191. var days = cur.getDate(); // 取得天数
  192. var hours = cur.getHours(); // 取得小时数
  193. var minutes = cur.getMinutes(); // 取得分钟数
  194. courseChapter3dShow().show.showCarEyeYears = years;
  195. courseChapter3dShow().show.showCarEyeMonths = months;
  196. courseChapter3dShow().show.showCarEyeDays = days;
  197. courseChapter3dShow().show.showCarEyeHours = hours;
  198. courseChapter3dShow().show.showCarEyeMinutes = minutes;
  199. }, 1000);
  200. /**
  201. * 监听是否点击 进入活动,或者是点击屏幕进入3d场景回调
  202. */
  203. const goPlayCallbackEventMain = () => {
  204. if (
  205. // @ts-ignore
  206. threeWorld().obj.newIframe.objIframe.window["goPlayCallbackEvent"] != null
  207. // @ts-ignore
  208. && threeWorld().obj.newIframe.objIframe.window["goPlayCallbackEvent"] != undefined
  209. ) {
  210. return;
  211. }
  212. // @ts-ignore
  213. threeWorld().obj.newIframe.objIframe.window["goPlayCallbackEvent"] = function(res : any) {
  214. // console.log(
  215. // " ========== 监听是否点击 进入活动,或者是点击屏幕进入3d场景 ========== ",
  216. // res
  217. // );
  218. setTimeout(function() {
  219. initScene();
  220. }, 200);
  221. }
  222. }
  223. /**
  224. * 设置3d事件
  225. */
  226. const threeEvent = () => {
  227. // @ts-ignore
  228. let objScene = threeWorld().obj.newIframe.objIframe.ChengGuangYuanJing.CommonVal.find().objScene;
  229. /**
  230. * 可以被追加事件的API【 可以不断追加进来 】
  231. */
  232. // @ts-ignore
  233. threeWorld().obj.newIframe.objIframe.ChengGuangYuanJing.CommonVal.find().objScene.onPointerObservable.add(
  234. (pointerInfo : any) => {
  235. // console.log(
  236. // " ============================== 鼠标事件触发了 ============================== ",
  237. // pointerInfo.pickInfo,
  238. // pointerInfo.pickInfo.hit
  239. // );
  240. // 当前的步骤状态
  241. let statue = props.config?.threeDimensionalConfig;
  242. if (courseChapter3dShow().show.pickResultMoveBool == true) {
  243. courseChapter3dShow().show.showDirectionViewBool = false;
  244. courseChapter3dShow().show.showClickRotationViewBool = false;
  245. // 获取选择的对象, 通过测试 scene.pick() 方法是非常消耗性能的
  246. let pickResultMove = objScene.pick(
  247. objScene.pointerX,
  248. objScene.pointerY
  249. );
  250. if (pickResultMove.hit) {
  251. // console.log(
  252. // "鼠标移动触发逻辑",
  253. // "pickResultMove", pickResultMove
  254. // );
  255. }
  256. }
  257. // 如果点击到3d世界里的物体的时候
  258. if (pointerInfo.pickInfo.hit) {
  259. var pickResult = pointerInfo.pickInfo;
  260. // console.log(
  261. // " threeEvent = () => { 鼠标事件 》》》》》》》》》》 ", pickResult
  262. // );
  263. // // 获取点击对象的方向【 一定要加上 true,否则载入的获取模型的方向,会有问题 】
  264. // var getNormal = pickResult.getNormal(true);
  265. // console.log({
  266. // '事件' : 'lickType 鼠标事件点击到物体了',
  267. // '被点击到的物体' : pickResult,
  268. // '坐标' : pickResult.pickedPoint,
  269. // '被点击的物体的名字' : pickResult.pickedMesh.name,
  270. // '被点击物体的材质' : pickResult.pickedMesh.material,
  271. // '获取点击对象的方向' : getNormal,
  272. // '得到点击物体的所有点位集合[ 物体都是通过很多点为链接而形成的,可以通过这个方法知道物体的最大尺寸,或者最小尺寸等 ]' : pickResult.pickedMesh._geometry._positions,
  273. // '得到被点击物体的绝对坐标,后续该坐标例如取中心点垂直就很有用处【3d模型如果错位,说明是3d建模把模型给合并导致的,需要跟对方沟通,让模型单独】' : pickResult.pickedMesh._absolutePosition
  274. // });
  275. switch (pointerInfo.type) {
  276. case BABYLON.PointerEventTypes.POINTERDOWN:
  277. // console.log("clickType 鼠标按下");
  278. courseChapter3dShow().show.showDirectionViewBool = false;
  279. courseChapter3dShow().show.showClickRotationViewBool = false;
  280. break;
  281. case BABYLON.PointerEventTypes.POINTERUP:
  282. // console.log("clickType 鼠标弹起");
  283. break;
  284. case BABYLON.PointerEventTypes.POINTERMOVE:
  285. // console.log("clickType 鼠标移动中");
  286. break;
  287. case BABYLON.PointerEventTypes.POINTERWHEEL:
  288. // console.log("clickType 鼠标滚轮滚动中");
  289. break;
  290. case BABYLON.PointerEventTypes.POINTERPICK:
  291. // console.log("clickType 鼠标指针选中事件");
  292. break;
  293. case BABYLON.PointerEventTypes.POINTERTAP:
  294. // console.log("clickType 当对象被触摸并在没有拖动的情况下释放时,触发指针触发事件。");
  295. break;
  296. case BABYLON.PointerEventTypes.POINTERDOUBLETAP:
  297. // console.log("clickType 鼠标双击");
  298. break;
  299. }
  300. }
  301. });
  302. // @ts-ignore 添加帧事件
  303. threeWorld().obj.newIframe.objIframe.ChengGuangYuanJing.CommonVal.find().objScene.onBeforeRenderObservable.add(function() {
  304. });
  305. }
  306. /**
  307. * 3d加载完成优先切换到默认的视角
  308. */
  309. const initScene = () => {
  310. // console.log(
  311. // "initScene = () => {",
  312. // threeWorld().obj.newIframe,
  313. // threeWorld().obj.newIframe.getCameraVisualAngleGetList()
  314. // );
  315. // @ts-ignore
  316. threeWorld().obj.newIframe.cameraVisualAngleGetListNameGpsTo("检测车");
  317. // @ts-ignore
  318. threeWorld().obj.newIframe.roleShow(false);
  319. // 清空3d背景
  320. // @ts-ignore
  321. threeWorld().obj.newIframe.objIframe.ChengGuangYuanJing.CommonVal.find().objScene.clearColor = {
  322. "r" : 0, "g" : 0, "b" : 0, "a" : 0
  323. };
  324. groundNoRemove();
  325. typeEvent(props.config?.threeDimensionalConfig);
  326. // 触发自动点击逻辑
  327. // @ts-ignore
  328. threeWorld().obj.newIframe.objIframe.ChengGuangYuanJing.CommonVal.find().screenClickNumEvent();
  329. }
  330. /**
  331. * 将所有设置为地面的,改为不设置为地面
  332. */
  333. const groundNoRemove = () => {
  334. // @ts-ignore
  335. for (const key in threeWorld().obj.newIframe.objIframe.ChengGuangYuanJing.ModelObjEdit.find().meshListIdToJson) {
  336. if (
  337. // @ts-ignore
  338. threeWorld().obj.newIframe.objIframe.ChengGuangYuanJing.ModelObjEdit.find().meshListIdToJson[key]['isGround'] == true
  339. ) {
  340. // 通过更新材质的参数,来更新
  341. // @ts-ignore
  342. threeWorld().obj.newIframe.objIframe.ChengGuangYuanJing.ModelObjEdit.find().objOneMeshListIdToJsonUpdate(
  343. key,
  344. {
  345. "isGround" : false,
  346. }
  347. );
  348. }
  349. }
  350. // @ts-ignore
  351. for (let i = 0; i < threeWorld().obj.newIframe.objIframe.ChengGuangYuanJing.CommonVal.find().objScene.meshes.length; i++) {
  352. // @ts-ignore
  353. let objMesh = threeWorld().obj.newIframe.objIframe.ChengGuangYuanJing.CommonVal.find().objScene.meshes[i];
  354. // console.log(
  355. // "myIsGround",
  356. // objMesh["myIsGround"]
  357. // );
  358. // if (objMesh["myIsGround"] == true) {
  359. // delete objMesh["myIsGround"];
  360. // }
  361. delete objMesh["myIsGround"];
  362. }
  363. // @ts-ignore
  364. threeWorld().obj.newIframe.objIframe.ChengGuangYuanJing.Role.find().isGroundList = {};
  365. // @ts-ignore
  366. threeWorld().obj.newIframe.objIframe.ChengGuangYuanJing.ModelObjEdit.find().myIsGroundConfigNum = 0;
  367. }
  368. /**
  369. * 根据不同的类型,切换对应的3d逻辑
  370. */
  371. const typeEvent = (type : any) => {
  372. console.log(
  373. "根据不同的类型,切换对应的3d逻辑",
  374. type
  375. );
  376. switch(type) {
  377. // 操作帮助
  378. case 'operationHelp':
  379. break;
  380. // 设备拆解
  381. case 'equipmentDisassembly':
  382. break;
  383. default:
  384. break;
  385. }
  386. }
  387. </script>
  388. <template>
  389. <!-- 3d课程配置:{{ props.config }} -->
  390. <img v-if="courseChapter3dShow().show.threeWorldMask == true" class="threeWorldMask" src="../../assets/student/mask.webp" />
  391. <CourseChapter3dMain
  392. :iframeId="'newIframe'"
  393. :url="threeWorldUrl"
  394. @callbackLoadPercentage="callbackLoadPercentageEvent"
  395. @callbackLoadEnd="callbackLoadEndEvent"
  396. @callbackDistanceYes="callbackDistanceYesEvent"
  397. @callbackDistanceNo="callbackDistanceNoEvent"
  398. @callbackCameraVisualAngleJsonToLook="callbackCameraVisualAngleJsonToLookEvent"
  399. ></CourseChapter3dMain>
  400. </template>
  401. <style scoped>
  402. .threeWorldMask {
  403. position: fixed;
  404. z-index: 1;
  405. width: 100%;
  406. height: 100%;
  407. top: 0px;
  408. left: 0px;
  409. pointer-events: none;
  410. }
  411. </style>