1
0
mirror of https://github.com/troisjs/trois.git synced 2024-11-25 04:42:01 +08:00
trois/src/core/Renderer.ts

262 lines
8.2 KiB
TypeScript
Raw Normal View History

2021-04-21 07:23:09 +08:00
/* eslint-disable no-use-before-define */
2021-04-22 03:05:02 +08:00
import { Camera, Scene, WebGLRenderer } from 'three'
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer'
2021-04-25 03:45:57 +08:00
import { defineComponent, InjectionKey, PropType } from 'vue'
2021-04-20 01:37:11 +08:00
import useThree, { SizeInterface, ThreeConfigInterface, ThreeInterface } from './useThree'
2021-04-16 07:56:52 +08:00
2021-04-22 03:05:02 +08:00
type CallbackType<T> = (event: T) => void
2021-04-19 09:22:02 +08:00
2021-04-21 07:23:09 +08:00
// type EventType = 'init' | 'mounted' | 'beforerender' | 'afterrender' | 'resize'
2021-04-22 03:05:02 +08:00
export interface EventInterface {
2021-04-21 07:23:09 +08:00
type: 'init' | 'mounted'
renderer: RendererInterface
2021-04-19 09:22:02 +08:00
}
2021-04-22 03:05:02 +08:00
export interface RenderEventInterface {
2021-04-21 07:23:09 +08:00
type: 'beforerender' | 'afterrender'
renderer: RendererInterface
2021-04-19 09:22:02 +08:00
time: number
}
2021-04-22 03:05:02 +08:00
export interface ResizeEventInterface {
2021-04-21 07:23:09 +08:00
type: 'resize'
renderer: RendererInterface
size: SizeInterface
}
type InitCallbackType = CallbackType<EventInterface>
type MountedCallbackType = CallbackType<EventInterface>
type RenderCallbackType = CallbackType<RenderEventInterface>
type ResizeCallbackType = CallbackType<ResizeEventInterface>
2021-04-22 01:10:01 +08:00
// type CallbackTypes = InitCallbackType | MountedCallbackType | RenderCallbackType | ResizeCallbackType
2021-04-21 07:23:09 +08:00
// interface EventMap {
// 'init': EventInterface;
// 'mounted': EventInterface;
// 'beforerender': RenderEventInterface;
// 'afterrender': RenderEventInterface;
// 'resize': ResizeEventInterface;
// }
interface EventCallbackMap {
'init': InitCallbackType;
'mounted': MountedCallbackType;
'beforerender': RenderCallbackType;
'afterrender': RenderCallbackType;
'resize': ResizeCallbackType;
}
2021-04-20 01:37:11 +08:00
interface RendererSetupInterface {
2021-04-19 09:22:02 +08:00
canvas: HTMLCanvasElement
three: ThreeInterface
renderer: WebGLRenderer
2021-04-20 01:37:11 +08:00
size: SizeInterface
2021-04-19 09:22:02 +08:00
renderFn(): void
raf: boolean
2021-04-21 07:23:09 +08:00
2021-04-20 01:37:11 +08:00
// pointerPosition?: Vector2
// pointerPositionN?: Vector2
// pointerPositionV3?: Vector3
2021-04-21 07:23:09 +08:00
initCallbacks: InitCallbackType[]
mountedCallbacks: MountedCallbackType[]
beforeRenderCallbacks: RenderCallbackType[]
afterRenderCallbacks: RenderCallbackType[]
resizeCallbacks: ResizeCallbackType[]
2021-04-19 04:27:53 +08:00
}
2021-04-20 01:37:11 +08:00
export interface RendererInterface extends RendererSetupInterface {
2021-04-22 03:05:02 +08:00
scene?: Scene
camera?: Camera
composer?: EffectComposer
2021-04-21 20:24:32 +08:00
onInit(cb: InitCallbackType): void
onMounted(cb: MountedCallbackType): void
2021-04-21 07:23:09 +08:00
2021-04-21 20:24:32 +08:00
onBeforeRender(cb: RenderCallbackType): void
offBeforeRender(cb: RenderCallbackType): void
onAfterRender(cb: RenderCallbackType): void
offAfterRender(cb: RenderCallbackType): void
2021-04-21 07:23:09 +08:00
2021-04-21 20:24:32 +08:00
onResize(cb: ResizeCallbackType): void
offResize(cb: ResizeCallbackType): void
2021-04-21 07:23:09 +08:00
addListener<T extends keyof EventCallbackMap>(t: T, cb: EventCallbackMap[T]): void
removeListener<T extends keyof EventCallbackMap>(t: T, cb: EventCallbackMap[T]): void
2021-04-20 01:37:11 +08:00
}
2021-04-25 03:45:57 +08:00
export const RendererInjectionKey: InjectionKey<RendererInterface> = Symbol('Renderer')
2021-04-16 07:56:52 +08:00
export default defineComponent({
name: 'Renderer',
props: {
antialias: Boolean,
alpha: Boolean,
autoClear: { type: Boolean, default: true },
orbitCtrl: { type: [Boolean, Object], default: false },
pointer: { type: [Boolean, Object], default: false },
resize: { type: [Boolean, String], default: false },
shadow: Boolean,
width: String,
height: String,
xr: Boolean,
2021-04-21 07:23:09 +08:00
onReady: Function as PropType<(r: RendererInterface) => void>,
2021-04-22 18:02:41 +08:00
onClick: Function as PropType<(this: HTMLCanvasElement, ev: MouseEvent) => any>,
2021-04-16 07:56:52 +08:00
},
2021-04-20 01:37:11 +08:00
setup(props): RendererSetupInterface {
2021-04-21 07:23:09 +08:00
const initCallbacks: InitCallbackType[] = []
const mountedCallbacks: MountedCallbackType[] = []
const beforeRenderCallbacks: RenderCallbackType[] = []
const afterRenderCallbacks: RenderCallbackType[] = []
const resizeCallbacks: ResizeCallbackType[] = []
2021-04-19 04:27:53 +08:00
const canvas = document.createElement('canvas')
const config: ThreeConfigInterface = {
canvas,
antialias: props.antialias,
alpha: props.alpha,
autoClear: props.autoClear,
orbitCtrl: props.orbitCtrl,
pointer: props.pointer,
resize: props.resize,
}
if (props.width) config.width = parseInt(props.width)
if (props.height) config.height = parseInt(props.height)
const three = useThree(config)
const renderFn: {(): void} = () => {}
2021-04-16 07:56:52 +08:00
2021-04-22 18:02:41 +08:00
// we have to handle canvas events ourself (it is not rendered by vue)
if (props.onClick) {
canvas.addEventListener('click', props.onClick)
}
2021-04-16 07:56:52 +08:00
return {
2021-04-19 04:27:53 +08:00
canvas,
three,
renderer: three.renderer,
2021-04-20 01:37:11 +08:00
size: three.size,
2021-04-19 04:27:53 +08:00
renderFn,
2021-04-16 07:56:52 +08:00
raf: true,
2021-04-21 07:23:09 +08:00
initCallbacks,
mountedCallbacks,
2021-04-16 07:56:52 +08:00
beforeRenderCallbacks,
afterRenderCallbacks,
2021-04-21 07:23:09 +08:00
resizeCallbacks,
2021-04-16 07:56:52 +08:00
}
},
2021-04-22 03:05:02 +08:00
computed: {
camera: {
get: function(): Camera | undefined { return this.three.camera },
set: function(camera: Camera): void { this.three.camera = camera },
},
scene: {
get: function(): Scene | undefined { return this.three.scene },
set: function(scene: Scene): void { this.three.scene = scene },
},
composer: {
get: function(): EffectComposer | undefined { return this.three.composer },
set: function(composer: EffectComposer): void { this.three.composer = composer },
},
},
2021-04-16 07:56:52 +08:00
provide() {
return {
2021-04-25 03:45:57 +08:00
[RendererInjectionKey as symbol]: this,
2021-04-16 07:56:52 +08:00
}
},
mounted() {
2021-04-19 04:27:53 +08:00
// appendChild won't work on reload
this.$el.parentNode.insertBefore(this.canvas, this.$el)
2021-04-16 07:56:52 +08:00
2021-04-19 04:27:53 +08:00
if (this.three.init()) {
2021-04-20 01:37:11 +08:00
// if (this.three.pointer) {
// this.pointerPosition = this.three.pointer.position
// this.pointerPositionN = this.three.pointer.positionN
// this.pointerPositionV3 = this.three.pointer.positionV3
// }
2021-04-21 20:24:32 +08:00
// TODO : don't use config
2021-04-21 07:23:09 +08:00
this.three.config.onResize = (size) => {
this.resizeCallbacks.forEach(e => e({ type: 'resize', renderer: this, size }))
}
2021-04-21 20:24:32 +08:00
// TODO : improve shadow params
2021-04-16 07:56:52 +08:00
this.renderer.shadowMap.enabled = this.shadow
2021-04-19 04:27:53 +08:00
this.renderFn = this.three.composer ? this.three.renderC : this.three.render
2021-04-16 07:56:52 +08:00
2021-04-21 07:23:09 +08:00
this.initCallbacks.forEach(e => e({ type: 'init', renderer: this }))
this.onReady?.(this as RendererInterface)
2021-04-16 07:56:52 +08:00
if (this.xr) {
this.renderer.xr.enabled = true
this.renderer.setAnimationLoop(this.render)
} else {
requestAnimationFrame(this.renderLoop)
}
2021-04-16 10:12:41 +08:00
}
2021-04-16 07:56:52 +08:00
2021-04-21 07:23:09 +08:00
this.mountedCallbacks.forEach(e => e({ type: 'mounted', renderer: this }))
2021-04-16 07:56:52 +08:00
},
beforeUnmount() {
2021-04-19 04:27:53 +08:00
this.canvas.remove()
2021-04-16 07:56:52 +08:00
this.beforeRenderCallbacks = []
this.afterRenderCallbacks = []
this.raf = false
this.three.dispose()
},
methods: {
2021-04-21 20:24:32 +08:00
onInit(cb: InitCallbackType) { this.addListener('init', cb) },
onMounted(cb: MountedCallbackType) { this.addListener('mounted', cb) },
onBeforeRender(cb: RenderCallbackType) { this.addListener('beforerender', cb) },
offBeforeRender(cb: RenderCallbackType) { this.removeListener('beforerender', cb) },
onAfterRender(cb: RenderCallbackType) { this.addListener('afterrender', cb) },
offAfterRender(cb: RenderCallbackType) { this.removeListener('afterrender', cb) },
onResize(cb: ResizeCallbackType) { this.addListener('resize', cb) },
offResize(cb: ResizeCallbackType) { this.removeListener('resize', cb) },
2021-04-22 01:10:01 +08:00
addListener(type: string, cb: {(e?: any): void}) {
2021-04-21 07:23:09 +08:00
const callbacks = this.getCallbacks(type)
callbacks.push(cb)
2021-04-16 07:56:52 +08:00
},
2021-04-22 01:10:01 +08:00
removeListener(type: string, cb: {(e?: any): void}) {
2021-04-21 07:23:09 +08:00
const callbacks = this.getCallbacks(type)
const index = callbacks.indexOf(cb)
if (index) callbacks.splice(index, 1)
2021-04-16 07:56:52 +08:00
},
2021-04-22 01:10:01 +08:00
2021-04-21 07:23:09 +08:00
getCallbacks(type: string) {
if (type === 'init') {
return this.initCallbacks
} else if (type === 'mounted') {
return this.mountedCallbacks
} else if (type === 'beforerender') {
return this.beforeRenderCallbacks
} else if (type === 'afterrender') {
return this.afterRenderCallbacks
} else {
return this.resizeCallbacks
}
2021-04-16 07:56:52 +08:00
},
2021-04-22 01:10:01 +08:00
2021-04-16 07:56:52 +08:00
render(time: number) {
2021-04-21 07:23:09 +08:00
this.beforeRenderCallbacks.forEach(e => e({ type: 'beforerender', renderer: this, time }))
2021-04-19 09:22:02 +08:00
// this.onFrame?.(cbParams)
2021-04-19 04:27:53 +08:00
this.renderFn()
2021-04-21 07:23:09 +08:00
this.afterRenderCallbacks.forEach(e => e({ type: 'afterrender', renderer: this, time }))
2021-04-16 07:56:52 +08:00
},
renderLoop(time: number) {
if (this.raf) requestAnimationFrame(this.renderLoop)
this.render(time)
},
},
render() {
2021-04-19 04:27:53 +08:00
return this.$slots.default ? this.$slots.default() : []
2021-04-16 07:56:52 +08:00
},
__hmrId: 'Renderer',
})