2021-04-19 09:22:02 +08:00
|
|
|
import { WebGLRenderer } from 'three'
|
2021-04-19 04:27:53 +08:00
|
|
|
import { defineComponent } from 'vue'
|
2021-04-19 09:22:02 +08:00
|
|
|
import useThree, { ThreeConfigInterface, ThreeInterface } from './useThree'
|
2021-04-16 07:56:52 +08:00
|
|
|
|
2021-04-19 09:22:02 +08:00
|
|
|
type CallbackType<T> = (e: T) => void
|
|
|
|
|
|
|
|
interface EventInterface<T> {
|
|
|
|
renderer: T
|
|
|
|
}
|
|
|
|
|
|
|
|
interface RenderEventInterface<T> extends EventInterface<T> {
|
|
|
|
time: number
|
|
|
|
}
|
|
|
|
|
|
|
|
export interface RendererInterface {
|
|
|
|
canvas: HTMLCanvasElement
|
|
|
|
three: ThreeInterface
|
|
|
|
renderer: WebGLRenderer
|
|
|
|
renderFn(): void
|
|
|
|
raf: boolean
|
|
|
|
onMountedCallbacks: CallbackType<EventInterface<this>>[]
|
|
|
|
beforeRenderCallbacks: CallbackType<RenderEventInterface<this>>[]
|
|
|
|
afterRenderCallbacks: CallbackType<RenderEventInterface<this>>[]
|
2021-04-19 04:27:53 +08:00
|
|
|
}
|
|
|
|
|
2021-04-19 09:22:02 +08:00
|
|
|
type MountedCallbackType = CallbackType<EventInterface<RendererInterface>>
|
|
|
|
type RenderCallbackType = CallbackType<RenderEventInterface<RendererInterface>>
|
2021-04-19 04:27:53 +08:00
|
|
|
|
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-19 04:27:53 +08:00
|
|
|
onReady: Function,
|
2021-04-19 09:22:02 +08:00
|
|
|
// onFrame: Function,
|
2021-04-16 07:56:52 +08:00
|
|
|
},
|
2021-04-19 09:22:02 +08:00
|
|
|
setup(props): RendererInterface {
|
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
|
|
|
|
|
|
|
const onMountedCallbacks: {(): void}[] = []
|
|
|
|
const beforeRenderCallbacks: {(): void}[] = []
|
|
|
|
const afterRenderCallbacks: {(): void}[] = []
|
|
|
|
|
|
|
|
return {
|
2021-04-19 04:27:53 +08:00
|
|
|
canvas,
|
|
|
|
three,
|
|
|
|
renderer: three.renderer,
|
|
|
|
renderFn,
|
2021-04-16 07:56:52 +08:00
|
|
|
raf: true,
|
|
|
|
onMountedCallbacks,
|
|
|
|
beforeRenderCallbacks,
|
|
|
|
afterRenderCallbacks,
|
|
|
|
}
|
|
|
|
},
|
|
|
|
provide() {
|
|
|
|
return {
|
|
|
|
renderer: this,
|
|
|
|
three: this.three,
|
|
|
|
}
|
|
|
|
},
|
|
|
|
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()) {
|
|
|
|
this.onReady?.(this)
|
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
|
|
|
|
|
|
|
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-19 09:22:02 +08:00
|
|
|
this.onMountedCallbacks.forEach(c => c({ 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: {
|
|
|
|
onMounted(cb: {(): void}) {
|
|
|
|
this.onMountedCallbacks.push(cb)
|
|
|
|
},
|
|
|
|
onBeforeRender(cb: {(): void}) {
|
|
|
|
this.beforeRenderCallbacks.push(cb)
|
|
|
|
},
|
|
|
|
offBeforeRender(cb: {(): void}) {
|
|
|
|
this.beforeRenderCallbacks = this.beforeRenderCallbacks.filter(c => c !== cb)
|
|
|
|
},
|
|
|
|
onAfterRender(cb: {(): void}) {
|
|
|
|
this.afterRenderCallbacks.push(cb)
|
|
|
|
},
|
|
|
|
offAfterRender(cb: {(): void}) {
|
|
|
|
this.afterRenderCallbacks = this.afterRenderCallbacks.filter(c => c !== cb)
|
|
|
|
},
|
|
|
|
onAfterResize(cb: {(): void}) {
|
|
|
|
this.three.onAfterResize(cb)
|
|
|
|
},
|
|
|
|
offAfterResize(cb: {(): void}) {
|
|
|
|
this.three.offAfterResize(cb)
|
|
|
|
},
|
|
|
|
render(time: number) {
|
2021-04-19 04:27:53 +08:00
|
|
|
const cbParams = { time, renderer: this }
|
|
|
|
this.beforeRenderCallbacks.forEach(cb => cb(cbParams))
|
2021-04-19 09:22:02 +08:00
|
|
|
// this.onFrame?.(cbParams)
|
2021-04-19 04:27:53 +08:00
|
|
|
this.renderFn()
|
|
|
|
this.afterRenderCallbacks.forEach(cb => cb(cbParams))
|
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',
|
|
|
|
})
|