1
0
mirror of https://github.com/troisjs/trois.git synced 2024-11-24 04:12:02 +08:00
This commit is contained in:
Kevin Levron 2021-04-16 03:19:50 +02:00
parent d2006b1f2c
commit de98dd8d11
9 changed files with 129 additions and 99 deletions

View File

@ -61,8 +61,8 @@ export default defineComponent({
} }
return false return false
}, },
add(o) { this.o3d.add(o); }, add(o: Object3D) { this.o3d.add(o) },
remove(o) { this.o3d.remove(o); }, remove(o: Object3D) { this.o3d.remove(o) },
}, },
render() { render() {
return this.$slots.default ? this.$slots.default() : [] return this.$slots.default ? this.$slots.default() : []

View File

@ -1,12 +1,11 @@
import { defineComponent, watch } from 'vue'; import { defineComponent, watch } from 'vue'
import { OrthographicCamera } from 'three'; import { OrthographicCamera } from 'three'
import { bindProp } from '../tools'; import { bindProp } from '../tools'
import Camera from './Camera'; import Camera from './Camera'
export default defineComponent({ export default defineComponent({
extends: Camera, extends: Camera,
name: 'OrthographicCamera', name: 'OrthographicCamera',
inject: ['three'],
props: { props: {
left: { type: Number, default: -1 }, left: { type: Number, default: -1 },
right: { type: Number, default: 1 }, right: { type: Number, default: 1 },
@ -15,20 +14,25 @@ export default defineComponent({
near: { type: Number, default: 0.1 }, near: { type: Number, default: 0.1 },
far: { type: Number, default: 2000 }, far: { type: Number, default: 2000 },
zoom: { type: Number, default: 1 }, 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() { created() {
this.camera = new OrthographicCamera(this.left, this.right, this.top, this.bottom, this.near, this.far);
bindProp(this, 'position', this.camera); bindProp(this, 'position', this.camera);
['left', 'right', 'top', 'bottom', 'near', 'far', 'zoom'].forEach(p => { ['left', 'right', 'top', 'bottom', 'near', 'far', 'zoom'].forEach(p => {
watch(() => this[p], () => { watch(() => this[p], () => {
this.camera[p] = 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', __hmrId: 'OrthographicCamera',
}); })

View File

@ -1,7 +1,7 @@
import { defineComponent, watch } from 'vue'; import { defineComponent, watch } from 'vue'
import { PerspectiveCamera } from 'three'; import { PerspectiveCamera } from 'three'
import { bindProp } from '../tools'; import { bindProp } from '../tools'
import Camera from './Camera'; import Camera from './Camera'
export default defineComponent({ export default defineComponent({
extends: Camera, extends: Camera,
@ -12,24 +12,30 @@ export default defineComponent({
far: { type: Number, default: 2000 }, far: { type: Number, default: 2000 },
fov: { type: Number, default: 50 }, fov: { type: Number, default: 50 },
near: { type: Number, default: 0.1 }, 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 }, lookAt: { type: Object, default: null },
}, },
setup(props) {
return {
camera: new PerspectiveCamera(props.fov, props.aspect, props.near, props.far),
}
},
created() { 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); // TODO : fix lookAt x
watch(() => this.lookAt, (v) => { this.camera.lookAt(v.x, v.y, v.z); }, { deep: true }); 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], () => { watch(() => this[p], () => {
this.camera[p] = this[p]; this.camera[p] = this[p]
this.camera.updateProjectionMatrix(); this.camera.updateProjectionMatrix()
}); })
}); })
this.three.camera = this.camera; this.three.camera = this.camera
}, },
__hmrId: 'PerspectiveCamera', __hmrId: 'PerspectiveCamera',
}); })

View File

@ -1,15 +1,19 @@
import { defineComponent } from 'vue'; import { Object3D } from 'three'
import usePointer from './usePointer'; import { defineComponent } from 'vue'
import usePointer, { PointerIntersectCallbackType } from './usePointer'
// eslint-disable-next-line @typescript-eslint/no-empty-function
const emptyCallBack: PointerIntersectCallbackType = () => {}
export default defineComponent({ export default defineComponent({
name: 'Raycaster', name: 'Raycaster',
inject: ['three', 'renderer'], inject: ['three', 'renderer'],
props: { props: {
onPointerEnter: { type: Function, default: () => {} }, onPointerEnter: { type: Function, default: emptyCallBack },
onPointerOver: { type: Function, default: () => {} }, onPointerOver: { type: Function, default: emptyCallBack },
onPointerMove: { type: Function, default: () => {} }, onPointerMove: { type: Function, default: emptyCallBack },
onPointerLeave: { type: Function, default: () => {} }, onPointerLeave: { type: Function, default: emptyCallBack },
onClick: { type: Function, default: () => {} }, onClick: { type: Function, default: emptyCallBack },
intersectMode: { type: String, default: 'move' }, intersectMode: { type: String, default: 'move' },
}, },
mounted() { mounted() {
@ -18,32 +22,32 @@ export default defineComponent({
camera: this.three.camera, camera: this.three.camera,
domElement: this.three.renderer.domElement, domElement: this.three.renderer.domElement,
intersectObjects: this.getIntersectObjects(), intersectObjects: this.getIntersectObjects(),
onIntersectEnter: this.onPointerEnter, onIntersectEnter: (<PointerIntersectCallbackType> this.onPointerEnter),
onIntersectOver: this.onPointerOver, onIntersectOver: (<PointerIntersectCallbackType> this.onPointerOver),
onIntersectMove: this.onPointerMove, onIntersectMove: (<PointerIntersectCallbackType> this.onPointerMove),
onIntersectLeave: this.onPointerLeave, onIntersectLeave: (<PointerIntersectCallbackType> this.onPointerLeave),
onIntersectClick: this.onClick, onIntersectClick: (<PointerIntersectCallbackType> this.onClick),
}); })
this.pointer.addListeners(); this.pointer.addListeners()
if (this.intersectMode === 'frame') { if (this.intersectMode === 'frame') {
this.renderer.onBeforeRender(this.pointer.intersect); this.renderer.onBeforeRender(this.pointer.intersect)
} }
}); })
}, },
unmounted() { unmounted() {
if (this.pointer) { if (this.pointer) {
this.pointer.removeListeners(); this.pointer.removeListeners()
this.renderer.offBeforeRender(this.pointer.intersect); this.renderer.offBeforeRender(this.pointer.intersect)
} }
}, },
methods: { methods: {
getIntersectObjects() { 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() { render() {
return []; return []
}, },
__hmrId: 'Raycaster', __hmrId: 'Raycaster',
}); })

View File

@ -1,5 +1,5 @@
import { defineComponent, watch } from 'vue'; import { defineComponent, watch } from 'vue'
import { Scene, Color } from 'three'; import { Scene, Color, Object3D } from 'three'
export default defineComponent({ export default defineComponent({
name: 'Scene', name: 'Scene',
@ -9,27 +9,29 @@ export default defineComponent({
background: [String, Number], background: [String, Number],
}, },
setup(props) { setup(props) {
const scene = new Scene(); const scene = new Scene()
if (props.background) scene.background = new Color(props.background); if (props.background) {
watch(() => props.background, (value) => { scene.background.set(value); }); scene.background = new Color(props.background)
return { scene }; }
watch(() => props.background, (value) => { if (scene.background instanceof Color && value) scene.background.set(value) })
return { scene }
}, },
provide() { provide() {
return { return {
scene: this.scene, scene: this.scene,
}; }
}, },
mounted() { mounted() {
if (!this.three.scene) { if (!this.three.scene) {
this.three.scene = this.scene; this.three.scene = this.scene
} }
}, },
methods: { methods: {
add(o) { this.scene.add(o); }, add(o: Object3D) { this.scene.add(o) },
remove(o) { this.scene.remove(o); }, remove(o: Object3D) { this.scene.remove(o) },
}, },
render() { render() {
return this.$slots.default ? this.$slots.default() : []; return this.$slots.default ? this.$slots.default() : []
}, },
__hmrId: 'Scene', __hmrId: 'Scene',
}); })

View File

@ -1,8 +1,8 @@
export { default as Renderer } from './Renderer'; export { default as Renderer } from './Renderer'
export { default as OrthographicCamera } from './OrthographicCamera'; export { default as OrthographicCamera } from './OrthographicCamera'
export { default as PerspectiveCamera } from './PerspectiveCamera'; export { default as PerspectiveCamera } from './PerspectiveCamera'
export { default as Camera } from './PerspectiveCamera'; export { default as Camera } from './PerspectiveCamera'
export { default as Group } from './Group'; export { default as Group } from './Group'
export { default as Scene } from './Scene'; export { default as Scene } from './Scene'
export { default as Object3D } from './Object3D'; export { default as Object3D } from './Object3D'
export { default as Raycaster } from './Raycaster'; export { default as Raycaster } from './Raycaster'

View File

@ -16,6 +16,8 @@ export interface PointerIntersectEventInterface {
intersect?: Intersection intersect?: Intersection
} }
export type PointerIntersectCallbackType = (e: PointerIntersectEventInterface) => void
export type IntersectObject = Mesh | InstancedMesh export type IntersectObject = Mesh | InstancedMesh
export interface PointerConfigInterface { export interface PointerConfigInterface {
@ -31,11 +33,11 @@ export interface PointerConfigInterface {
onMove?(e: PointerEventInterface): void onMove?(e: PointerEventInterface): void
onLeave?(e: PointerEventInterface): void onLeave?(e: PointerEventInterface): void
onClick?(e: PointerEventInterface): void onClick?(e: PointerEventInterface): void
onIntersectEnter?(e: PointerIntersectEventInterface): void onIntersectEnter: PointerIntersectCallbackType
onIntersectOver?(e: PointerIntersectEventInterface): void onIntersectOver: PointerIntersectCallbackType
onIntersectMove?(e: PointerIntersectEventInterface): void onIntersectMove: PointerIntersectCallbackType
onIntersectLeave?(e: PointerIntersectEventInterface): void onIntersectLeave: PointerIntersectCallbackType
onIntersectClick?(e: PointerIntersectEventInterface): void onIntersectClick: PointerIntersectCallbackType
} }
export interface PointerInterface { export interface PointerInterface {

View File

@ -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 { const {
camera, camera,
resetPosition = new Vector3(0, 0, 0), resetPosition = new Vector3(0, 0, 0),
} = options; } = options
const raycaster = new Raycaster(); const raycaster = new Raycaster()
const position = resetPosition.clone(); const position = resetPosition.clone()
const plane = new Plane(new Vector3(0, 0, 1), 0); const plane = new Plane(new Vector3(0, 0, 1), 0)
const updatePosition = (coords) => { const updatePosition = (coords: Vector2) => {
raycaster.setFromCamera(coords, camera); raycaster.setFromCamera(coords, camera)
camera.getWorldDirection(plane.normal); camera.getWorldDirection(plane.normal)
raycaster.ray.intersectPlane(plane, position); raycaster.ray.intersectPlane(plane, position)
}; }
const intersect = (coords, objects) => { const intersect = (coords: Vector2, objects: IntersectObject[]) => {
raycaster.setFromCamera(coords, camera); raycaster.setFromCamera(coords, camera)
return raycaster.intersectObjects(objects); return raycaster.intersectObjects(objects)
}; }
return { return {
position, position,
updatePosition, updatePosition,
intersect, intersect,
}; }
}; }

View File

@ -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 { export function bindProp(src: any, srcProp: string, dst: any, dstProp?: string): void {
if (!dstProp) dstProp = srcProp const _dstProp = dstProp || srcProp
const ref = toRef(src, srcProp) const ref = toRef(src, srcProp)
if (ref.value instanceof Object) { if (ref.value instanceof Object) {
setFromProp(dst[dstProp], ref.value) setFromProp(dst[_dstProp], ref.value)
watch(ref, (value) => { setFromProp(dst[dstProp], value) }, { deep: true }) watch(ref, (value) => { setFromProp(dst[_dstProp], value) }, { deep: true })
} else { } else {
if (ref.value) dst[dstProp] = src[srcProp] if (ref.value) dst[_dstProp] = src[srcProp]
watch(ref, (value) => { dst[dstProp] = value }) watch(ref, (value) => { dst[_dstProp] = value })
} }
} }