123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301 |
- import { computed, ref, watch, watchEffect } from "vue";
- import {
- installThreeGlobalVar,
- useCamera,
- useContainer,
- useRender,
- useStageProps,
- } from "./use-stage";
- import { OrbitControls } from "three/examples/jsm/Addons.js";
- import { listener } from "@/utils/event";
- import { Intersection, Matrix3, Vector3 } from "three";
- import { mergeFuns } from "@/utils/shared";
- import { useMouseEventRegister } from "./use-event";
- import { useCameraAnimation } from "./use-animation";
- import { getMoveDirectrionByKeys, useFigureMoveCollision } from "./use-move";
- import { useGetIntersectObject } from "./use-getter";
- import { subgroupName } from "../container";
- const useModelControls = () => {
- const container = useContainer();
- const camera = useCamera();
- const _render = useRender();
- const render = () => {
- camera.bus.emit("change");
- _render();
- };
- const controls = new OrbitControls(camera);
- controls.target.set(0, 5, 0);
- controls.enabled = false;
- const unListener = listener(controls as any, "change", render);
- let prevOrigin: Vector3 | null = null;
- let prevDire: Vector3 | null = null;
- watch(
- container,
- (container, _, onCleanup) => {
- if (container) {
- controls.domElement = container;
- controls.connect();
- onCleanup(() => {
- controls.disconnect();
- });
- }
- },
- { immediate: true }
- );
- return {
- controls,
- onDestory() {
- controls.domElement && controls.dispose();
- unListener();
- },
- syncCamera() {
- controls.update();
- },
- disable() {
- prevOrigin = camera.position.clone();
- prevDire = camera.getWorldDirection(new Vector3());
- controls.enabled = false;
- },
- enable() {
- controls.enabled = true;
- },
- get current() {
- return {
- prevOrigin,
- prevDire,
- };
- },
- };
- };
- const roamingEysHeight = 120;
- const useRoamingControls = () => {
- const container = useContainer();
- const camera = useCamera();
- const _render = useRender();
- const render = () => {
- camera.bus.emit("change");
- _render();
- };
- const enabled = ref(false);
- const syncCamera = (direction = camera.getWorldDirection(new Vector3())) => {
- controls.target.copy(direction.add(camera.position));
- controls.update();
- };
- const { direction, onDestory: onDownDestory } = getMoveDirectrionByKeys();
- const move = useFigureMoveCollision(camera, direction, syncCamera);
- move.pause();
- const controls = new OrbitControls(camera);
- controls.rotateSpeed = -0.3;
- controls.enableZoom = false;
- let prevOrigin: Vector3 | null = null;
- let prevDire: Vector3 | null = null;
- return {
- controls,
- onDestory: mergeFuns(
- watchEffect(() => (controls.enabled = enabled.value)),
- watch(
- container,
- (container, _, onCleanup) => {
- if (container) {
- controls.domElement = container;
- controls.connect();
- onCleanup(() => {
- controls.disconnect();
- });
- }
- },
- { immediate: true }
- ),
- move.destory,
- () => {
- controls.domElement && controls.dispose();
- },
- listener(controls as any, "change", render),
- onDownDestory
- ),
- syncCamera,
- disable() {
- prevOrigin = camera.position.clone();
- prevDire = camera.getWorldDirection(new Vector3());
- controls.enabled = false;
- move.pause();
- },
- enable() {
- controls.enabled = true;
- move.continue();
- render();
- },
- get current() {
- return {
- prevOrigin,
- prevDire,
- };
- },
- };
- };
- const controlsFactory = {
- model: useModelControls,
- roaming: useRoamingControls,
- };
- export type ControlsType = keyof typeof controlsFactory;
- export type Controls = ReturnType<(typeof controlsFactory)[ControlsType]>;
- export const useControls = installThreeGlobalVar(() => {
- const container = useContainer();
- const type = ref<ControlsType>();
- const controls = ref<Controls>();
- const controlsMap = {} as Record<ControlsType, Controls>;
- for (const [type, factory] of Object.entries(controlsFactory)) {
- controlsMap[type as ControlsType] = factory();
- }
- const stopWatch = watch(
- [container, type],
- ([container, type], _, onCleanup) => {
- if (!(type && container)) return;
- const ct = controlsMap[type];
- ct.enable();
- controls.value = ct;
- onCleanup(() => {
- ct.disable();
- controls.value = undefined;
- });
- },
- { immediate: true, flush: "sync" }
- );
- return {
- var: { type, value: controls },
- onDestroy: () => {
- stopWatch();
- for (const controls of Object.values(controlsMap)) {
- controls.onDestory();
- }
- },
- };
- });
- export const useFlyRoaming = installThreeGlobalVar(() => {
- const camera = useCamera();
- const { type, value: controls } = useControls();
- const cameraAnimation = useCameraAnimation();
- const getIntersectObject = useGetIntersectObject();
- const normalMatrix = new Matrix3();
-
- const bottom = new Vector3(0, -1, 0);
- return async (point: Intersection | Vector3) => {
- let intersect: Intersection;
- if ("point" in point) {
- intersect = point;
- } else {
- point = point.clone();
- const objects = getIntersectObject(point, bottom);
- if (!objects.length) {
- throw "当前位置无法漫游";
- }
- intersect = objects[0];
- }
- const normal = intersect.face?.normal.clone();
- if (!normal) {
- throw "当前位置无法漫游";
- }
- normalMatrix.getNormalMatrix(intersect.object.matrixWorld);
- normal.applyMatrix3(normalMatrix).normalize();
- if (normal.y < 0.8) {
- throw "当前位置无法漫游";
- }
- if (type.value !== 'roaming') {
- type.value = undefined
- } else {
- controls.value?.disable()
- }
- const position = intersect.point
- .clone()
- .add({ x: 0, y: roamingEysHeight, z: 0 });
- const direction = camera.getWorldDirection(new Vector3());
- const target = position.clone().add(direction);
- await cameraAnimation(position, target);
- type.value = "roaming";
- controls.value?.enable()
- controls.value?.syncCamera();
- };
- });
- export const useFlyModel = installThreeGlobalVar(() => {
- const camera = useCamera();
- const { type, value: controls } = useControls();
- const cameraAnimation = useCameraAnimation();
- return async (set: { point?: Vector3; direction?: Vector3 } = {}) => {
- type.value = "model";
- const prev = controls.value?.current;
- if (!set.point) {
- set.point =
- prev?.prevOrigin ||
- camera.position.clone() ||
- new Vector3(400, 400, 400);
- }
- if (!set.direction) {
- set.direction =
- prev?.prevDire ||
- camera.getWorldDirection(new Vector3()) ||
- camera.getWorldDirection(new Vector3());
- }
- const direction = set.direction;
- const target = set.point.clone().add(direction);
- await cameraAnimation(set.point, target);
- controls.value?.syncCamera();
- };
- });
- export const installAutoSwitchControls = installThreeGlobalVar(() => {
- const { type } = useControls();
- const mouseRegister = useMouseEventRegister();
- const flyRoaming = useFlyRoaming();
- const flyModel = useFlyModel();
- const sProps = useStageProps();
- const canFlyTypes = ["line", "icon"] as const;
- const canFlyNames = computed(() => {
- const names = canFlyTypes.flatMap((type) =>
- sProps.value.draw.store.getTypeItems(type).map((item) => item.id)
- )
- names.push(subgroupName)
- return names
- });
- const onDestroy = mergeFuns(
- listener(document.documentElement, "keydown", (ev) => {
- if (ev.key === "Escape" && type.value !== "model") {
- flyModel();
- }
- }),
- watchEffect((onCleanup) => {
- const cleanups = canFlyNames.value.map((name) =>
- mouseRegister(name, "dblclick", (object) => {
- flyRoaming(object);
- })
- );
- onCleanup(mergeFuns(cleanups))
- })
- );
- type.value = "model";
- return { onDestroy };
- });
|