ThreeMap.vue 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. <template>
  2. <div ref="map_container_ref" :class="['three-map', props.className]" :style="{
  3. width: props.width,
  4. height: props.height
  5. }"></div>
  6. </template>
  7. <script setup lang="ts">
  8. import { useInitThree } from "./useInitThree";
  9. import { ref, watch, onMounted, shallowRef, inject,toRaw } from "vue";
  10. import { useGLTFLoader } from "@/hooks/useGLTFLoader";
  11. import {
  12. Group,
  13. Mesh,
  14. MeshBasicMaterial,
  15. Vector3,
  16. SphereGeometry,
  17. Sprite,
  18. TextureLoader,
  19. Texture, RepeatWrapping, Box3
  20. } from "three";
  21. import { useRayCast } from "./useRayCast";
  22. import { coordinate_src, cycle_light_src, Floor } from "./Constants";
  23. import { createImgSprite, createSphere, useConnectPoint } from "@/hooks/useMesh";
  24. import { getRandomIntInclusive, isMesh } from "@/hooks/Utils";
  25. import { connect_point_inject_key } from "../../global";
  26. import { Character } from "../Character/Character";
  27. import { character_src } from "../Character/Constants";
  28. import { PathFinder } from "../PathFinder/PathFinder";
  29. const props = defineProps<{
  30. className: string,
  31. width: string,
  32. height: string,
  33. floorId: string,
  34. }>();
  35. const {
  36. map_conn_points,
  37. graph
  38. } = inject(connect_point_inject_key)!;
  39. // 坐标点texture
  40. const texture_loader = new TextureLoader();
  41. let coordinate_texture: Texture | undefined;
  42. texture_loader.load(coordinate_src, (texture) => {
  43. coordinate_texture = texture;
  44. });
  45. // 连通点texture
  46. let cycle_light_texture: Texture | undefined;
  47. texture_loader.load(cycle_light_src, (texture) => {
  48. cycle_light_texture = texture;
  49. cycle_light_texture.wrapS = RepeatWrapping;
  50. cycle_light_texture.repeat.set(2, 1);
  51. });
  52. const cur_map = shallowRef<Group | null>(null);
  53. const map_file_cache: Record<string, Group> = {};
  54. // 选择的坐标点Mesh(起始点/终点)
  55. const startPoint = shallowRef<Group | null>(null);
  56. const endPoint = shallowRef<Group | null>(null);
  57. let ray_cast: ReturnType<typeof useRayCast> | null = null;
  58. const map_container_ref = ref<HTMLElement>();
  59. const {
  60. scene,
  61. camera,
  62. renderer,
  63. clock,
  64. controls,
  65. renderCSS2D
  66. } = useInitThree(map_container_ref);
  67. const character = new Character(camera, scene, character_src, new Vector3(0, 0, 0));
  68. const path_finder = new PathFinder();
  69. // 连通点
  70. const { renderConnectPoint } = useConnectPoint(scene);
  71. // 切换地图
  72. const setfloorId = async (scene_url: string) => {
  73. if (cur_map.value) {
  74. cur_map.value.traverse(item => {
  75. if (isMesh(item)) {
  76. item.material.dispose();
  77. }
  78. });
  79. clearPointHelper();
  80. scene.remove(cur_map.value);
  81. }
  82. let map: Group;
  83. if (map_file_cache[scene_url]) {
  84. map = map_file_cache[scene_url];
  85. } else {
  86. map = (await useGLTFLoader(Floor[scene_url])).scene;
  87. map_file_cache[scene_url] = map;
  88. }
  89. map.traverse(item => {
  90. console.log(item);
  91. if (isMesh(item) && item.name.includes("NavMesh")) {
  92. item.material.visible = false;
  93. path_finder.setZoneData(props.floorId, item);
  94. }
  95. });
  96. cur_map.value = map;
  97. scene.add(map);
  98. const bbox = new Box3().setFromObject(map);
  99. const center = new Vector3();
  100. bbox.getCenter(center);
  101. // 设置控制器目标点为模型中心
  102. controls.target.copy(center);
  103. camera.position.copy(center).add(new Vector3(0, 60, 50)); //3d视角
  104. controls.update();
  105. // renderConnectPoint(map_conn_points.get(scene_url)!, graph.getAdjList(), cycle_light_texture as Texture);
  106. if (ray_cast) {
  107. if (!ray_cast.intersect.value) {
  108. ray_cast.onRayCast(map, handleMapClick);
  109. } else {
  110. ray_cast.setIntersect(map);
  111. }
  112. }
  113. };
  114. const switch3D = (is3D) => {
  115. const bbox = new Box3().setFromObject(cur_map.value);
  116. const center = new Vector3();
  117. bbox.getCenter(center);
  118. controls.target.copy(center);
  119. if (is3D) {
  120. camera.position.copy(center).add(new Vector3(0, 60, 50)); //3d视角
  121. controls.update();
  122. } else {
  123. camera.position.copy(center).add(new Vector3(0, 75, 0)); //2d视角
  124. controls.update();
  125. }
  126. }
  127. onMounted(() => {
  128. ray_cast = useRayCast(map_container_ref.value as HTMLElement, camera);
  129. watch(() => props.floorId, setfloorId, { immediate: true });
  130. });
  131. const clearPointHelper = () => {
  132. if (startPoint.value) {
  133. // const sphere = select_point.value.getObjectByName("sphere") as Mesh<SphereGeometry, MeshBasicMaterial>;
  134. const sprite = startPoint.value.getObjectByName("sprite") as Sprite;
  135. // sphere.material.dispose();
  136. sprite.material.dispose();
  137. scene.remove(startPoint.value);
  138. startPoint.value = null;
  139. }
  140. if (endPoint.value) {
  141. // const sphere = select_point.value.getObjectByName("sphere") as Mesh<SphereGeometry, MeshBasicMaterial>;
  142. const sprite = endPoint.value.getObjectByName("sprite") as Sprite;
  143. // sphere.material.dispose();
  144. sprite.material.dispose();
  145. scene.remove(endPoint.value);
  146. endPoint.value = null;
  147. character.removeToScene();
  148. character.clearPath();
  149. }
  150. };
  151. const renderPointHelper = (point: Vector3) => {
  152. if (startPoint.value && endPoint.value) clearPointHelper();
  153. const group = new Group();
  154. group.userData.position = point;
  155. // const sphere = createSphere(.1, 0xff9900, point);
  156. const sprite = createImgSprite(
  157. coordinate_texture as Texture,
  158. point,
  159. 0.15,
  160. 0.02
  161. );
  162. // group.add(sphere);
  163. group.add(sprite);
  164. scene.add(group);
  165. if (startPoint.value) {
  166. endPoint.value = group;
  167. insideNavigation(props.floorId, startPoint.value.userData.position, endPoint.value.userData.position);
  168. console.log( startPoint.value.userData.position);
  169. console.log( endPoint.value.userData.position);
  170. } else {
  171. startPoint.value = group;
  172. }
  173. };
  174. // 室内导航方案(会初始化地图)
  175. const insideNavigation = (zone: string, start_pos: Vector3, end_pos: Vector3) => {
  176. // controls.enabled = false;
  177. // character.setPathLineColor(`rgb(${getRandomIntInclusive(0, 255)}, ${getRandomIntInclusive(0, 255)}, ${getRandomIntInclusive(0, 255)})`);
  178. createPath(zone, start_pos, end_pos);
  179. };
  180. // 单地图两点间的导航路径(不会初始化地图)
  181. const createPath = (zone: string, start_pos: Vector3, end_pos: Vector3) => {
  182. const path = path_finder.queryPath(zone, start_pos, end_pos);
  183. character.addToScene(start_pos);
  184. path.unshift(start_pos);
  185. character.setPath(path);
  186. };
  187. const handleMapClick = (point: Vector3) => {
  188. renderPointHelper(point);
  189. };
  190. renderer.setAnimationLoop(() => {
  191. const delta = clock.getDelta();
  192. renderer.render(scene, camera);
  193. if (cycle_light_texture) {
  194. cycle_light_texture.offset.x -= 0.005;
  195. }
  196. renderCSS2D(scene, camera);
  197. });
  198. defineExpose({
  199. startPoint,
  200. clearPointHelper,
  201. switch3D
  202. });
  203. </script>
  204. <style scoped lang="scss">
  205. .three-map {
  206. // border: 1px solid #ccc;
  207. overflow: hidden;
  208. }
  209. </style>