/Users/song/Code/threejs_learn_vanilla_class_singleton/threejs_learn_vanilla_ts_class_singleton/src/main.ts
| import "./style.css"; |
| |
| import Base from "./threejs/Base"; |
| |
| ((document) => { |
| const oApp = document.getElementById("app")!; |
| const oCanvas = document.createElement("canvas"); |
| oCanvas.style.position = "absolute"; |
| Base.getInstance(oCanvas); |
| oApp.appendChild(oCanvas); |
| })(document); |
/Users/song/Code/threejs_learn_vanilla_class_singleton/threejs_learn_vanilla_ts_class_singleton/src/main_transform控制器.ts
| import "./style.css"; |
| |
| import Base from "./threejs/Base"; |
| |
| ((document) => { |
| const oApp = document.getElementById("app")!; |
| const oCanvas = document.createElement("canvas"); |
| oCanvas.style.position = "absolute"; |
| Base.getInstance(oCanvas); |
| oApp.appendChild(oCanvas); |
| })(document); |
/Users/song/Code/threejs_learn_vanilla_class_singleton/threejs_learn_vanilla_ts_class_singleton/src/main_单例模式_getInstance.ts
| import "./style.css"; |
| |
| import Base from "./threejs/Base"; |
| |
| ((document) => { |
| const oApp = document.getElementById("app")!; |
| const oCanvas = document.createElement("canvas"); |
| oCanvas.style.position = "absolute"; |
| Base.getInstance(oCanvas); |
| oApp.appendChild(oCanvas); |
| })(document); |
/Users/song/Code/threejs_learn_vanilla_class_singleton/threejs_learn_vanilla_ts_class_singleton/src/main_全局事件管理.ts
| import "./style.css"; |
| |
| import Base from "./threejs/Base"; |
| |
| ((document) => { |
| const oApp = document.getElementById("app")!; |
| const oCanvas = document.createElement("canvas"); |
| oCanvas.style.position = "absolute"; |
| Base.getInstance(oCanvas); |
| oApp.appendChild(oCanvas); |
| })(document); |
/Users/song/Code/threejs_learn_vanilla_class_singleton/threejs_learn_vanilla_ts_class_singleton/src/main_单例模式_constructor.ts
| import "./style.css"; |
| |
| import Base from "./threejs/Base"; |
| |
| ((document) => { |
| const oApp = document.getElementById("app")!; |
| const oCanvas = document.createElement("canvas"); |
| new Base(oCanvas); |
| oApp.appendChild(oCanvas); |
| })(document); |
/Users/song/Code/threejs_learn_vanilla_class_singleton/threejs_learn_vanilla_ts_class_singleton/src/main_css2dlabel.ts
| import "./style.css"; |
| |
| import Base from "./threejs/Base"; |
| |
| ((document) => { |
| const oApp = document.getElementById("app")!; |
| const oCanvas = document.createElement("canvas"); |
| oCanvas.style.position = "absolute"; |
| Base.getInstance(oCanvas); |
| oApp.appendChild(oCanvas); |
| })(document); |
| import { Light, Object3D, Scene, Vector2 } from "three"; |
| |
| import Camera from "./Cameras"; |
| import Helper from "./Helpers"; |
| import Renderer from "./Renderer"; |
| import Size from "./Size"; |
| import Time from "./Time"; |
| import Box from "./World/Box"; |
| import Control from "./Controls"; |
| import Lights from "./Lights"; |
| |
| export default class Base { |
| public static base: Base; |
| public canvas: HTMLCanvasElement; |
| public scene: Scene; |
| public camera: Camera; |
| public size: Size; |
| public time: Time; |
| public renderer: Renderer; |
| public control: Control; |
| public helper: Helper; |
| public noTransfromControlObjs: Object3D[] = []; |
| |
| constructor(canvas: HTMLCanvasElement) { |
| Base.base = this; |
| |
| this.canvas = canvas as HTMLCanvasElement; |
| this.scene = new Scene(); |
| this.size = new Size(); |
| this.camera = new Camera(); |
| this.time = new Time(); |
| this.renderer = new Renderer(); |
| new Lights(); |
| |
| |
| const helper = new Helper(); |
| this.helper = helper; |
| this.noTransfromControlObjs.push(helper.gridHelper, helper.axesHelper); |
| |
| |
| const control = new Control(); |
| this.control = control; |
| this.noTransfromControlObjs.push(this.control.transformControl); |
| |
| |
| new Box(); |
| |
| this.time.on("update", () => { |
| this.update(); |
| }); |
| this.size.on("resize", () => { |
| this.resize(); |
| }); |
| } |
| |
| public static getInstance(canvas?: HTMLCanvasElement) { |
| if (canvas === undefined && Base.base !== undefined) { |
| return Base.base; |
| } |
| Base.base = new Base(canvas as HTMLCanvasElement); |
| return Base.base; |
| } |
| |
| private resize() { |
| this.camera.resize(); |
| this.renderer.resize(); |
| } |
| |
| private update() { |
| this.control.update(); |
| this.renderer.update(); |
| } |
| } |
| import { AxesHelper, GridHelper } from "three"; |
| import Base from "./Base"; |
| |
| export default class { |
| public axesHelper: AxesHelper; |
| public gridHelper: GridHelper; |
| constructor() { |
| const base = Base.getInstance(); |
| const axesHelper = new AxesHelper(5); |
| this.axesHelper = axesHelper; |
| base.scene.add(axesHelper); |
| |
| const size = 10; |
| const divisions = 10; |
| |
| const gridHelper = new GridHelper(size, divisions); |
| this.gridHelper = gridHelper; |
| base.scene.add(gridHelper); |
| } |
| } |
| import { AmbientLight, DirectionalLight } from "three"; |
| import Base from "./Base"; |
| |
| export default class Environment { |
| private sunLight: DirectionalLight; |
| private ambientLight: AmbientLight; |
| constructor() { |
| const base = Base.getInstance(); |
| this.sunLight = new DirectionalLight("#ffffff", 2); |
| this.sunLight.castShadow = true; |
| this.sunLight.shadow.camera.far = 20; |
| this.sunLight.shadow.mapSize.set(2048, 2048); |
| this.sunLight.shadow.normalBias = 0.05; |
| |
| this.sunLight.position.set(-1.5, 7, 10); |
| base.scene.add(this.sunLight); |
| |
| this.ambientLight = new AmbientLight("#ffffff", 0.1); |
| base.scene.add(this.ambientLight); |
| } |
| } |
| import { PerspectiveCamera, Scene } from "three"; |
| import { OrbitControls } from "three/examples/jsm/controls/OrbitControls"; |
| import Base from "./Base"; |
| import type Size from "./Size"; |
| |
| export default class Camera { |
| private size: Size; |
| private scene: Scene; |
| private canvas: HTMLCanvasElement; |
| public perspectiveCamera: PerspectiveCamera; |
| |
| |
| constructor() { |
| const base = Base.getInstance(); |
| this.size = base.size; |
| this.scene = base.scene; |
| this.canvas = base.canvas; |
| |
| this.perspectiveCamera = this.createPerspectiveCamera(); |
| this.scene.add(this.perspectiveCamera); |
| } |
| private createPerspectiveCamera() { |
| const perspectiveCamera = new PerspectiveCamera( |
| 35, |
| this.size.aspect, |
| 0.1, |
| 1000 |
| ); |
| perspectiveCamera.position.set(15, 30, 20); |
| return perspectiveCamera; |
| } |
| |
| public resize() { |
| |
| this.perspectiveCamera.aspect = this.size.aspect; |
| this.perspectiveCamera.updateProjectionMatrix(); |
| } |
| } |
| import EventEmitter from "eventemitter2"; |
| export default class Size extends EventEmitter { |
| public width: number; |
| public height: number; |
| public aspect: number; |
| public pixelRatio: number; |
| |
| constructor() { |
| super(); |
| this.width = window.innerWidth; |
| this.height = window.innerHeight; |
| this.aspect = this.width / this.height; |
| this.pixelRatio = Math.min(window.devicePixelRatio, 2); |
| |
| window.addEventListener("resize", () => { |
| this.width = window.innerWidth; |
| this.height = window.innerHeight; |
| this.aspect = this.width / this.height; |
| this.pixelRatio = Math.min(window.devicePixelRatio, 2); |
| this.emit("resize"); |
| }); |
| } |
| } |
| import * as THREE from "three"; |
| import Base from "./Base"; |
| import { Scene, WebGLRenderer } from "three"; |
| import Camera from "./Cameras"; |
| import Size from "./Size"; |
| |
| export default class Renderer { |
| private size: Size; |
| private scene: Scene; |
| private canvas: HTMLCanvasElement; |
| private camera: Camera; |
| private renderer: WebGLRenderer; |
| |
| constructor() { |
| const base = Base.getInstance(); |
| |
| this.size = base.size; |
| this.scene = base.scene; |
| this.canvas = base.canvas; |
| this.camera = base.camera; |
| |
| this.renderer = this.setRenderer(); |
| } |
| |
| setRenderer() { |
| const renderer = new WebGLRenderer({ |
| canvas: this.canvas, |
| antialias: true, |
| }); |
| |
| renderer.physicallyCorrectLights = true; |
| renderer.outputEncoding = THREE.sRGBEncoding; |
| renderer.toneMapping = THREE.CineonToneMapping; |
| renderer.toneMappingExposure = 1.75; |
| renderer.shadowMap.enabled = true; |
| renderer.shadowMap.type = THREE.PCFSoftShadowMap; |
| renderer.setSize(this.size.width, this.size.height); |
| renderer.setPixelRatio(this.size.pixelRatio); |
| |
| return renderer; |
| } |
| |
| resize() { |
| this.renderer.setSize(this.size.width, this.size.height); |
| this.renderer.setPixelRatio(this.size.pixelRatio); |
| } |
| |
| update() { |
| this.renderer.render(this.scene, this.camera.perspectiveCamera); |
| } |
| } |
| import { |
| TransformControls, |
| TransformControlsGizmo, |
| TransformControlsPlane, |
| } from "three/examples/jsm/controls/TransformControls"; |
| import Base from "./Base"; |
| import { |
| AxesHelper, |
| GridHelper, |
| Line, |
| MOUSE, |
| PerspectiveCamera, |
| Raycaster, |
| Scene, |
| Vector2, |
| } from "three"; |
| import { OrbitControls } from "three/examples/jsm/controls/OrbitControls"; |
| |
| export default class Control { |
| public transformControl: TransformControls; |
| public orbitControl: OrbitControls; |
| private base: Base; |
| private camera: PerspectiveCamera; |
| private canvas: HTMLCanvasElement; |
| private scene: Scene; |
| private transing: boolean; |
| constructor() { |
| const base = Base.getInstance(); |
| this.base = base; |
| |
| this.camera = base.camera.perspectiveCamera; |
| this.canvas = base.canvas; |
| this.scene = base.scene; |
| |
| |
| this.transing = false; |
| this.transformControl = this.createTransformControl(); |
| this.initTransformControlEvent(); |
| this.scene.add(this.transformControl); |
| this.orbitControl = this.createOrbitControl(); |
| } |
| |
| private createTransformControl() { |
| const transformControls = new TransformControls(this.camera, this.canvas); |
| |
| |
| const raycaster = new Raycaster(); |
| const mouse = new Vector2(); |
| let x = 0; |
| let y = 0; |
| let width = 0; |
| let height = 0; |
| |
| |
| this.canvas.addEventListener("mousemove", (event) => { |
| x = event.offsetX; |
| y = event.offsetY; |
| width = this.canvas.offsetWidth; |
| height = this.canvas.offsetHeight; |
| mouse.x = (x / width) * 2 - 1; |
| mouse.y = (-y * 2) / height + 1; |
| }); |
| |
| this.canvas.addEventListener("click", (event) => { |
| if (this.transing) { |
| this.transing = false; |
| return false; |
| } |
| |
| raycaster.setFromCamera(mouse, this.camera); |
| |
| |
| this.scene.remove(...this.base.noTransfromControlObjs); |
| const intersections = raycaster.intersectObjects(this.scene.children); |
| |
| this.scene.add(...this.base.noTransfromControlObjs); |
| |
| const targetIntersection = intersections.filter((intersection) => { |
| return ( |
| |
| !(intersection.object instanceof TransformControlsPlane) && |
| !(intersection.object instanceof AxesHelper) && |
| !(intersection.object instanceof GridHelper) && |
| !(intersection.object instanceof Line) && |
| !(intersection.object.parent instanceof TransformControlsGizmo) |
| |
| ); |
| }); |
| |
| console.log(targetIntersection); |
| |
| if (intersections.length) { |
| |
| |
| const object = intersections[0].object; |
| |
| |
| this.transformControl.attach(object); |
| } |
| }); |
| return transformControls; |
| } |
| |
| private initTransformControlEvent() { |
| this.transformControl.addEventListener("mouseDown", (e) => { |
| this.transing = true; |
| }); |
| } |
| |
| public update() { |
| this.orbitControl.update(); |
| } |
| |
| private createOrbitControl() { |
| const controls = new OrbitControls(this.camera, this.canvas); |
| controls.enableDamping = true; |
| controls.enableZoom = true; |
| |
| controls.mouseButtons = { |
| LEFT: null as unknown as MOUSE, |
| MIDDLE: MOUSE.DOLLY, |
| RIGHT: MOUSE.ROTATE, |
| }; |
| |
| return controls; |
| } |
| } |
| import EventEmitter from "eventemitter2"; |
| import { Clock } from "three"; |
| |
| export default class Time extends EventEmitter { |
| public clock: Clock; |
| constructor() { |
| super(); |
| this.clock = new Clock(); |
| |
| this.update(); |
| } |
| |
| update() { |
| this.emit("update"); |
| window.requestAnimationFrame(() => this.update()); |
| } |
| } |
| import { |
| BoxGeometry, |
| MeshBasicMaterial, |
| Mesh, |
| MeshLambertMaterial, |
| } from "three"; |
| import Base from "../Base"; |
| |
| export default class Box { |
| constructor() { |
| const base = Base.getInstance(); |
| |
| const cube = new Mesh( |
| new BoxGeometry(1, 1, 1), |
| new MeshBasicMaterial({ color: 0x00ff00 }) |
| ); |
| cube.position.y = 0.5; |
| |
| const cube2 = new Mesh( |
| new BoxGeometry(1, 1, 1), |
| new MeshLambertMaterial({ color: 0x00ff00 }) |
| ); |
| cube2.position.y = 0.5; |
| cube2.position.x = 3; |
| |
| base.scene.add(cube, cube2); |
| } |
| } |
| import { Light, Object3D, Scene, Vector2 } from "three"; |
| |
| import Camera from "./Cameras"; |
| import Helper from "./Helpers"; |
| import Renderer from "./Renderer"; |
| import Size from "./Size"; |
| import Time from "./Time"; |
| import Box from "./World/Box"; |
| import Control from "./Controls"; |
| import Lights from "./Lights"; |
| import EventManger from "./Events"; |
| |
| export default class Base { |
| public static base: Base; |
| public canvas: HTMLCanvasElement; |
| public scene: Scene; |
| public cameras: Camera; |
| public size: Size; |
| public time: Time; |
| public renderer: Renderer; |
| public control: Control; |
| public helper: Helper; |
| public eventManger: EventManger; |
| public noTransfromControlObjs: Object3D[] = []; |
| |
| constructor(canvas: HTMLCanvasElement) { |
| Base.base = this; |
| |
| this.canvas = canvas as HTMLCanvasElement; |
| this.scene = new Scene(); |
| this.size = new Size(); |
| this.cameras = new Camera(); |
| this.time = new Time(); |
| this.renderer = new Renderer(); |
| new Lights(); |
| |
| |
| const helper = new Helper(); |
| this.helper = helper; |
| this.noTransfromControlObjs.push(helper.gridHelper, helper.axesHelper); |
| |
| |
| const control = new Control(); |
| this.control = control; |
| this.noTransfromControlObjs.push(this.control.transformControl); |
| |
| this.eventManger = new EventManger(); |
| |
| |
| this.eventManger.addEventListener("click", (e) => { |
| console.log("点击射线穿过了下面这些物体"); |
| console.log(e); |
| }); |
| |
| |
| new Box(); |
| |
| this.time.on("update", () => { |
| this.update(); |
| }); |
| this.size.on("resize", () => { |
| this.resize(); |
| }); |
| } |
| |
| public static getInstance(canvas?: HTMLCanvasElement) { |
| if (canvas === undefined && Base.base !== undefined) { |
| return Base.base; |
| } |
| Base.base = new Base(canvas as HTMLCanvasElement); |
| return Base.base; |
| } |
| |
| private resize() { |
| this.cameras.resize(); |
| this.renderer.resize(); |
| } |
| |
| private update() { |
| this.control.update(); |
| this.renderer.update(); |
| } |
| } |
| import { AxesHelper, GridHelper } from "three"; |
| import Base from "./Base"; |
| |
| export default class { |
| public axesHelper: AxesHelper; |
| public gridHelper: GridHelper; |
| constructor() { |
| const base = Base.getInstance(); |
| const axesHelper = new AxesHelper(5); |
| this.axesHelper = axesHelper; |
| base.scene.add(axesHelper); |
| |
| const size = 10; |
| const divisions = 10; |
| |
| const gridHelper = new GridHelper(size, divisions); |
| this.gridHelper = gridHelper; |
| base.scene.add(gridHelper); |
| } |
| } |
| import { AmbientLight, DirectionalLight } from "three"; |
| import Base from "./Base"; |
| |
| export default class Environment { |
| private sunLight: DirectionalLight; |
| private ambientLight: AmbientLight; |
| constructor() { |
| const base = Base.getInstance(); |
| this.sunLight = new DirectionalLight("#ffffff", 2); |
| this.sunLight.castShadow = true; |
| this.sunLight.shadow.camera.far = 20; |
| this.sunLight.shadow.mapSize.set(2048, 2048); |
| this.sunLight.shadow.normalBias = 0.05; |
| |
| this.sunLight.position.set(-1.5, 7, 10); |
| base.scene.add(this.sunLight); |
| |
| this.ambientLight = new AmbientLight("#ffffff", 0.1); |
| base.scene.add(this.ambientLight); |
| } |
| } |
| import { PerspectiveCamera, Scene } from "three"; |
| import { OrbitControls } from "three/examples/jsm/controls/OrbitControls"; |
| import Base from "./Base"; |
| import type Size from "./Size"; |
| |
| export default class Camera { |
| private size: Size; |
| private scene: Scene; |
| private canvas: HTMLCanvasElement; |
| public perspectiveCamera: PerspectiveCamera; |
| |
| |
| constructor() { |
| const base = Base.getInstance(); |
| this.size = base.size; |
| this.scene = base.scene; |
| this.canvas = base.canvas; |
| |
| this.perspectiveCamera = this.createPerspectiveCamera(); |
| this.scene.add(this.perspectiveCamera); |
| } |
| private createPerspectiveCamera() { |
| const perspectiveCamera = new PerspectiveCamera( |
| 35, |
| this.size.aspect, |
| 0.1, |
| 1000 |
| ); |
| perspectiveCamera.position.set(15, 30, 20); |
| return perspectiveCamera; |
| } |
| |
| public resize() { |
| |
| this.perspectiveCamera.aspect = this.size.aspect; |
| this.perspectiveCamera.updateProjectionMatrix(); |
| } |
| } |
| import EventEmitter from "eventemitter2"; |
| export default class Size extends EventEmitter { |
| public width: number; |
| public height: number; |
| public aspect: number; |
| public pixelRatio: number; |
| |
| constructor() { |
| super(); |
| this.width = window.innerWidth; |
| this.height = window.innerHeight; |
| this.aspect = this.width / this.height; |
| this.pixelRatio = Math.min(window.devicePixelRatio, 2); |
| |
| window.addEventListener("resize", () => { |
| this.width = window.innerWidth; |
| this.height = window.innerHeight; |
| this.aspect = this.width / this.height; |
| this.pixelRatio = Math.min(window.devicePixelRatio, 2); |
| this.emit("resize"); |
| }); |
| } |
| } |
| import { |
| Camera, |
| EventDispatcher, |
| Object3D, |
| Raycaster, |
| Scene, |
| Vector2, |
| } from "three"; |
| import Base from "./Base"; |
| |
| export default class EventManger extends EventDispatcher { |
| private raycaster: Raycaster = new Raycaster(); |
| private mouse: Vector2 = new Vector2(); |
| private dom: HTMLCanvasElement; |
| private scene: Scene; |
| private camera: Camera; |
| constructor() { |
| super(); |
| const base = Base.getInstance(); |
| |
| this.dom = base.canvas; |
| this.scene = base.scene; |
| this.camera = base.cameras.perspectiveCamera; |
| |
| const mouse = this.mouse; |
| const raycaster = this.raycaster; |
| const dom = this.dom; |
| |
| let cacheObject: Object3D | null = null; |
| |
| dom.addEventListener("mousedown", (event) => { |
| |
| raycaster.setFromCamera(mouse, this.camera); |
| const intersection = raycaster.intersectObjects(this.scene.children); |
| |
| |
| this.dispatchEvent({ |
| type: "mousedown", |
| intersection, |
| }); |
| |
| if (intersection.length) { |
| const object = intersection[0].object; |
| |
| object.dispatchEvent({ |
| type: "mousedown", |
| }); |
| } |
| }); |
| |
| dom.addEventListener("mousemove", (event) => { |
| mouse.x = (event.offsetX / dom.offsetWidth) * 2 - 1; |
| mouse.y = (-event.offsetY * 2) / dom.offsetHeight + 1; |
| |
| |
| raycaster.setFromCamera(mouse, this.camera); |
| |
| const intersection = raycaster.intersectObjects(this.scene.children); |
| |
| |
| this.dispatchEvent({ |
| type: "mousemove", |
| intersection, |
| }); |
| |
| if (intersection.length) { |
| const object = intersection[0].object; |
| |
| if (object !== cacheObject) { |
| if (cacheObject) { |
| cacheObject.dispatchEvent({ |
| type: "mouseleave", |
| }); |
| } |
| object.dispatchEvent({ |
| type: "mouseenter", |
| }); |
| } else if (object === cacheObject) { |
| object.dispatchEvent({ |
| type: "mousemove", |
| }); |
| } |
| cacheObject = object; |
| } else { |
| if (cacheObject) { |
| cacheObject.dispatchEvent({ |
| type: "mouseleave", |
| }); |
| } |
| cacheObject = null; |
| } |
| }); |
| |
| dom.addEventListener("mouseup", (event) => { |
| |
| raycaster.setFromCamera(mouse, this.camera); |
| const intersection = raycaster.intersectObjects(this.scene.children); |
| |
| this.dispatchEvent({ |
| type: "mouseup", |
| intersection, |
| }); |
| if (intersection.length) { |
| const object = intersection[0].object; |
| object.dispatchEvent({ |
| type: "mouseup", |
| }); |
| } |
| }); |
| |
| dom.addEventListener("click", (event) => { |
| |
| raycaster.setFromCamera(mouse, this.camera); |
| const intersection = raycaster.intersectObjects(this.scene.children); |
| |
| this.dispatchEvent({ |
| type: "click", |
| intersection, |
| }); |
| |
| if (intersection.length) { |
| const object = intersection[0].object; |
| object.dispatchEvent({ |
| type: "click", |
| }); |
| } |
| }); |
| } |
| } |
| import * as THREE from "three"; |
| import Base from "./Base"; |
| import { Scene, WebGLRenderer } from "three"; |
| import Camera from "./Cameras"; |
| import Size from "./Size"; |
| |
| export default class Renderer { |
| private size: Size; |
| private scene: Scene; |
| private canvas: HTMLCanvasElement; |
| private camera: Camera; |
| private renderer: WebGLRenderer; |
| |
| constructor() { |
| const base = Base.getInstance(); |
| |
| this.size = base.size; |
| this.scene = base.scene; |
| this.canvas = base.canvas; |
| this.camera = base.cameras; |
| |
| this.renderer = this.setRenderer(); |
| } |
| |
| setRenderer() { |
| const renderer = new WebGLRenderer({ |
| canvas: this.canvas, |
| antialias: true, |
| }); |
| |
| renderer.physicallyCorrectLights = true; |
| renderer.outputEncoding = THREE.sRGBEncoding; |
| renderer.toneMapping = THREE.CineonToneMapping; |
| renderer.toneMappingExposure = 1.75; |
| renderer.shadowMap.enabled = true; |
| renderer.shadowMap.type = THREE.PCFSoftShadowMap; |
| renderer.setSize(this.size.width, this.size.height); |
| renderer.setPixelRatio(this.size.pixelRatio); |
| |
| return renderer; |
| } |
| |
| resize() { |
| this.renderer.setSize(this.size.width, this.size.height); |
| this.renderer.setPixelRatio(this.size.pixelRatio); |
| } |
| |
| update() { |
| this.renderer.render(this.scene, this.camera.perspectiveCamera); |
| } |
| } |
| import { |
| TransformControls, |
| TransformControlsGizmo, |
| TransformControlsPlane, |
| } from "three/examples/jsm/controls/TransformControls"; |
| import Base from "./Base"; |
| import { |
| AxesHelper, |
| GridHelper, |
| Line, |
| MOUSE, |
| Object3D, |
| PerspectiveCamera, |
| Raycaster, |
| Scene, |
| Vector2, |
| } from "three"; |
| import { OrbitControls } from "three/examples/jsm/controls/OrbitControls"; |
| |
| export default class Control { |
| public transformControl: TransformControls; |
| public orbitControl: OrbitControls; |
| private base: Base; |
| private camera: PerspectiveCamera; |
| private canvas: HTMLCanvasElement; |
| private scene: Scene; |
| private transing: boolean; |
| constructor() { |
| const base = Base.getInstance(); |
| this.base = base; |
| |
| this.camera = base.cameras.perspectiveCamera; |
| this.canvas = base.canvas; |
| this.scene = base.scene; |
| |
| |
| this.transing = false; |
| this.transformControl = this.createTransformControl(); |
| this.initTransformControlEvent(); |
| this.scene.add(this.transformControl); |
| this.orbitControl = this.createOrbitControl(); |
| } |
| |
| private createTransformControl() { |
| const transformControls = new TransformControls(this.camera, this.canvas); |
| return transformControls; |
| } |
| |
| private initTransformControlEvent() { |
| this.transformControl.addEventListener("mouseDown", (e) => { |
| this.transing = true; |
| }); |
| } |
| |
| public update() { |
| this.orbitControl.update(); |
| } |
| |
| private createOrbitControl() { |
| const controls = new OrbitControls(this.camera, this.canvas); |
| controls.enableDamping = true; |
| controls.enableZoom = true; |
| |
| controls.mouseButtons = { |
| LEFT: null as unknown as MOUSE, |
| MIDDLE: MOUSE.DOLLY, |
| RIGHT: MOUSE.PAN, |
| }; |
| |
| return controls; |
| } |
| } |
| import EventEmitter from "eventemitter2"; |
| import { Clock } from "three"; |
| |
| export default class Time extends EventEmitter { |
| public clock: Clock; |
| constructor() { |
| super(); |
| this.clock = new Clock(); |
| |
| this.update(); |
| } |
| |
| update() { |
| this.emit("update"); |
| window.requestAnimationFrame(() => this.update()); |
| } |
| } |
| import { |
| BoxGeometry, |
| MeshBasicMaterial, |
| Mesh, |
| MeshLambertMaterial, |
| Color, |
| } from "three"; |
| import Base from "../Base"; |
| |
| export default class Box { |
| private cube: Mesh; |
| private cubeMaterial: MeshBasicMaterial; |
| |
| constructor() { |
| const base = Base.getInstance(); |
| this.cubeMaterial = new MeshBasicMaterial({ color: 0x00ff00 }); |
| const cube = this.createCube(); |
| this.cube = cube; |
| base.scene.add(cube); |
| const cube2 = new Mesh( |
| new BoxGeometry(1, 1, 1), |
| new MeshBasicMaterial({ color: 0x00ff00 }) |
| ); |
| cube2.position.setX(3); |
| base.scene.add(cube2); |
| |
| this.initCubeEvent(); |
| } |
| private createCube() { |
| const cube = new Mesh(new BoxGeometry(1, 1, 1), this.cubeMaterial); |
| cube.position.y = 0.5; |
| return cube; |
| } |
| private initCubeEvent() { |
| this.cube.addEventListener("mouseenter", (e) => { |
| console.log("鼠标进入"); |
| this.cubeMaterial.color = new Color(0xff0000); |
| }); |
| this.cube.addEventListener("mousemove", (e) => { |
| console.log("鼠标正在移动"); |
| }); |
| |
| this.cube.addEventListener("mouseleave", (e) => { |
| console.log("鼠标离开"); |
| this.cubeMaterial.color = new Color(0x00ff00); |
| }); |
| this.cube.addEventListener("click", (e) => { |
| console.log("鼠标点击"); |
| this.cubeMaterial.color = new Color(0x0000ff); |
| }); |
| } |
| } |
| import { Scene } from "three"; |
| |
| import Camera from "./Camera"; |
| |
| export default class Base { |
| public static base: Base; |
| public canvas!: HTMLCanvasElement; |
| public scene!: Scene; |
| public camera!: Camera; |
| |
| constructor(canvas?: HTMLCanvasElement) { |
| if (canvas === undefined && Base.base !== undefined) { |
| return Base.base; |
| } |
| Base.base = this; |
| |
| this.canvas = canvas as HTMLCanvasElement; |
| this.scene = new Scene(); |
| this.camera = new Camera(); |
| } |
| } |
| import Base from "./Base"; |
| |
| export default class Camera { |
| constructor() { |
| const base = new Base(); |
| console.log(base.canvas); |
| } |
| } |
| import { Scene } from "three"; |
| |
| import Camera from "./Camera"; |
| import Helper from "./Helper"; |
| import Renderer from "./Renderer"; |
| import Size from "./Size"; |
| import Time from "./Time"; |
| import World from "./World/World"; |
| |
| export default class Base { |
| public static base: Base; |
| public canvas: HTMLCanvasElement; |
| public scene: Scene; |
| public camera: Camera; |
| public size: Size; |
| public time: Time; |
| public renderer: Renderer; |
| |
| public world: World; |
| |
| constructor(canvas: HTMLCanvasElement) { |
| Base.base = this; |
| |
| this.canvas = canvas as HTMLCanvasElement; |
| this.scene = new Scene(); |
| this.size = new Size(); |
| this.camera = new Camera(); |
| this.time = new Time(); |
| this.renderer = new Renderer(); |
| new Helper(); |
| |
| this.world = new World(); |
| |
| this.time.on("update", () => { |
| this.update(); |
| }); |
| this.size.on("resize", () => { |
| this.resize(); |
| }); |
| } |
| |
| public static getInstance(canvas?: HTMLCanvasElement) { |
| if (canvas === undefined && Base.base !== undefined) { |
| return Base.base; |
| } |
| Base.base = new Base(canvas as HTMLCanvasElement); |
| return Base.base; |
| } |
| |
| private resize() { |
| this.camera.resize(); |
| this.renderer.resize(); |
| } |
| |
| private update() { |
| this.camera.update(); |
| this.renderer.update(); |
| } |
| } |
| import { AxesHelper, GridHelper } from "three"; |
| import Base from "./Base"; |
| |
| export default class { |
| constructor() { |
| const base = Base.getInstance(); |
| const axesHelper = new AxesHelper(5); |
| base.scene.add(axesHelper); |
| |
| const size = 10; |
| const divisions = 10; |
| |
| const gridHelper = new GridHelper(size, divisions); |
| base.scene.add(gridHelper); |
| } |
| } |
| import { PerspectiveCamera, Scene } from "three"; |
| import { OrbitControls } from "three/examples/jsm/controls/OrbitControls"; |
| import Base from "./Base"; |
| import type Size from "./Size"; |
| |
| export default class Camera { |
| private size: Size; |
| private scene: Scene; |
| private canvas: HTMLCanvasElement; |
| public perspectiveCamera: PerspectiveCamera; |
| private controls: OrbitControls; |
| |
| constructor() { |
| const base = Base.getInstance(); |
| this.size = base.size; |
| this.scene = base.scene; |
| this.canvas = base.canvas; |
| |
| this.perspectiveCamera = this.createPerspectiveCamera(); |
| this.scene.add(this.perspectiveCamera); |
| |
| this.controls = this.createOrbitControls(); |
| } |
| private createPerspectiveCamera() { |
| const perspectiveCamera = new PerspectiveCamera( |
| 35, |
| this.size.aspect, |
| 0.1, |
| 1000 |
| ); |
| perspectiveCamera.position.set(15, 30, 20); |
| return perspectiveCamera; |
| } |
| private createOrbitControls() { |
| const controls = new OrbitControls(this.perspectiveCamera, this.canvas); |
| controls.enableDamping = true; |
| controls.enableZoom = true; |
| return controls; |
| } |
| public update() { |
| this.controls.update(); |
| } |
| public resize() { |
| |
| this.perspectiveCamera.aspect = this.size.aspect; |
| this.perspectiveCamera.updateProjectionMatrix(); |
| } |
| } |
| import EventEmitter from "eventemitter2"; |
| export default class Size extends EventEmitter { |
| public width: number; |
| public height: number; |
| public aspect: number; |
| public pixelRatio: number; |
| |
| constructor() { |
| super(); |
| this.width = window.innerWidth; |
| this.height = window.innerHeight; |
| this.aspect = this.width / this.height; |
| this.pixelRatio = Math.min(window.devicePixelRatio, 2); |
| |
| window.addEventListener("resize", () => { |
| this.width = window.innerWidth; |
| this.height = window.innerHeight; |
| this.aspect = this.width / this.height; |
| this.pixelRatio = Math.min(window.devicePixelRatio, 2); |
| this.emit("resize"); |
| }); |
| } |
| } |
| import * as THREE from "three"; |
| import Base from "./Base"; |
| import { Scene, WebGLRenderer } from "three"; |
| import Camera from "./Camera"; |
| import Size from "./Size"; |
| |
| export default class Renderer { |
| private size: Size; |
| private scene: Scene; |
| private canvas: HTMLCanvasElement; |
| private camera: Camera; |
| private renderer: WebGLRenderer; |
| |
| constructor() { |
| const base = Base.getInstance(); |
| |
| this.size = base.size; |
| this.scene = base.scene; |
| this.canvas = base.canvas; |
| this.camera = base.camera; |
| |
| this.renderer = this.setRenderer(); |
| } |
| |
| setRenderer() { |
| const renderer = new WebGLRenderer({ |
| canvas: this.canvas, |
| antialias: true, |
| }); |
| |
| renderer.physicallyCorrectLights = true; |
| renderer.outputEncoding = THREE.sRGBEncoding; |
| renderer.toneMapping = THREE.CineonToneMapping; |
| renderer.toneMappingExposure = 1.75; |
| renderer.shadowMap.enabled = true; |
| renderer.shadowMap.type = THREE.PCFSoftShadowMap; |
| renderer.setSize(this.size.width, this.size.height); |
| renderer.setPixelRatio(this.size.pixelRatio); |
| |
| return renderer; |
| } |
| |
| resize() { |
| this.renderer.setSize(this.size.width, this.size.height); |
| this.renderer.setPixelRatio(this.size.pixelRatio); |
| } |
| |
| update() { |
| this.renderer.render(this.scene, this.camera.perspectiveCamera); |
| } |
| } |
| import EventEmitter from "eventemitter2"; |
| import { Clock } from "three"; |
| |
| export default class Time extends EventEmitter { |
| public clock: Clock; |
| constructor() { |
| super(); |
| this.clock = new Clock(); |
| |
| this.update(); |
| } |
| |
| update() { |
| this.emit("update"); |
| window.requestAnimationFrame(() => this.update()); |
| } |
| } |
| import { AmbientLight, DirectionalLight } from "three"; |
| import Base from "../Base"; |
| |
| export default class Environment { |
| private sunLight: DirectionalLight; |
| private ambientLight: AmbientLight; |
| constructor() { |
| const base = Base.getInstance(); |
| this.sunLight = new DirectionalLight("#ffffff", 3); |
| this.sunLight.castShadow = true; |
| this.sunLight.shadow.camera.far = 20; |
| this.sunLight.shadow.mapSize.set(2048, 2048); |
| this.sunLight.shadow.normalBias = 0.05; |
| |
| |
| |
| this.sunLight.position.set(-1.5, 7, 3); |
| base.scene.add(this.sunLight); |
| |
| this.ambientLight = new AmbientLight("#ffffff", 1); |
| base.scene.add(this.ambientLight); |
| } |
| } |
| import { BackSide, Mesh, MeshBasicMaterial, PlaneGeometry } from "three"; |
| import Base from "../Base"; |
| |
| export default class World { |
| private plane: Mesh; |
| constructor() { |
| const base = Base.getInstance(); |
| const geometry = new PlaneGeometry(10, 10); |
| const material = new MeshBasicMaterial({ |
| color: 0xfefefe, |
| side: BackSide, |
| }); |
| this.plane = new Mesh(geometry, material); |
| this.plane.rotateX(Math.PI * 0.5); |
| |
| base.scene.add(this.plane); |
| } |
| } |
| import { Light, Object3D, Scene, Vector2 } from "three"; |
| |
| import Camera from "./Cameras"; |
| import Helper from "./Helpers"; |
| import Renderer from "./Renderer"; |
| import Size from "./Size"; |
| import Time from "./Time"; |
| import Box from "./World/Universe"; |
| import Control from "./Controls"; |
| import Lights from "./Lights"; |
| import EventManger from "./Events"; |
| |
| export default class Base { |
| public static base: Base; |
| public canvas: HTMLCanvasElement; |
| public scene: Scene; |
| public cameras: Camera; |
| public size: Size; |
| public time: Time; |
| public renderer: Renderer; |
| public control: Control; |
| public helper: Helper; |
| public eventManger: EventManger; |
| public noTransfromControlObjs: Object3D[] = []; |
| |
| constructor(canvas: HTMLCanvasElement) { |
| Base.base = this; |
| |
| this.canvas = canvas as HTMLCanvasElement; |
| this.scene = new Scene(); |
| this.size = new Size(); |
| this.cameras = new Camera(); |
| this.time = new Time(); |
| this.renderer = new Renderer(); |
| new Lights(); |
| |
| |
| const helper = new Helper(); |
| this.helper = helper; |
| this.noTransfromControlObjs.push(helper.gridHelper, helper.axesHelper); |
| |
| |
| const control = new Control(); |
| this.control = control; |
| this.noTransfromControlObjs.push(this.control.transformControl); |
| |
| this.eventManger = new EventManger(); |
| |
| |
| this.eventManger.addEventListener("click", (e) => { |
| console.log("点击射线穿过了下面这些物体"); |
| console.log(e); |
| }); |
| |
| |
| new Box(); |
| |
| this.time.on("update", () => { |
| this.update(); |
| }); |
| this.size.on("resize", () => { |
| this.resize(); |
| }); |
| } |
| |
| public static getInstance(canvas?: HTMLCanvasElement) { |
| if (canvas === undefined && Base.base !== undefined) { |
| return Base.base; |
| } |
| Base.base = new Base(canvas as HTMLCanvasElement); |
| return Base.base; |
| } |
| |
| private resize() { |
| this.cameras.resize(); |
| this.renderer.resize(); |
| } |
| |
| private update() { |
| this.control.update(); |
| this.renderer.update(); |
| } |
| } |
| import { AxesHelper, GridHelper } from "three"; |
| import Base from "./Base"; |
| |
| export default class { |
| public axesHelper: AxesHelper; |
| public gridHelper: GridHelper; |
| constructor() { |
| const base = Base.getInstance(); |
| const axesHelper = new AxesHelper(5); |
| this.axesHelper = axesHelper; |
| base.scene.add(axesHelper); |
| |
| const size = 10; |
| const divisions = 10; |
| |
| const gridHelper = new GridHelper(size, divisions); |
| this.gridHelper = gridHelper; |
| base.scene.add(gridHelper); |
| } |
| } |
| import { AmbientLight, DirectionalLight } from "three"; |
| import Base from "./Base"; |
| |
| export default class Environment { |
| private sunLight: DirectionalLight; |
| private ambientLight: AmbientLight; |
| constructor() { |
| const base = Base.getInstance(); |
| this.sunLight = new DirectionalLight("#ffffff", 2); |
| this.sunLight.castShadow = true; |
| this.sunLight.shadow.camera.far = 20; |
| this.sunLight.shadow.mapSize.set(2048, 2048); |
| this.sunLight.shadow.normalBias = 0.05; |
| |
| this.sunLight.position.set(-1.5, 7, 10); |
| base.scene.add(this.sunLight); |
| |
| this.ambientLight = new AmbientLight("#ffffff", 0.1); |
| base.scene.add(this.ambientLight); |
| } |
| } |
| import { PerspectiveCamera, Scene } from "three"; |
| import { OrbitControls } from "three/examples/jsm/controls/OrbitControls"; |
| import Base from "./Base"; |
| import type Size from "./Size"; |
| |
| export default class Camera { |
| private size: Size; |
| private scene: Scene; |
| private canvas: HTMLCanvasElement; |
| public perspectiveCamera: PerspectiveCamera; |
| |
| |
| constructor() { |
| const base = Base.getInstance(); |
| this.size = base.size; |
| this.scene = base.scene; |
| this.canvas = base.canvas; |
| |
| this.perspectiveCamera = this.createPerspectiveCamera(); |
| this.scene.add(this.perspectiveCamera); |
| } |
| private createPerspectiveCamera() { |
| const perspectiveCamera = new PerspectiveCamera( |
| 35, |
| this.size.aspect, |
| 0.1, |
| 1000 |
| ); |
| perspectiveCamera.position.set(0, 10, 0); |
| return perspectiveCamera; |
| } |
| |
| public resize() { |
| |
| this.perspectiveCamera.aspect = this.size.aspect; |
| this.perspectiveCamera.updateProjectionMatrix(); |
| } |
| } |
| import EventEmitter from "eventemitter2"; |
| export default class Size extends EventEmitter { |
| public width: number; |
| public height: number; |
| public aspect: number; |
| public pixelRatio: number; |
| |
| constructor() { |
| super(); |
| this.width = window.innerWidth; |
| this.height = window.innerHeight; |
| this.aspect = this.width / this.height; |
| this.pixelRatio = Math.min(window.devicePixelRatio, 2); |
| |
| window.addEventListener("resize", () => { |
| this.width = window.innerWidth; |
| this.height = window.innerHeight; |
| this.aspect = this.width / this.height; |
| this.pixelRatio = Math.min(window.devicePixelRatio, 2); |
| this.emit("resize"); |
| }); |
| } |
| } |
| import { |
| AxesHelper, |
| Camera, |
| EventDispatcher, |
| GridHelper, |
| Line, |
| Object3D, |
| Raycaster, |
| Scene, |
| Vector2, |
| } from "three"; |
| import { |
| TransformControlsGizmo, |
| TransformControlsPlane, |
| } from "three/examples/jsm/controls/TransformControls"; |
| import Base from "./Base"; |
| |
| export default class EventManger extends EventDispatcher { |
| private base: Base; |
| private raycaster: Raycaster = new Raycaster(); |
| private mouse: Vector2 = new Vector2(); |
| private dom: HTMLCanvasElement; |
| private scene: Scene; |
| private camera: Camera; |
| constructor() { |
| super(); |
| const base = Base.getInstance(); |
| this.base = base; |
| |
| this.dom = base.canvas; |
| this.scene = base.scene; |
| this.camera = base.cameras.perspectiveCamera; |
| |
| const mouse = this.mouse; |
| const raycaster = this.raycaster; |
| const dom = this.dom; |
| |
| let cacheObject: Object3D | null = null; |
| |
| dom.addEventListener("mousedown", (event) => { |
| |
| raycaster.setFromCamera(mouse, this.camera); |
| const intersection = raycaster.intersectObjects(this.scene.children); |
| |
| |
| this.dispatchEvent({ |
| type: "mousedown", |
| intersection, |
| }); |
| |
| if (intersection.length) { |
| const object = intersection[0].object; |
| |
| object.dispatchEvent({ |
| type: "mousedown", |
| }); |
| } |
| }); |
| |
| dom.addEventListener("mousemove", (event) => { |
| mouse.x = (event.offsetX / dom.offsetWidth) * 2 - 1; |
| mouse.y = (-event.offsetY * 2) / dom.offsetHeight + 1; |
| |
| |
| raycaster.setFromCamera(mouse, this.camera); |
| |
| const intersection = raycaster.intersectObjects(this.scene.children); |
| |
| |
| this.dispatchEvent({ |
| type: "mousemove", |
| intersection, |
| }); |
| |
| if (intersection.length) { |
| const object = intersection[0].object; |
| |
| if (object !== cacheObject) { |
| if (cacheObject) { |
| cacheObject.dispatchEvent({ |
| type: "mouseleave", |
| }); |
| } |
| object.dispatchEvent({ |
| type: "mouseenter", |
| }); |
| } else if (object === cacheObject) { |
| object.dispatchEvent({ |
| type: "mousemove", |
| }); |
| } |
| cacheObject = object; |
| } else { |
| if (cacheObject) { |
| cacheObject.dispatchEvent({ |
| type: "mouseleave", |
| }); |
| } |
| cacheObject = null; |
| } |
| }); |
| |
| dom.addEventListener("mouseup", (event) => { |
| |
| raycaster.setFromCamera(mouse, this.camera); |
| const intersection = raycaster.intersectObjects(this.scene.children); |
| |
| this.dispatchEvent({ |
| type: "mouseup", |
| intersection, |
| }); |
| if (intersection.length) { |
| const object = intersection[0].object; |
| object.dispatchEvent({ |
| type: "mouseup", |
| }); |
| } |
| }); |
| |
| dom.addEventListener("click", (event) => { |
| |
| raycaster.setFromCamera(mouse, this.camera); |
| let intersections = raycaster.intersectObjects(this.scene.children); |
| |
| this.dispatchEvent({ |
| type: "click", |
| intersections, |
| }); |
| |
| |
| this.scene.remove(...this.base.noTransfromControlObjs); |
| intersections = raycaster.intersectObjects(this.scene.children); |
| |
| this.scene.add(...this.base.noTransfromControlObjs); |
| |
| const targetIntersection = intersections.filter((intersection) => { |
| return ( |
| |
| !(intersection.object instanceof TransformControlsPlane) && |
| !(intersection.object instanceof AxesHelper) && |
| !(intersection.object instanceof GridHelper) && |
| !(intersection.object instanceof Line) && |
| !(intersection.object.parent instanceof TransformControlsGizmo) |
| |
| ); |
| }); |
| |
| if (targetIntersection.length) { |
| const object = targetIntersection[0].object; |
| |
| console.log(object); |
| |
| object.dispatchEvent({ |
| type: "click", |
| }); |
| } |
| }); |
| } |
| } |
| import * as THREE from "three"; |
| import Base from "./Base"; |
| import { Scene, WebGLRenderer } from "three"; |
| import Camera from "./Cameras"; |
| import Size from "./Size"; |
| import { CSS2DRenderer } from "three/examples/jsm/renderers/CSS2DRenderer"; |
| |
| export default class Renderer { |
| private size: Size; |
| private scene: Scene; |
| private canvas: HTMLCanvasElement; |
| private camera: Camera; |
| private labelRenderer: CSS2DRenderer; |
| private renderer: WebGLRenderer; |
| |
| constructor() { |
| const base = Base.getInstance(); |
| |
| this.size = base.size; |
| this.scene = base.scene; |
| this.canvas = base.canvas; |
| this.camera = base.cameras; |
| |
| this.renderer = this.setRenderer(); |
| |
| const labelRenderer = this.setLabelRenderer(); |
| this.labelRenderer = labelRenderer; |
| } |
| |
| setRenderer() { |
| const renderer = new WebGLRenderer({ |
| canvas: this.canvas, |
| antialias: true, |
| }); |
| |
| renderer.physicallyCorrectLights = true; |
| renderer.outputEncoding = THREE.sRGBEncoding; |
| renderer.toneMapping = THREE.CineonToneMapping; |
| renderer.toneMappingExposure = 1.75; |
| renderer.shadowMap.enabled = true; |
| renderer.shadowMap.type = THREE.PCFSoftShadowMap; |
| renderer.setSize(this.size.width, this.size.height); |
| renderer.setPixelRatio(this.size.pixelRatio); |
| |
| return renderer; |
| } |
| private setLabelRenderer() { |
| const labelRenderer = new CSS2DRenderer(); |
| labelRenderer.setSize(this.size.width, this.size.height); |
| document.body.appendChild(labelRenderer.domElement); |
| return labelRenderer; |
| } |
| |
| resize() { |
| this.renderer.setSize(this.size.width, this.size.height); |
| this.renderer.setPixelRatio(this.size.pixelRatio); |
| |
| this.labelRenderer.setSize(this.size.width, this.size.height); |
| } |
| |
| update() { |
| this.renderer.render(this.scene, this.camera.perspectiveCamera); |
| this.labelRenderer.render(this.scene, this.camera.perspectiveCamera); |
| } |
| } |
| import { |
| TransformControls, |
| TransformControlsGizmo, |
| TransformControlsPlane, |
| } from "three/examples/jsm/controls/TransformControls"; |
| import Base from "./Base"; |
| import { |
| AxesHelper, |
| GridHelper, |
| Line, |
| MOUSE, |
| Object3D, |
| PerspectiveCamera, |
| Raycaster, |
| Scene, |
| Vector2, |
| } from "three"; |
| import { OrbitControls } from "three/examples/jsm/controls/OrbitControls"; |
| |
| export default class Control { |
| public transformControl: TransformControls; |
| public orbitControl: OrbitControls; |
| private base: Base; |
| private camera: PerspectiveCamera; |
| private canvas: HTMLCanvasElement; |
| private scene: Scene; |
| private transing: boolean; |
| constructor() { |
| const base = Base.getInstance(); |
| this.base = base; |
| |
| this.camera = base.cameras.perspectiveCamera; |
| this.canvas = base.canvas; |
| this.scene = base.scene; |
| |
| |
| this.transing = false; |
| this.transformControl = this.createTransformControl(); |
| this.initTransformControlEvent(); |
| this.scene.add(this.transformControl); |
| this.orbitControl = this.createOrbitControl(); |
| } |
| |
| private createTransformControl() { |
| const transformControls = new TransformControls(this.camera, this.canvas); |
| return transformControls; |
| } |
| |
| private initTransformControlEvent() { |
| this.transformControl.addEventListener("mouseDown", (e) => { |
| this.transing = true; |
| }); |
| } |
| |
| public update() { |
| this.orbitControl.update(); |
| } |
| |
| private createOrbitControl() { |
| const controls = new OrbitControls(this.camera, this.canvas); |
| controls.enableDamping = true; |
| controls.enableZoom = true; |
| |
| |
| |
| |
| |
| |
| |
| return controls; |
| } |
| } |
| import EventEmitter from "eventemitter2"; |
| import { Clock } from "three"; |
| |
| export default class Time extends EventEmitter { |
| public clock: Clock; |
| constructor() { |
| super(); |
| this.clock = new Clock(); |
| |
| this.update(); |
| } |
| |
| update() { |
| this.emit("update"); |
| window.requestAnimationFrame(() => this.update()); |
| } |
| } |
| import { BoxGeometry, MeshBasicMaterial, Mesh, Color } from "three"; |
| import { CSS2DObject } from "three/examples/jsm/renderers/CSS2DRenderer"; |
| |
| import Base from "../Base"; |
| |
| export default class Box { |
| private cube: Mesh; |
| private label: CSS2DObject; |
| private cubeMaterial: MeshBasicMaterial; |
| |
| constructor() { |
| const base = Base.getInstance(); |
| this.cubeMaterial = new MeshBasicMaterial({ color: 0x00ff00 }); |
| const cube = this.createCube(); |
| this.cube = cube; |
| const label = this.createLabel(); |
| this.label = label; |
| |
| base.scene.add(cube); |
| base.scene.add(label); |
| |
| this.initCubeEvents(); |
| } |
| |
| private createLabel() { |
| const oLabel = document.createElement("div"); |
| oLabel.className = "label"; |
| oLabel.innerHTML = `<div>导航到LM12</div>`; |
| |
| |
| oLabel.style.backgroundColor = "skyblue"; |
| |
| const label = new CSS2DObject(oLabel); |
| |
| return label; |
| } |
| private createCube() { |
| const cube = new Mesh(new BoxGeometry(1, 1, 1), this.cubeMaterial); |
| cube.position.y = 0.5; |
| return cube; |
| } |
| private initCubeEvents() { |
| this.cube.addEventListener("click", (e) => { |
| this.cubeMaterial.color = new Color(0xff0000); |
| this.label.position.set(2, 0, 0); |
| |
| this.label.element.innerHTML = "导航到LM13"; |
| }); |
| } |
| } |
| import { |
| BoxGeometry, |
| MeshBasicMaterial, |
| Mesh, |
| MeshLambertMaterial, |
| Color, |
| } from "three"; |
| import Base from "../Base"; |
| |
| export default class Box { |
| private cube: Mesh; |
| private cubeMaterial: MeshBasicMaterial; |
| |
| constructor() { |
| const base = Base.getInstance(); |
| this.cubeMaterial = new MeshBasicMaterial({ color: 0x00ff00 }); |
| const cube = this.createCube(); |
| this.cube = cube; |
| base.scene.add(cube); |
| const cube2 = new Mesh( |
| new BoxGeometry(1, 1, 1), |
| new MeshBasicMaterial({ color: 0x00ff00 }) |
| ); |
| cube2.position.setX(3); |
| base.scene.add(cube2); |
| |
| this.initCubeEvent(); |
| } |
| private createCube() { |
| const cube = new Mesh(new BoxGeometry(1, 1, 1), this.cubeMaterial); |
| cube.position.y = 0.5; |
| return cube; |
| } |
| private initCubeEvent() { |
| this.cube.addEventListener("mouseenter", (e) => { |
| console.log("鼠标进入"); |
| this.cubeMaterial.color = new Color(0xff0000); |
| }); |
| this.cube.addEventListener("mousemove", (e) => { |
| console.log("鼠标正在移动"); |
| }); |
| |
| this.cube.addEventListener("mouseleave", (e) => { |
| console.log("鼠标离开"); |
| this.cubeMaterial.color = new Color(0x00ff00); |
| }); |
| this.cube.addEventListener("click", (e) => { |
| console.log("鼠标点击"); |
| this.cubeMaterial.color = new Color(0x0000ff); |
| }); |
| } |
| } |
| import { Light, Object3D, Scene, Vector2 } from "three"; |
| |
| import Camera from "./Cameras"; |
| import Helper from "./Helpers"; |
| import Renderer from "./Renderer"; |
| import Size from "./Size"; |
| import Time from "./Time"; |
| import Box from "./World/Universe"; |
| import Control from "./Controls"; |
| import Lights from "./Lights"; |
| import EventManger from "./Events"; |
| |
| export default class Base { |
| public static base: Base; |
| public canvas: HTMLCanvasElement; |
| public scene: Scene; |
| public cameras: Camera; |
| public size: Size; |
| public time: Time; |
| public renderer: Renderer; |
| public control: Control; |
| public helper: Helper; |
| public eventManger: EventManger; |
| public noTransfromControlObjs: Object3D[] = []; |
| |
| constructor(canvas: HTMLCanvasElement) { |
| Base.base = this; |
| |
| this.canvas = canvas as HTMLCanvasElement; |
| this.scene = new Scene(); |
| this.size = new Size(); |
| this.cameras = new Camera(); |
| this.time = new Time(); |
| this.renderer = new Renderer(); |
| new Lights(); |
| |
| |
| const helper = new Helper(); |
| this.helper = helper; |
| this.noTransfromControlObjs.push(helper.gridHelper, helper.axesHelper); |
| |
| |
| const control = new Control(); |
| this.control = control; |
| this.noTransfromControlObjs.push(this.control.transformControl); |
| |
| this.eventManger = new EventManger(); |
| |
| |
| this.eventManger.addEventListener("click", (e) => { |
| console.log("点击射线穿过了下面这些物体"); |
| console.log(e); |
| }); |
| |
| |
| new Box(); |
| |
| this.time.on("update", () => { |
| this.update(); |
| }); |
| this.size.on("resize", () => { |
| this.resize(); |
| }); |
| } |
| |
| public static getInstance(canvas?: HTMLCanvasElement) { |
| if (canvas === undefined && Base.base !== undefined) { |
| return Base.base; |
| } |
| Base.base = new Base(canvas as HTMLCanvasElement); |
| return Base.base; |
| } |
| |
| private resize() { |
| this.cameras.resize(); |
| this.renderer.resize(); |
| } |
| |
| private update() { |
| this.control.update(); |
| this.renderer.update(); |
| } |
| } |
| import { AxesHelper, GridHelper } from "three"; |
| import Base from "./Base"; |
| |
| export default class { |
| public axesHelper: AxesHelper; |
| public gridHelper: GridHelper; |
| constructor() { |
| const base = Base.getInstance(); |
| const axesHelper = new AxesHelper(5); |
| this.axesHelper = axesHelper; |
| base.scene.add(axesHelper); |
| |
| const size = 10; |
| const divisions = 10; |
| |
| const gridHelper = new GridHelper(size, divisions); |
| this.gridHelper = gridHelper; |
| base.scene.add(gridHelper); |
| } |
| } |
| import { AmbientLight, DirectionalLight } from "three"; |
| import Base from "./Base"; |
| |
| export default class Environment { |
| private sunLight: DirectionalLight; |
| private ambientLight: AmbientLight; |
| constructor() { |
| const base = Base.getInstance(); |
| this.sunLight = new DirectionalLight("#ffffff", 2); |
| this.sunLight.castShadow = true; |
| this.sunLight.shadow.camera.far = 20; |
| this.sunLight.shadow.mapSize.set(2048, 2048); |
| this.sunLight.shadow.normalBias = 0.05; |
| |
| this.sunLight.position.set(-1.5, 7, 10); |
| base.scene.add(this.sunLight); |
| |
| this.ambientLight = new AmbientLight("#ffffff", 0.1); |
| base.scene.add(this.ambientLight); |
| } |
| } |
| import { PerspectiveCamera, Scene } from "three"; |
| import { OrbitControls } from "three/examples/jsm/controls/OrbitControls"; |
| import Base from "./Base"; |
| import type Size from "./Size"; |
| |
| export default class Camera { |
| private size: Size; |
| private scene: Scene; |
| private canvas: HTMLCanvasElement; |
| public perspectiveCamera: PerspectiveCamera; |
| |
| |
| constructor() { |
| const base = Base.getInstance(); |
| this.size = base.size; |
| this.scene = base.scene; |
| this.canvas = base.canvas; |
| |
| this.perspectiveCamera = this.createPerspectiveCamera(); |
| this.scene.add(this.perspectiveCamera); |
| } |
| private createPerspectiveCamera() { |
| const perspectiveCamera = new PerspectiveCamera( |
| 35, |
| this.size.aspect, |
| 0.1, |
| 1000 |
| ); |
| perspectiveCamera.position.set(0, 10, 0); |
| return perspectiveCamera; |
| } |
| |
| public resize() { |
| |
| this.perspectiveCamera.aspect = this.size.aspect; |
| this.perspectiveCamera.updateProjectionMatrix(); |
| } |
| } |
| import EventEmitter from "eventemitter2"; |
| export default class Size extends EventEmitter { |
| public width: number; |
| public height: number; |
| public aspect: number; |
| public pixelRatio: number; |
| |
| constructor() { |
| super(); |
| this.width = window.innerWidth; |
| this.height = window.innerHeight; |
| this.aspect = this.width / this.height; |
| this.pixelRatio = Math.min(window.devicePixelRatio, 2); |
| |
| window.addEventListener("resize", () => { |
| this.width = window.innerWidth; |
| this.height = window.innerHeight; |
| this.aspect = this.width / this.height; |
| this.pixelRatio = Math.min(window.devicePixelRatio, 2); |
| this.emit("resize"); |
| }); |
| } |
| } |
| import { |
| AxesHelper, |
| Camera, |
| EventDispatcher, |
| GridHelper, |
| Line, |
| Object3D, |
| Raycaster, |
| Scene, |
| Vector2, |
| } from "three"; |
| import { |
| TransformControlsGizmo, |
| TransformControlsPlane, |
| } from "three/examples/jsm/controls/TransformControls"; |
| import Base from "./Base"; |
| |
| export default class EventManger extends EventDispatcher { |
| private base: Base; |
| private raycaster: Raycaster = new Raycaster(); |
| private mouse: Vector2 = new Vector2(); |
| private dom: HTMLCanvasElement; |
| private scene: Scene; |
| private camera: Camera; |
| constructor() { |
| super(); |
| const base = Base.getInstance(); |
| this.base = base; |
| |
| this.dom = base.canvas; |
| this.scene = base.scene; |
| this.camera = base.cameras.perspectiveCamera; |
| |
| const mouse = this.mouse; |
| const raycaster = this.raycaster; |
| const dom = this.dom; |
| |
| let cacheObject: Object3D | null = null; |
| |
| dom.addEventListener("mousedown", (event) => { |
| |
| raycaster.setFromCamera(mouse, this.camera); |
| const intersection = raycaster.intersectObjects(this.scene.children); |
| |
| |
| this.dispatchEvent({ |
| type: "mousedown", |
| intersection, |
| }); |
| |
| if (intersection.length) { |
| const object = intersection[0].object; |
| |
| object.dispatchEvent({ |
| type: "mousedown", |
| }); |
| } |
| }); |
| |
| dom.addEventListener("mousemove", (event) => { |
| mouse.x = (event.offsetX / dom.offsetWidth) * 2 - 1; |
| mouse.y = (-event.offsetY * 2) / dom.offsetHeight + 1; |
| |
| |
| raycaster.setFromCamera(mouse, this.camera); |
| |
| const intersection = raycaster.intersectObjects(this.scene.children); |
| |
| |
| this.dispatchEvent({ |
| type: "mousemove", |
| intersection, |
| }); |
| |
| if (intersection.length) { |
| const object = intersection[0].object; |
| |
| if (object !== cacheObject) { |
| if (cacheObject) { |
| cacheObject.dispatchEvent({ |
| type: "mouseleave", |
| }); |
| } |
| object.dispatchEvent({ |
| type: "mouseenter", |
| }); |
| } else if (object === cacheObject) { |
| object.dispatchEvent({ |
| type: "mousemove", |
| }); |
| } |
| cacheObject = object; |
| } else { |
| if (cacheObject) { |
| cacheObject.dispatchEvent({ |
| type: "mouseleave", |
| }); |
| } |
| cacheObject = null; |
| } |
| }); |
| |
| dom.addEventListener("mouseup", (event) => { |
| |
| raycaster.setFromCamera(mouse, this.camera); |
| const intersection = raycaster.intersectObjects(this.scene.children); |
| |
| this.dispatchEvent({ |
| type: "mouseup", |
| intersection, |
| }); |
| if (intersection.length) { |
| const object = intersection[0].object; |
| object.dispatchEvent({ |
| type: "mouseup", |
| }); |
| } |
| }); |
| |
| dom.addEventListener("click", (event) => { |
| |
| raycaster.setFromCamera(mouse, this.camera); |
| let intersections = raycaster.intersectObjects(this.scene.children); |
| |
| this.dispatchEvent({ |
| type: "click", |
| intersections, |
| }); |
| |
| |
| this.scene.remove(...this.base.noTransfromControlObjs); |
| intersections = raycaster.intersectObjects(this.scene.children); |
| |
| this.scene.add(...this.base.noTransfromControlObjs); |
| |
| const targetIntersection = intersections.filter((intersection) => { |
| return ( |
| |
| !(intersection.object instanceof TransformControlsPlane) && |
| !(intersection.object instanceof AxesHelper) && |
| !(intersection.object instanceof GridHelper) && |
| !(intersection.object instanceof Line) && |
| !(intersection.object.parent instanceof TransformControlsGizmo) |
| |
| ); |
| }); |
| |
| if (targetIntersection.length) { |
| const object = targetIntersection[0].object; |
| |
| console.log(object); |
| |
| object.dispatchEvent({ |
| type: "click", |
| }); |
| } |
| }); |
| } |
| } |
| import * as THREE from "three"; |
| import Base from "./Base"; |
| import { Scene, WebGLRenderer } from "three"; |
| import Camera from "./Cameras"; |
| import Size from "./Size"; |
| import { CSS2DRenderer } from "three/examples/jsm/renderers/CSS2DRenderer"; |
| |
| export default class Renderer { |
| private size: Size; |
| private scene: Scene; |
| private canvas: HTMLCanvasElement; |
| private camera: Camera; |
| private labelRenderer: CSS2DRenderer; |
| private renderer: WebGLRenderer; |
| |
| constructor() { |
| const base = Base.getInstance(); |
| |
| this.size = base.size; |
| this.scene = base.scene; |
| this.canvas = base.canvas; |
| this.camera = base.cameras; |
| |
| this.renderer = this.setRenderer(); |
| |
| const labelRenderer = this.setLabelRenderer(); |
| this.labelRenderer = labelRenderer; |
| } |
| |
| setRenderer() { |
| const renderer = new WebGLRenderer({ |
| canvas: this.canvas, |
| antialias: true, |
| }); |
| |
| renderer.physicallyCorrectLights = true; |
| renderer.outputEncoding = THREE.sRGBEncoding; |
| renderer.toneMapping = THREE.CineonToneMapping; |
| renderer.toneMappingExposure = 1.75; |
| renderer.shadowMap.enabled = true; |
| renderer.shadowMap.type = THREE.PCFSoftShadowMap; |
| renderer.setSize(this.size.width, this.size.height); |
| renderer.setPixelRatio(this.size.pixelRatio); |
| |
| return renderer; |
| } |
| private setLabelRenderer() { |
| const labelRenderer = new CSS2DRenderer(); |
| labelRenderer.setSize(this.size.width, this.size.height); |
| document.body.appendChild(labelRenderer.domElement); |
| return labelRenderer; |
| } |
| |
| resize() { |
| this.renderer.setSize(this.size.width, this.size.height); |
| this.renderer.setPixelRatio(this.size.pixelRatio); |
| |
| this.labelRenderer.setSize(this.size.width, this.size.height); |
| } |
| |
| update() { |
| this.renderer.render(this.scene, this.camera.perspectiveCamera); |
| this.labelRenderer.render(this.scene, this.camera.perspectiveCamera); |
| } |
| } |
| import { |
| TransformControls, |
| TransformControlsGizmo, |
| TransformControlsPlane, |
| } from "three/examples/jsm/controls/TransformControls"; |
| import Base from "./Base"; |
| import { |
| AxesHelper, |
| GridHelper, |
| Line, |
| MOUSE, |
| Object3D, |
| PerspectiveCamera, |
| Raycaster, |
| Scene, |
| Vector2, |
| } from "three"; |
| import { OrbitControls } from "three/examples/jsm/controls/OrbitControls"; |
| |
| export default class Control { |
| public transformControl: TransformControls; |
| public orbitControl: OrbitControls; |
| private base: Base; |
| private camera: PerspectiveCamera; |
| private canvas: HTMLCanvasElement; |
| private scene: Scene; |
| private transing: boolean; |
| constructor() { |
| const base = Base.getInstance(); |
| this.base = base; |
| |
| this.camera = base.cameras.perspectiveCamera; |
| this.canvas = base.canvas; |
| this.scene = base.scene; |
| |
| |
| this.transing = false; |
| this.transformControl = this.createTransformControl(); |
| this.initTransformControlEvent(); |
| this.scene.add(this.transformControl); |
| this.orbitControl = this.createOrbitControl(); |
| } |
| |
| private createTransformControl() { |
| const transformControls = new TransformControls(this.camera, this.canvas); |
| return transformControls; |
| } |
| |
| private initTransformControlEvent() { |
| this.transformControl.addEventListener("mouseDown", (e) => { |
| this.transing = true; |
| }); |
| } |
| |
| public update() { |
| this.orbitControl.update(); |
| } |
| |
| private createOrbitControl() { |
| const controls = new OrbitControls(this.camera, this.canvas); |
| controls.enableDamping = true; |
| controls.enableZoom = true; |
| |
| |
| |
| |
| |
| |
| |
| return controls; |
| } |
| } |
| import EventEmitter from "eventemitter2"; |
| import { Clock } from "three"; |
| |
| export default class Time extends EventEmitter { |
| public clock: Clock; |
| constructor() { |
| super(); |
| this.clock = new Clock(); |
| |
| this.update(); |
| } |
| |
| update() { |
| this.emit("update"); |
| window.requestAnimationFrame(() => this.update()); |
| } |
| } |
| import { BoxGeometry, MeshBasicMaterial, Mesh, Color } from "three"; |
| import { CSS2DObject } from "three/examples/jsm/renderers/CSS2DRenderer"; |
| |
| import Base from "../Base"; |
| |
| export default class Box { |
| private cube: Mesh; |
| private label: CSS2DObject; |
| private cubeMaterial: MeshBasicMaterial; |
| |
| constructor() { |
| const base = Base.getInstance(); |
| this.cubeMaterial = new MeshBasicMaterial({ color: 0x00ff00 }); |
| const cube = this.createCube(); |
| this.cube = cube; |
| const label = this.createLabel(); |
| this.label = label; |
| |
| base.scene.add(cube); |
| base.scene.add(label); |
| |
| this.initCubeEvents(); |
| } |
| |
| private createLabel() { |
| const oLabel = document.createElement("div"); |
| oLabel.className = "label"; |
| oLabel.innerHTML = `<div>导航到LM12</div>`; |
| |
| |
| oLabel.style.backgroundColor = "skyblue"; |
| |
| const label = new CSS2DObject(oLabel); |
| |
| return label; |
| } |
| private createCube() { |
| const cube = new Mesh(new BoxGeometry(1, 1, 1), this.cubeMaterial); |
| cube.position.y = 0.5; |
| return cube; |
| } |
| private initCubeEvents() { |
| this.cube.addEventListener("click", (e) => { |
| this.cubeMaterial.color = new Color(0xff0000); |
| this.label.position.set(2, 0, 0); |
| |
| this.label.element.innerHTML = "导航到LM13"; |
| }); |
| } |
| } |
| import { |
| BoxGeometry, |
| MeshBasicMaterial, |
| Mesh, |
| MeshLambertMaterial, |
| Color, |
| } from "three"; |
| import Base from "../Base"; |
| |
| export default class Box { |
| private cube: Mesh; |
| private cubeMaterial: MeshBasicMaterial; |
| |
| constructor() { |
| const base = Base.getInstance(); |
| this.cubeMaterial = new MeshBasicMaterial({ color: 0x00ff00 }); |
| const cube = this.createCube(); |
| this.cube = cube; |
| base.scene.add(cube); |
| const cube2 = new Mesh( |
| new BoxGeometry(1, 1, 1), |
| new MeshBasicMaterial({ color: 0x00ff00 }) |
| ); |
| cube2.position.setX(3); |
| base.scene.add(cube2); |
| |
| this.initCubeEvent(); |
| } |
| private createCube() { |
| const cube = new Mesh(new BoxGeometry(1, 1, 1), this.cubeMaterial); |
| cube.position.y = 0.5; |
| return cube; |
| } |
| private initCubeEvent() { |
| this.cube.addEventListener("mouseenter", (e) => { |
| console.log("鼠标进入"); |
| this.cubeMaterial.color = new Color(0xff0000); |
| }); |
| this.cube.addEventListener("mousemove", (e) => { |
| console.log("鼠标正在移动"); |
| }); |
| |
| this.cube.addEventListener("mouseleave", (e) => { |
| console.log("鼠标离开"); |
| this.cubeMaterial.color = new Color(0x00ff00); |
| }); |
| this.cube.addEventListener("click", (e) => { |
| console.log("鼠标点击"); |
| this.cubeMaterial.color = new Color(0x0000ff); |
| }); |
| } |
| } |
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
2022-02-09 es6的块级作用域
2022-02-09 var关键字和let/const关键字的区别
2022-02-09 cheerio 如何获取 数据属性data 中的数据
2022-02-09 Property 固有属性、Attribute 自定义属性 区别