Three.js Editor右下角视角指示器
官方editor的视角指示器import * as THREE from 'three'nnexport class ViewHelper extends THREE.Object3D {n edito
import * as THREE from 'three'nnexport class ViewHelper extends THREE.Object3D {n editorCamera: THREE.Cameran private readonly container: HTMLElementnn private readonly panel: HTMLElementn private readonly point = new THREE.Vector3()n private readonly dim = 128nn private readonly posXAxisHelper: THREE.Spriten private readonly posYAxisHelper: THREE.Spriten private readonly posZAxisHelper: THREE.Spriten private readonly negXAxisHelper: THREE.Spriten private readonly negYAxisHelper: THREE.Spriten private readonly negZAxisHelper: THREE.Spritenn private readonly camera: THREE.OrthographicCameran private readonly interactiveObjects: THREE.Object3D[]n private readonly raycaster = new THREE.Raycaster()n private readonly mouse = new THREE.Vector2()nn constructor(editorCamera: THREE.Camera, container: HTMLElement) {n super()nn this.container = containern this.editorCamera = editorCamerann const panel = document.createElement('div')n panel.style.position = 'absolute'n panel.style.right = '0px'n panel.style.bottom = '0px'n panel.style.height = '128px'n panel.style.width = '128px'n panel.addEventListener('mouseup', event => {n event.stopPropagation()n event.preventDefault()n this.handleClick(event)n })n panel.addEventListener('mousedown', (event) => {n event.stopPropagation()n })n container.appendChild(panel)n this.panel = panelnn const color1 = new THREE.Color('#ff3653')n const color2 = new THREE.Color('#8adb00')n const color3 = new THREE.Color('#2c8fff')nn const interactiveObjects: THREE.Object3D[] = []n const camera = new THREE.OrthographicCamera(-2, 2, 2, -2, 0, 4)n camera.position.set(0, 0, 2)n this.camera = camerann const geometry = new THREE.BoxGeometry(0.8, 0.05, 0.05).translate(0.4, 0, 0)nn const xAxis = new THREE.Mesh(geometry, getAxisMaterial(color1))n const yAxis = new THREE.Mesh(geometry, getAxisMaterial(color2))n const zAxis = new THREE.Mesh(geometry, getAxisMaterial(color3))nn yAxis.rotation.z = Math.PI / 2n zAxis.rotation.y = -Math.PI / 2nn this.add(xAxis)n this.add(zAxis)n this.add(yAxis)nn const posXAxisHelper = new THREE.Sprite(getSpriteMaterial(color1, 'X'))n posXAxisHelper.userData.type = 'posX'n const posYAxisHelper = new THREE.Sprite(getSpriteMaterial(color2, 'Y'))n posYAxisHelper.userData.type = 'posY'n const posZAxisHelper = new THREE.Sprite(getSpriteMaterial(color3, 'Z'))n posZAxisHelper.userData.type = 'posZ'n const negXAxisHelper = new THREE.Sprite(getSpriteMaterial(color1))n negXAxisHelper.userData.type = 'negX'n const negYAxisHelper = new THREE.Sprite(getSpriteMaterial(color2))n negYAxisHelper.userData.type = 'negY'n const negZAxisHelper = new THREE.Sprite(getSpriteMaterial(color3))n negZAxisHelper.userData.type = 'negZ'nn posXAxisHelper.position.x = 1n posYAxisHelper.position.y = 1n posZAxisHelper.position.z = 1n negXAxisHelper.position.x = -1n negXAxisHelper.scale.setScalar(0.8)n negYAxisHelper.position.y = -1n negYAxisHelper.scale.setScalar(0.8)n negZAxisHelper.position.z = -1n negZAxisHelper.scale.setScalar(0.8)nn this.add(posXAxisHelper)n this.add(posYAxisHelper)n this.add(posZAxisHelper)n this.add(negXAxisHelper)n this.add(negYAxisHelper)n this.add(negZAxisHelper)nn interactiveObjects.push(posXAxisHelper)n interactiveObjects.push(posYAxisHelper)n interactiveObjects.push(posZAxisHelper)n interactiveObjects.push(negXAxisHelper)n interactiveObjects.push(negYAxisHelper)n interactiveObjects.push(negZAxisHelper)nn this.posXAxisHelper = posXAxisHelpern this.posYAxisHelper = posYAxisHelpern this.posZAxisHelper = posZAxisHelpern this.negXAxisHelper = negXAxisHelpern this.negYAxisHelper = negYAxisHelpern this.negZAxisHelper = negZAxisHelpern this.interactiveObjects = interactiveObjectsn }nn render(renderer: THREE.WebGLRenderer) {n this.quaternion.copy(this.editorCamera.quaternion).invert()n this.updateMatrixWorld()nn const point = this.pointn point.set(0, 0, 1)n point.applyQuaternion(this.editorCamera.quaternion)nn if (point.x >= 0) {n this.posXAxisHelper.material.opacity = 1n this.negXAxisHelper.material.opacity = 0.5n } else {n this.posXAxisHelper.material.opacity = 0.5n this.negXAxisHelper.material.opacity = 1n }nn if (point.y >= 0) {n this.posYAxisHelper.material.opacity = 1n this.negYAxisHelper.material.opacity = 0.5n } else {n this.posYAxisHelper.material.opacity = 0.5n this.negYAxisHelper.material.opacity = 1n }nn if (point.z >= 0) {n this.posZAxisHelper.material.opacity = 1n this.negZAxisHelper.material.opacity = 0.5n } else {n this.posZAxisHelper.material.opacity = 0.5n this.negZAxisHelper.material.opacity = 1n }nn const x = this.container.offsetWidth - this.dimn const v4 = renderer.getViewport(new THREE.Vector4())n renderer.setViewport(x, 0, this.dim, this.dim)n renderer.render(this, this.camera)n renderer.setViewport(v4)n }nn handleClick(event: MouseEvent) {n this.mouse.x = (event.offsetX / this.dim) * 2 - 1n this.mouse.y = -(event.offsetY / this.dim) * 2 + 1nn this.raycaster.setFromCamera(this.mouse, this.camera)nn const intersects = this.raycaster.intersectObjects(this.interactiveObjects)nn if (intersects.length === 0) {n returnn }nn switch (intersects[0].object.userData.type) {n case 'posX':n console.log('posX')n breaknn case 'posY':n breaknn case 'posZ':n breaknn case 'negX':n breaknn case 'negY':n breaknn case 'negZ':n breaknn default:n console.error('ViewHelper: Invalid axis.')n }n }n}nnfunction getAxisMaterial(color: THREE.Color) {n return new THREE.MeshBasicMaterial({n color: color,n toneMapped: falsen })n}nnfunction getSpriteMaterial(color: THREE.Color, text?: string) {n const canvas = document.createElement('canvas')n canvas.width = 64n canvas.height = 64nn const context = canvas.getContext('2d')!n context.beginPath()n context.arc(32, 32, 16, 0, 2 * Math.PI)n context.closePath()n context.fillStyle = color.getStyle()n context.fill()nn if (text) {n context.font = '24px Arial'n context.textAlign = 'center'n context.fillStyle = '#000000'n context.fillText(text, 32, 41)n }nn const texture = new THREE.CanvasTexture(canvas)nn return new THREE.SpriteMaterial({n map: texture,n toneMapped: falsen })n}