1
0
mirror of https://github.com/troisjs/trois.git synced 2024-11-24 04:12:02 +08:00

wip: core

This commit is contained in:
Kevin Levron 2021-04-18 22:27:53 +02:00
parent 5df3223bd6
commit b5ab6567e3
7 changed files with 120 additions and 89 deletions

View File

@ -1,7 +1,12 @@
import { Object3D } from 'three'
import { defineComponent, watch } from 'vue'
import { ComponentPublicInstance, defineComponent, watch } from 'vue'
import { bindProp } from '../tools'
interface Object3DInterface {
o3d?: Object3D
parent?: ComponentPublicInstance
}
export default defineComponent({
name: 'Object3D',
inject: ['three', 'scene', 'renderer'],
@ -14,55 +19,57 @@ export default defineComponent({
autoRemove: { type: Boolean, default: true },
userData: { type: Object, default: () => ({}) },
},
// can't use setup because it will not be used in sub components
// setup() {},
setup(): Object3DInterface {
return {}
},
unmounted() {
if (this.autoRemove) this.removeFromParent()
},
methods: {
initObject3D(o3d: Object3D) {
this.o3d = o3d
this.o3d.userData = this.userData
this.$emit('created', this.o3d)
bindProp(this, 'position', this.o3d)
bindProp(this, 'rotation', this.o3d)
bindProp(this, 'scale', this.o3d)
o3d.userData = this.userData
this.$emit('created', o3d)
bindProp(this, 'position', o3d)
bindProp(this, 'rotation', o3d)
bindProp(this, 'scale', o3d)
// TODO : fix lookat.x
if (this.lookAt) this.o3d.lookAt(this.lookAt.x, this.lookAt.y, this.lookAt.z)
watch(() => this.lookAt, (v) => { this.o3d.lookAt(v.x, v.y, v.z) }, { deep: true })
if (this.lookAt) o3d.lookAt(this.lookAt.x, this.lookAt.y, this.lookAt.z)
watch(() => this.lookAt, (v) => { o3d.lookAt(v.x, v.y, v.z) }, { deep: true })
this._parent = this.getParent()
this.parent = this.getParent()
if (this.addToParent()) this.$emit('ready', this)
else console.error('Missing parent (Scene, Group...)')
},
getParent() {
getParent(): undefined | ComponentPublicInstance {
let parent = this.$parent
while (parent) {
if (parent.add) return parent
if ((parent as any).add) return parent
parent = parent.$parent
}
return false
return undefined
},
addToParent(o) {
addToParent(o?: Object3D) {
const o3d = o || this.o3d
if (this._parent) {
this._parent.add(o3d)
if (this.parent) {
(this.parent as any).add(o3d)
return true
}
return false
},
removeFromParent(o) {
removeFromParent(o?: Object3D) {
const o3d = o || this.o3d
if (this._parent) {
this._parent.remove(o3d)
if (this.parent) {
(this.parent as any).remove(o3d)
return true
}
return false
},
add(o: Object3D) { this.o3d.add(o) },
remove(o: Object3D) { 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() : []

View File

@ -18,21 +18,20 @@ export default defineComponent({
},
setup(props) {
const camera = new OrthographicCamera(props.left, props.right, props.top, props.bottom, props.near, props.far)
return {
camera,
}
},
created() {
bindProp(this, 'position', this.camera)
bindProp(this, 'position', camera)
const watchProps = ['left', 'right', 'top', 'bottom', 'near', 'far', 'zoom']
watchProps.forEach(p => {
watch(() => this[p], () => {
this.camera[p] = this[p]
this.camera.updateProjectionMatrix()
watch(() => props[p], (value) => {
camera[p] = value
camera.updateProjectionMatrix()
})
})
return { camera }
},
created() {
this.three.camera = this.camera
},
__hmrId: 'OrthographicCamera',

View File

@ -16,25 +16,25 @@ export default defineComponent({
lookAt: { type: Object, default: null },
},
setup(props) {
return {
camera: new PerspectiveCamera(props.fov, props.aspect, props.near, props.far),
}
},
created() {
bindProp(this, 'position', this.camera)
const camera = new PerspectiveCamera(props.fov, props.aspect, props.near, props.far)
bindProp(props, 'position', camera)
// 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 })
if (props.lookAt) camera.lookAt(props.lookAt.x, props.lookAt.y, props.lookAt.z)
watch(() => props.lookAt, (v) => { camera.lookAt(v.x, v.y, v.z) }, { deep: true })
const watchProps = ['aspect', 'far', 'fov', 'near']
watchProps.forEach(p => {
watch(() => this[p], () => {
this.camera[p] = this[p]
this.camera.updateProjectionMatrix()
watch(() => props[p], (value) => {
camera[p] = value
camera.updateProjectionMatrix()
})
})
return { camera }
},
created() {
this.three.camera = this.camera
},
__hmrId: 'PerspectiveCamera',

View File

@ -1,7 +1,12 @@
import { WebGLRenderer } from 'three'
import { defineComponent, h } from 'vue'
import { defineComponent } from 'vue'
import useThree, { ThreeConfigInterface } from './useThree'
interface RendererEventInterface {
}
// interface RendererEventListenerInterface {
// }
export default defineComponent({
name: 'Renderer',
props: {
@ -15,20 +20,38 @@ export default defineComponent({
width: String,
height: String,
xr: Boolean,
onReady: Function,
onFrame: Function,
},
setup() {
const renderer: null | WebGLRenderer = null
const _render: {(): void} = () => {}
setup(props) {
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} = () => {}
const onMountedCallbacks: {(): void}[] = []
const beforeRenderCallbacks: {(): void}[] = []
const afterRenderCallbacks: {(): void}[] = []
return {
three: useThree(),
renderer,
canvas,
three,
renderer: three.renderer,
renderFn,
raf: true,
_render,
onMountedCallbacks,
beforeRenderCallbacks,
afterRenderCallbacks,
@ -41,24 +64,14 @@ export default defineComponent({
}
},
mounted() {
const params: ThreeConfigInterface = {
canvas: this.$el,
antialias: this.antialias,
alpha: this.alpha,
autoClear: this.autoClear,
orbitCtrl: this.orbitCtrl,
pointer: this.pointer,
resize: this.resize,
}
// appendChild won't work on reload
this.$el.parentNode.insertBefore(this.canvas, this.$el)
if (this.width) params.width = parseInt(this.width)
if (this.height) params.height = parseInt(this.height)
if (this.three.init(params)) {
this.renderer = this.three.renderer
if (this.three.init()) {
this.onReady?.(this)
this.renderer.shadowMap.enabled = this.shadow
this._render = this.three.composer ? this.three.renderC : this.three.render
this.renderFn = this.three.composer ? this.three.renderC : this.three.render
if (this.xr) {
this.renderer.xr.enabled = true
@ -71,6 +84,7 @@ export default defineComponent({
this.onMountedCallbacks.forEach(c => c())
},
beforeUnmount() {
this.canvas.remove()
this.beforeRenderCallbacks = []
this.afterRenderCallbacks = []
this.raf = false
@ -99,9 +113,11 @@ export default defineComponent({
this.three.offAfterResize(cb)
},
render(time: number) {
this.beforeRenderCallbacks.forEach(c => c({ time }))
this._render()
this.afterRenderCallbacks.forEach(c => c({ time }))
const cbParams = { time, renderer: this }
this.beforeRenderCallbacks.forEach(cb => cb(cbParams))
this.onFrame?.(cbParams)
this.renderFn()
this.afterRenderCallbacks.forEach(cb => cb(cbParams))
},
renderLoop(time: number) {
if (this.raf) requestAnimationFrame(this.renderLoop)
@ -109,7 +125,8 @@ export default defineComponent({
},
},
render() {
return h('canvas', {}, this.$slots.default ? this.$slots.default() : [])
// return h('canvas', {}, this.$slots.default ? this.$slots.default() : [])
return this.$slots.default ? this.$slots.default() : []
},
__hmrId: 'Renderer',
})

View File

@ -21,7 +21,7 @@ export default defineComponent({
scene: this.scene,
}
},
mounted() {
created() {
if (!this.three.scene) {
this.three.scene = this.scene
}

View File

@ -1,4 +1,5 @@
/* eslint-disable @typescript-eslint/no-non-null-assertion */
import { createRenderer } from '@vue/runtime-core'
import { Camera, OrthographicCamera, PerspectiveCamera, Scene, WebGLRenderer } from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js'
@ -27,14 +28,14 @@ export interface SizeInterface {
export interface ThreeInterface {
conf: ThreeConfigInterface
renderer?: WebGLRenderer
renderer: WebGLRenderer
composer?: EffectComposer
camera?: Camera
cameraCtrl?: OrbitControls
scene?: Scene
pointer?: PointerInterface
size: SizeInterface
composer?: EffectComposer
init(config: ThreeConfigInterface): boolean
init(): boolean
dispose(): void
render(): void
renderC(): void
@ -49,7 +50,7 @@ export interface ThreeInterface {
/**
* Three.js helper
*/
export default function useThree(): ThreeInterface {
export default function useThree(params: ThreeConfigInterface): ThreeInterface {
// default conf
const conf: ThreeConfigInterface = {
antialias: true,
@ -62,6 +63,12 @@ export default function useThree(): ThreeInterface {
height: 150,
}
if (params) {
Object.entries(params).forEach(([key, value]) => {
conf[key] = value
})
}
// size
const size: SizeInterface = {
width: 1, height: 1,
@ -70,9 +77,6 @@ export default function useThree(): ThreeInterface {
}
// handlers
// const afterInitCallbacks: void[] = []
// let afterResizeCallbacks: void[] = []
// let beforeRenderCallbacks: void[] = []
const afterInitCallbacks: {(): void}[] = []
let afterResizeCallbacks: {(): void}[] = []
let beforeRenderCallbacks: {(): void}[] = []
@ -82,6 +86,7 @@ export default function useThree(): ThreeInterface {
// returned object
const obj: ThreeInterface = {
conf,
renderer: createRenderer(),
size,
init,
dispose,
@ -96,15 +101,18 @@ export default function useThree(): ThreeInterface {
return obj
/**
* init three
* create WebGLRenderer
*/
function init(params: ThreeConfigInterface) {
if (params) {
Object.entries(params).forEach(([key, value]) => {
conf[key] = value
})
function createRenderer(): WebGLRenderer {
const renderer = new WebGLRenderer({ canvas: conf.canvas, antialias: conf.antialias, alpha: conf.alpha })
renderer.autoClear = conf.autoClear
return renderer
}
/**
* init three
*/
function init() {
if (!obj.scene) {
console.error('Missing Scene')
return false
@ -115,14 +123,11 @@ export default function useThree(): ThreeInterface {
return false
}
obj.renderer = new WebGLRenderer({ canvas: conf.canvas, antialias: conf.antialias, alpha: conf.alpha })
obj.renderer.autoClear = conf.autoClear
if (conf.resize) {
onResize()
window.addEventListener('resize', onResize)
} else {
setSize(conf.width!, conf.height!)
} else if (conf.width && conf.height) {
setSize(conf.width, conf.height)
}
initPointer()
@ -142,6 +147,9 @@ export default function useThree(): ThreeInterface {
return true
}
/**
* init pointer
*/
function initPointer() {
let pointerConf: PointerConfigInterface = {
camera: obj.camera!,