mirror of
https://github.com/troisjs/trois.git
synced 2024-11-24 04:12:02 +08:00
wip: core
This commit is contained in:
parent
5df3223bd6
commit
b5ab6567e3
@ -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() : []
|
||||
|
@ -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',
|
||||
|
@ -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',
|
||||
|
@ -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',
|
||||
})
|
||||
|
@ -21,7 +21,7 @@ export default defineComponent({
|
||||
scene: this.scene,
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
created() {
|
||||
if (!this.three.scene) {
|
||||
this.three.scene = this.scene
|
||||
}
|
||||
|
@ -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!,
|
||||
|
Loading…
Reference in New Issue
Block a user