@@ -133,6 +133,14 @@ const SGS_VISUAL_RENDER_CONFIG = {
|
|||||||
}
|
}
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
|
const OVERVIEW_INITIAL_CAMERA_PARAMS = {
|
||||||
|
position: new THREE.Vector3(496.3356, 708.6058, 137.501),
|
||||||
|
target: new THREE.Vector3(-4.1432, -31.3659, 3.3982),
|
||||||
|
zoom: 1
|
||||||
|
} as const
|
||||||
|
|
||||||
|
const MODEL_ADJUST_IDLE_REPORT_DELAY_MS = 800
|
||||||
|
|
||||||
const degToRad = (degrees: number) => THREE.MathUtils.degToRad(degrees)
|
const degToRad = (degrees: number) => THREE.MathUtils.degToRad(degrees)
|
||||||
|
|
||||||
const INTERACTION_POLICY = {
|
const INTERACTION_POLICY = {
|
||||||
@@ -347,14 +355,14 @@ const isLoading = ref(true)
|
|||||||
const loadError = ref(false)
|
const loadError = ref(false)
|
||||||
const loadingProgress = ref(0)
|
const loadingProgress = ref(0)
|
||||||
const loadingMessage = ref('正在读取馆内导览资源...')
|
const loadingMessage = ref('正在读取馆内导览资源...')
|
||||||
const activeView = ref<ViewMode>(props.initialView)
|
|
||||||
const currentFloor = ref(props.initialFloorId)
|
|
||||||
const loadingDisplayMessage = '正在加载馆内三维场景'
|
const loadingDisplayMessage = '正在加载馆内三维场景'
|
||||||
const modelLoadErrorTitle = '馆内三维场景加载失败'
|
const modelLoadErrorTitle = '馆内三维场景加载失败'
|
||||||
const modelLoadErrorMessage = '可能是网络原因,请重新加载。'
|
const modelLoadErrorMessage = '可能是网络原因,请重新加载。'
|
||||||
const setFriendlyModelLoadError = () => {
|
const setFriendlyModelLoadError = () => {
|
||||||
setProgress(0, modelLoadErrorMessage)
|
setProgress(0, modelLoadErrorMessage)
|
||||||
}
|
}
|
||||||
|
const activeView = ref<ViewMode>(props.initialView)
|
||||||
|
const currentFloor = ref(props.initialFloorId)
|
||||||
const selectedPOI = ref<RenderPoi | null>(null)
|
const selectedPOI = ref<RenderPoi | null>(null)
|
||||||
const activeFocusPoiId = ref('')
|
const activeFocusPoiId = ref('')
|
||||||
const floorIndex = ref<FloorIndexItem[]>([])
|
const floorIndex = ref<FloorIndexItem[]>([])
|
||||||
@@ -428,6 +436,8 @@ let floorExitZoomOutAccumulatedRatio = 0
|
|||||||
let autoSwitchTemporarilyDisabled = false
|
let autoSwitchTemporarilyDisabled = false
|
||||||
let autoSwitchDisableTimer: ReturnType<typeof setTimeout> | null = null
|
let autoSwitchDisableTimer: ReturnType<typeof setTimeout> | null = null
|
||||||
let overviewEntryIntentStartedAt = 0
|
let overviewEntryIntentStartedAt = 0
|
||||||
|
let modelAdjustReportTimer: ReturnType<typeof setTimeout> | null = null
|
||||||
|
let hasPendingManualModelAdjustment = false
|
||||||
let overviewEntryStartDistance = 0
|
let overviewEntryStartDistance = 0
|
||||||
let overviewEntryZoomInRounds = 0
|
let overviewEntryZoomInRounds = 0
|
||||||
let overviewWheelZoomInRounds = 0
|
let overviewWheelZoomInRounds = 0
|
||||||
@@ -539,6 +549,57 @@ const logThreeMapDiagnostic = (
|
|||||||
console.debug(`[ThreeMap] ${event} ${stringifyDiagnosticPayload(payload)}`)
|
console.debug(`[ThreeMap] ${event} ${stringifyDiagnosticPayload(payload)}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const roundModelAdjustValue = (value: number) => Number(value.toFixed(4))
|
||||||
|
|
||||||
|
const serializeVector3 = (value: THREE.Vector3) => ({
|
||||||
|
x: roundModelAdjustValue(value.x),
|
||||||
|
y: roundModelAdjustValue(value.y),
|
||||||
|
z: roundModelAdjustValue(value.z)
|
||||||
|
})
|
||||||
|
|
||||||
|
const serializeEulerDegrees = (value: THREE.Euler) => ({
|
||||||
|
x: roundModelAdjustValue(THREE.MathUtils.radToDeg(value.x)),
|
||||||
|
y: roundModelAdjustValue(THREE.MathUtils.radToDeg(value.y)),
|
||||||
|
z: roundModelAdjustValue(THREE.MathUtils.radToDeg(value.z))
|
||||||
|
})
|
||||||
|
|
||||||
|
const clearModelAdjustReportTimer = () => {
|
||||||
|
if (!modelAdjustReportTimer) return
|
||||||
|
clearTimeout(modelAdjustReportTimer)
|
||||||
|
modelAdjustReportTimer = null
|
||||||
|
}
|
||||||
|
|
||||||
|
const emitModelAdjustIdleReport = () => {
|
||||||
|
modelAdjustReportTimer = null
|
||||||
|
if (!hasPendingManualModelAdjustment || !camera || !controls || !activeModel) return
|
||||||
|
|
||||||
|
hasPendingManualModelAdjustment = false
|
||||||
|
const payload = {
|
||||||
|
view: activeView.value,
|
||||||
|
floorId: currentFloor.value,
|
||||||
|
modelName: activeModel.name || 'unnamed-model',
|
||||||
|
model: {
|
||||||
|
position: serializeVector3(activeModel.position),
|
||||||
|
rotationDeg: serializeEulerDegrees(activeModel.rotation),
|
||||||
|
scale: serializeVector3(activeModel.scale)
|
||||||
|
},
|
||||||
|
camera: {
|
||||||
|
position: serializeVector3(camera.position),
|
||||||
|
target: serializeVector3(controls.target),
|
||||||
|
distance: roundModelAdjustValue(controls.getDistance()),
|
||||||
|
zoom: roundModelAdjustValue(camera.zoom)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.info('[ThreeMap] 模型停止移动,当前调参信息:', payload)
|
||||||
|
}
|
||||||
|
|
||||||
|
const scheduleModelAdjustIdleReport = () => {
|
||||||
|
if (!hasPendingManualModelAdjustment || isProgrammaticCameraChange || !camera || !controls || !activeModel) return
|
||||||
|
clearModelAdjustReportTimer()
|
||||||
|
modelAdjustReportTimer = window.setTimeout(emitModelAdjustIdleReport, MODEL_ADJUST_IDLE_REPORT_DELAY_MS)
|
||||||
|
}
|
||||||
|
|
||||||
const getInteractionPolicyMode = (): InteractionPolicyMode => (
|
const getInteractionPolicyMode = (): InteractionPolicyMode => (
|
||||||
activeView.value === 'overview' ? 'display' : 'explore'
|
activeView.value === 'overview' ? 'display' : 'explore'
|
||||||
)
|
)
|
||||||
@@ -572,11 +633,12 @@ const shouldEnableRotation = () => (
|
|||||||
|
|
||||||
const shouldUseDirectMouseRotate = (event?: PointerEvent) => {
|
const shouldUseDirectMouseRotate = (event?: PointerEvent) => {
|
||||||
const hasAlt = event?.altKey ?? desktopRotateModifiers.alt
|
const hasAlt = event?.altKey ?? desktopRotateModifiers.alt
|
||||||
const hasShiftCtrlOrMeta = event
|
const hasCtrlOrMeta = event
|
||||||
? event.shiftKey || event.ctrlKey || event.metaKey
|
? event.ctrlKey || event.metaKey
|
||||||
: desktopRotateModifiers.shift || desktopRotateModifiers.ctrl || desktopRotateModifiers.meta
|
: desktopRotateModifiers.ctrl || desktopRotateModifiers.meta
|
||||||
|
const hasShift = event?.shiftKey ?? desktopRotateModifiers.shift
|
||||||
|
|
||||||
return hasAlt && !hasShiftCtrlOrMeta
|
return (hasCtrlOrMeta || hasAlt) && !hasShift
|
||||||
}
|
}
|
||||||
|
|
||||||
const resetInteractionGateState = () => {
|
const resetInteractionGateState = () => {
|
||||||
@@ -600,6 +662,7 @@ const syncControlInteractionOptions = (event?: PointerEvent) => {
|
|||||||
const policy = INTERACTION_POLICY[getInteractionPolicyMode()]
|
const policy = INTERACTION_POLICY[getInteractionPolicyMode()]
|
||||||
const enableRotate = shouldEnableRotation()
|
const enableRotate = shouldEnableRotation()
|
||||||
const enablePan = policy.allowPan && !enableRotate
|
const enablePan = policy.allowPan && !enableRotate
|
||||||
|
const directMouseRotate = enableRotate && shouldUseDirectMouseRotate(event)
|
||||||
|
|
||||||
controls.enableRotate = enableRotate
|
controls.enableRotate = enableRotate
|
||||||
controls.enablePan = enablePan
|
controls.enablePan = enablePan
|
||||||
@@ -612,14 +675,18 @@ const syncControlInteractionOptions = (event?: PointerEvent) => {
|
|||||||
controls.maxAzimuthAngle = policy.maxAzimuthAngle
|
controls.maxAzimuthAngle = policy.maxAzimuthAngle
|
||||||
|
|
||||||
controls.mouseButtons.LEFT = enableRotate
|
controls.mouseButtons.LEFT = enableRotate
|
||||||
? shouldUseDirectMouseRotate(event)
|
? directMouseRotate
|
||||||
? THREE.MOUSE.ROTATE
|
? THREE.MOUSE.ROTATE
|
||||||
: THREE.MOUSE.PAN
|
: THREE.MOUSE.PAN
|
||||||
: enablePan
|
: enablePan
|
||||||
? THREE.MOUSE.PAN
|
? THREE.MOUSE.PAN
|
||||||
: null
|
: null
|
||||||
controls.mouseButtons.MIDDLE = THREE.MOUSE.DOLLY
|
controls.mouseButtons.MIDDLE = THREE.MOUSE.DOLLY
|
||||||
controls.mouseButtons.RIGHT = enablePan ? THREE.MOUSE.PAN : null
|
controls.mouseButtons.RIGHT = directMouseRotate
|
||||||
|
? THREE.MOUSE.ROTATE
|
||||||
|
: enablePan
|
||||||
|
? THREE.MOUSE.PAN
|
||||||
|
: null
|
||||||
controls.touches.ONE = enablePan ? THREE.TOUCH.PAN : null
|
controls.touches.ONE = enablePan ? THREE.TOUCH.PAN : null
|
||||||
controls.touches.TWO = THREE.TOUCH.DOLLY_ROTATE
|
controls.touches.TWO = THREE.TOUCH.DOLLY_ROTATE
|
||||||
}
|
}
|
||||||
@@ -1437,6 +1504,7 @@ const refreshPoiVisibilityByDistance = () => {
|
|||||||
const handleControlChange = () => {
|
const handleControlChange = () => {
|
||||||
updatePoiVisibilityByDistance()
|
updatePoiVisibilityByDistance()
|
||||||
checkAutoSwitch()
|
checkAutoSwitch()
|
||||||
|
scheduleModelAdjustIdleReport()
|
||||||
}
|
}
|
||||||
|
|
||||||
const clearProgrammaticCameraTimer = () => {
|
const clearProgrammaticCameraTimer = () => {
|
||||||
@@ -1567,6 +1635,10 @@ const updatePoiTapFeedback = (now: number) => {
|
|||||||
|
|
||||||
const handleControlStart = () => {
|
const handleControlStart = () => {
|
||||||
activeControlGestureStartedAt = Date.now()
|
activeControlGestureStartedAt = Date.now()
|
||||||
|
if (!isProgrammaticCameraChange) {
|
||||||
|
hasPendingManualModelAdjustment = true
|
||||||
|
clearModelAdjustReportTimer()
|
||||||
|
}
|
||||||
cancelCameraTween({ manual: true })
|
cancelCameraTween({ manual: true })
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1577,6 +1649,7 @@ const handleControlEnd = () => {
|
|||||||
}
|
}
|
||||||
activeControlGestureStartedAt = 0
|
activeControlGestureStartedAt = 0
|
||||||
updateLastControlDistance()
|
updateLastControlDistance()
|
||||||
|
scheduleModelAdjustIdleReport()
|
||||||
}
|
}
|
||||||
|
|
||||||
const setProgress = (progress: number, message: string) => {
|
const setProgress = (progress: number, message: string) => {
|
||||||
@@ -3375,6 +3448,21 @@ const fitCameraToObject = (object: THREE.Object3D, preset: CameraPreset = 'obliq
|
|||||||
setCameraView(center, maxDim, direction, overviewFitOptions)
|
setCameraView(center, maxDim, direction, overviewFitOptions)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const applyOverviewInitialCamera = () => {
|
||||||
|
if (!camera || !controls) return
|
||||||
|
|
||||||
|
camera.position.copy(OVERVIEW_INITIAL_CAMERA_PARAMS.position)
|
||||||
|
camera.zoom = OVERVIEW_INITIAL_CAMERA_PARAMS.zoom
|
||||||
|
camera.updateProjectionMatrix()
|
||||||
|
controls.target.copy(OVERVIEW_INITIAL_CAMERA_PARAMS.target)
|
||||||
|
controls.update()
|
||||||
|
|
||||||
|
const distance = camera.position.distanceTo(controls.target)
|
||||||
|
if (Number.isFinite(distance)) {
|
||||||
|
lastControlDistance = distance
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const resetCamera = () => {
|
const resetCamera = () => {
|
||||||
if (activeModel) {
|
if (activeModel) {
|
||||||
fitCameraToObject(activeModel)
|
fitCameraToObject(activeModel)
|
||||||
@@ -3492,7 +3580,7 @@ const loadOverview = async () => {
|
|||||||
applyModelVisibilityForView(activeModel, 'overview')
|
applyModelVisibilityForView(activeModel, 'overview')
|
||||||
cacheAutoSwitchOverviewMaxDim(activeModel)
|
cacheAutoSwitchOverviewMaxDim(activeModel)
|
||||||
targetScene.add(activeModel)
|
targetScene.add(activeModel)
|
||||||
fitCameraToObject(activeModel)
|
applyOverviewInitialCamera()
|
||||||
loadCurrentFloorPoiMarkersInBackground(loadToken)
|
loadCurrentFloorPoiMarkersInBackground(loadToken)
|
||||||
resetAutoSwitchDistanceTracking()
|
resetAutoSwitchDistanceTracking()
|
||||||
refreshPoiVisibilityByDistance()
|
refreshPoiVisibilityByDistance()
|
||||||
@@ -3523,7 +3611,7 @@ const loadOverview = async () => {
|
|||||||
cachedOverviewModel = activeModel
|
cachedOverviewModel = activeModel
|
||||||
cachedSharedModelUrl = packageData.overviewModelUrl
|
cachedSharedModelUrl = packageData.overviewModelUrl
|
||||||
targetScene.add(activeModel)
|
targetScene.add(activeModel)
|
||||||
fitCameraToObject(activeModel)
|
applyOverviewInitialCamera()
|
||||||
loadCurrentFloorPoiMarkersInBackground(loadToken)
|
loadCurrentFloorPoiMarkersInBackground(loadToken)
|
||||||
resetAutoSwitchDistanceTracking()
|
resetAutoSwitchDistanceTracking()
|
||||||
refreshPoiVisibilityByDistance()
|
refreshPoiVisibilityByDistance()
|
||||||
@@ -5090,6 +5178,8 @@ const disposeScene = () => {
|
|||||||
|
|
||||||
clearOverviewWheelAutoSwitchTimer()
|
clearOverviewWheelAutoSwitchTimer()
|
||||||
clearProgrammaticCameraTimer()
|
clearProgrammaticCameraTimer()
|
||||||
|
clearModelAdjustReportTimer()
|
||||||
|
hasPendingManualModelAdjustment = false
|
||||||
cameraTween = null
|
cameraTween = null
|
||||||
isProgrammaticCameraChange = false
|
isProgrammaticCameraChange = false
|
||||||
lastControlDistance = 0
|
lastControlDistance = 0
|
||||||
|
|||||||
Reference in New Issue
Block a user