diff --git a/src/core/Object3D.ts b/src/core/Object3D.ts index f597000..b4dba13 100644 --- a/src/core/Object3D.ts +++ b/src/core/Object3D.ts @@ -61,8 +61,8 @@ export default defineComponent({ } return false }, - add(o) { this.o3d.add(o); }, - remove(o) { this.o3d.remove(o); }, + add(o: Object3D) { this.o3d.add(o) }, + remove(o: Object3D) { this.o3d.remove(o) }, }, render() { return this.$slots.default ? this.$slots.default() : [] diff --git a/src/core/OrthographicCamera.ts b/src/core/OrthographicCamera.ts index bd7ead1..370019b 100644 --- a/src/core/OrthographicCamera.ts +++ b/src/core/OrthographicCamera.ts @@ -1,12 +1,11 @@ -import { defineComponent, watch } from 'vue'; -import { OrthographicCamera } from 'three'; -import { bindProp } from '../tools'; -import Camera from './Camera'; +import { defineComponent, watch } from 'vue' +import { OrthographicCamera } from 'three' +import { bindProp } from '../tools' +import Camera from './Camera' export default defineComponent({ extends: Camera, name: 'OrthographicCamera', - inject: ['three'], props: { left: { type: Number, default: -1 }, right: { type: Number, default: 1 }, @@ -15,20 +14,25 @@ export default defineComponent({ near: { type: Number, default: 0.1 }, far: { type: Number, default: 2000 }, zoom: { type: Number, default: 1 }, - position: { type: Object, default: { x: 0, y: 0, z: 0 } }, + position: { type: Object, default: () => ({ x: 0, y: 0, z: 0 }) }, + }, + setup(props) { + const camera = new OrthographicCamera(props.left, props.right, props.top, props.bottom, props.near, props.far) + return { + camera, + } }, created() { - this.camera = new OrthographicCamera(this.left, this.right, this.top, this.bottom, this.near, this.far); bindProp(this, 'position', this.camera); ['left', 'right', 'top', 'bottom', 'near', 'far', 'zoom'].forEach(p => { watch(() => this[p], () => { this.camera[p] = this[p]; - this.camera.updateProjectionMatrix(); - }); - }); + this.camera.updateProjectionMatrix() + }) + }) - this.three.camera = this.camera; + this.three.camera = this.camera }, __hmrId: 'OrthographicCamera', -}); +}) diff --git a/src/core/PerspectiveCamera.ts b/src/core/PerspectiveCamera.ts index 2f7e7cf..cf2ea18 100644 --- a/src/core/PerspectiveCamera.ts +++ b/src/core/PerspectiveCamera.ts @@ -1,7 +1,7 @@ -import { defineComponent, watch } from 'vue'; -import { PerspectiveCamera } from 'three'; -import { bindProp } from '../tools'; -import Camera from './Camera'; +import { defineComponent, watch } from 'vue' +import { PerspectiveCamera } from 'three' +import { bindProp } from '../tools' +import Camera from './Camera' export default defineComponent({ extends: Camera, @@ -12,24 +12,30 @@ export default defineComponent({ far: { type: Number, default: 2000 }, fov: { type: Number, default: 50 }, near: { type: Number, default: 0.1 }, - position: { type: Object, default: { x: 0, y: 0, z: 0 } }, + position: { type: Object, default: () => ({ x: 0, y: 0, z: 0 }) }, lookAt: { type: Object, default: null }, }, + setup(props) { + return { + camera: new PerspectiveCamera(props.fov, props.aspect, props.near, props.far), + } + }, created() { - this.camera = new PerspectiveCamera(this.fov, this.aspect, this.near, this.far); - bindProp(this, 'position', this.camera); + bindProp(this, 'position', this.camera) - if (this.lookAt) this.camera.lookAt(this.lookAt.x, this.lookAt.y, this.lookAt.z); - watch(() => this.lookAt, (v) => { this.camera.lookAt(v.x, v.y, v.z); }, { deep: true }); + // TODO : fix lookAt x + if (this.lookAt) this.camera.lookAt(this.lookAt.x, this.lookAt.y, this.lookAt.z) + watch(() => this.lookAt, (v) => { this.camera.lookAt(v.x, v.y, v.z) }, { deep: true }) - ['aspect', 'far', 'fov', 'near'].forEach(p => { + const watchProps = ['aspect', 'far', 'fov', 'near'] + watchProps.forEach(p => { watch(() => this[p], () => { - this.camera[p] = this[p]; - this.camera.updateProjectionMatrix(); - }); - }); + this.camera[p] = this[p] + this.camera.updateProjectionMatrix() + }) + }) - this.three.camera = this.camera; + this.three.camera = this.camera }, __hmrId: 'PerspectiveCamera', -}); +}) diff --git a/src/core/Raycaster.ts b/src/core/Raycaster.ts index c166730..baacc96 100644 --- a/src/core/Raycaster.ts +++ b/src/core/Raycaster.ts @@ -1,15 +1,19 @@ -import { defineComponent } from 'vue'; -import usePointer from './usePointer'; +import { Object3D } from 'three' +import { defineComponent } from 'vue' +import usePointer, { PointerIntersectCallbackType } from './usePointer' + +// eslint-disable-next-line @typescript-eslint/no-empty-function +const emptyCallBack: PointerIntersectCallbackType = () => {} export default defineComponent({ name: 'Raycaster', inject: ['three', 'renderer'], props: { - onPointerEnter: { type: Function, default: () => {} }, - onPointerOver: { type: Function, default: () => {} }, - onPointerMove: { type: Function, default: () => {} }, - onPointerLeave: { type: Function, default: () => {} }, - onClick: { type: Function, default: () => {} }, + onPointerEnter: { type: Function, default: emptyCallBack }, + onPointerOver: { type: Function, default: emptyCallBack }, + onPointerMove: { type: Function, default: emptyCallBack }, + onPointerLeave: { type: Function, default: emptyCallBack }, + onClick: { type: Function, default: emptyCallBack }, intersectMode: { type: String, default: 'move' }, }, mounted() { @@ -18,32 +22,32 @@ export default defineComponent({ camera: this.three.camera, domElement: this.three.renderer.domElement, intersectObjects: this.getIntersectObjects(), - onIntersectEnter: this.onPointerEnter, - onIntersectOver: this.onPointerOver, - onIntersectMove: this.onPointerMove, - onIntersectLeave: this.onPointerLeave, - onIntersectClick: this.onClick, - }); - this.pointer.addListeners(); + onIntersectEnter: ( this.onPointerEnter), + onIntersectOver: ( this.onPointerOver), + onIntersectMove: ( this.onPointerMove), + onIntersectLeave: ( this.onPointerLeave), + onIntersectClick: ( this.onClick), + }) + this.pointer.addListeners() if (this.intersectMode === 'frame') { - this.renderer.onBeforeRender(this.pointer.intersect); + this.renderer.onBeforeRender(this.pointer.intersect) } - }); + }) }, unmounted() { if (this.pointer) { - this.pointer.removeListeners(); - this.renderer.offBeforeRender(this.pointer.intersect); + this.pointer.removeListeners() + this.renderer.offBeforeRender(this.pointer.intersect) } }, methods: { getIntersectObjects() { - return this.three.scene.children.filter(e => e.type === 'Mesh'); + return this.three.scene.children.filter((c: Object3D) => ['Mesh', 'InstancedMesh'].includes(c.type)) }, }, render() { - return []; + return [] }, __hmrId: 'Raycaster', -}); +}) diff --git a/src/core/Scene.ts b/src/core/Scene.ts index faab7ed..855fcb9 100644 --- a/src/core/Scene.ts +++ b/src/core/Scene.ts @@ -1,5 +1,5 @@ -import { defineComponent, watch } from 'vue'; -import { Scene, Color } from 'three'; +import { defineComponent, watch } from 'vue' +import { Scene, Color, Object3D } from 'three' export default defineComponent({ name: 'Scene', @@ -9,27 +9,29 @@ export default defineComponent({ background: [String, Number], }, setup(props) { - const scene = new Scene(); - if (props.background) scene.background = new Color(props.background); - watch(() => props.background, (value) => { scene.background.set(value); }); - return { scene }; + const scene = new Scene() + if (props.background) { + scene.background = new Color(props.background) + } + watch(() => props.background, (value) => { if (scene.background instanceof Color && value) scene.background.set(value) }) + return { scene } }, provide() { return { scene: this.scene, - }; + } }, mounted() { if (!this.three.scene) { - this.three.scene = this.scene; + this.three.scene = this.scene } }, methods: { - add(o) { this.scene.add(o); }, - remove(o) { this.scene.remove(o); }, + add(o: Object3D) { this.scene.add(o) }, + remove(o: Object3D) { this.scene.remove(o) }, }, render() { - return this.$slots.default ? this.$slots.default() : []; + return this.$slots.default ? this.$slots.default() : [] }, __hmrId: 'Scene', -}); +}) diff --git a/src/core/index.js b/src/core/index.js index 95ea447..c007f6e 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -1,8 +1,8 @@ -export { default as Renderer } from './Renderer'; -export { default as OrthographicCamera } from './OrthographicCamera'; -export { default as PerspectiveCamera } from './PerspectiveCamera'; -export { default as Camera } from './PerspectiveCamera'; -export { default as Group } from './Group'; -export { default as Scene } from './Scene'; -export { default as Object3D } from './Object3D'; -export { default as Raycaster } from './Raycaster'; +export { default as Renderer } from './Renderer' +export { default as OrthographicCamera } from './OrthographicCamera' +export { default as PerspectiveCamera } from './PerspectiveCamera' +export { default as Camera } from './PerspectiveCamera' +export { default as Group } from './Group' +export { default as Scene } from './Scene' +export { default as Object3D } from './Object3D' +export { default as Raycaster } from './Raycaster' diff --git a/src/core/usePointer.ts b/src/core/usePointer.ts index 85eb9ff..d7893c2 100644 --- a/src/core/usePointer.ts +++ b/src/core/usePointer.ts @@ -16,6 +16,8 @@ export interface PointerIntersectEventInterface { intersect?: Intersection } +export type PointerIntersectCallbackType = (e: PointerIntersectEventInterface) => void + export type IntersectObject = Mesh | InstancedMesh export interface PointerConfigInterface { @@ -31,11 +33,11 @@ export interface PointerConfigInterface { onMove?(e: PointerEventInterface): void onLeave?(e: PointerEventInterface): void onClick?(e: PointerEventInterface): void - onIntersectEnter?(e: PointerIntersectEventInterface): void - onIntersectOver?(e: PointerIntersectEventInterface): void - onIntersectMove?(e: PointerIntersectEventInterface): void - onIntersectLeave?(e: PointerIntersectEventInterface): void - onIntersectClick?(e: PointerIntersectEventInterface): void + onIntersectEnter: PointerIntersectCallbackType + onIntersectOver: PointerIntersectCallbackType + onIntersectMove: PointerIntersectCallbackType + onIntersectLeave: PointerIntersectCallbackType + onIntersectClick: PointerIntersectCallbackType } export interface PointerInterface { diff --git a/src/core/useRaycaster.ts b/src/core/useRaycaster.ts index 6d8b62e..c161517 100644 --- a/src/core/useRaycaster.ts +++ b/src/core/useRaycaster.ts @@ -1,29 +1,41 @@ -import { Plane, Raycaster, Vector3 } from 'three'; +import { Camera, Intersection, Plane, Raycaster, Vector2, Vector3 } from 'three'; +import { IntersectObject } from './usePointer' -export default function useRaycaster(options) { +export interface RaycasterInterface { + position: Vector3 + updatePosition(coords: Vector2): void + intersect(coords: Vector2, objects: IntersectObject[]): Intersection[], +} + +export interface RaycasterConfigInterface { + camera: Camera + resetPosition?: Vector3 +} + +export default function useRaycaster(options: RaycasterConfigInterface): RaycasterInterface { const { camera, resetPosition = new Vector3(0, 0, 0), - } = options; + } = options - const raycaster = new Raycaster(); - const position = resetPosition.clone(); - const plane = new Plane(new Vector3(0, 0, 1), 0); + const raycaster = new Raycaster() + const position = resetPosition.clone() + const plane = new Plane(new Vector3(0, 0, 1), 0) - const updatePosition = (coords) => { - raycaster.setFromCamera(coords, camera); - camera.getWorldDirection(plane.normal); - raycaster.ray.intersectPlane(plane, position); - }; + const updatePosition = (coords: Vector2) => { + raycaster.setFromCamera(coords, camera) + camera.getWorldDirection(plane.normal) + raycaster.ray.intersectPlane(plane, position) + } - const intersect = (coords, objects) => { - raycaster.setFromCamera(coords, camera); - return raycaster.intersectObjects(objects); - }; + const intersect = (coords: Vector2, objects: IntersectObject[]) => { + raycaster.setFromCamera(coords, camera) + return raycaster.intersectObjects(objects) + } return { position, updatePosition, intersect, - }; -}; + } +} diff --git a/src/tools.ts b/src/tools.ts index 53fbc66..ffe5bb1 100644 --- a/src/tools.ts +++ b/src/tools.ts @@ -15,15 +15,15 @@ export function bindProps(src: any, props: string[], dst: string): void { }) } -export function bindProp(src: any, srcProp: string, dst: any, dstProp: string): void { - if (!dstProp) dstProp = srcProp +export function bindProp(src: any, srcProp: string, dst: any, dstProp?: string): void { + const _dstProp = dstProp || srcProp const ref = toRef(src, srcProp) if (ref.value instanceof Object) { - setFromProp(dst[dstProp], ref.value) - watch(ref, (value) => { setFromProp(dst[dstProp], value) }, { deep: true }) + setFromProp(dst[_dstProp], ref.value) + watch(ref, (value) => { setFromProp(dst[_dstProp], value) }, { deep: true }) } else { - if (ref.value) dst[dstProp] = src[srcProp] - watch(ref, (value) => { dst[dstProp] = value }) + if (ref.value) dst[_dstProp] = src[srcProp] + watch(ref, (value) => { dst[_dstProp] = value }) } }