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 { Object3D } from 'three'
|
||||||
import { defineComponent, watch } from 'vue'
|
import { ComponentPublicInstance, defineComponent, watch } from 'vue'
|
||||||
import { bindProp } from '../tools'
|
import { bindProp } from '../tools'
|
||||||
|
|
||||||
|
interface Object3DInterface {
|
||||||
|
o3d?: Object3D
|
||||||
|
parent?: ComponentPublicInstance
|
||||||
|
}
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'Object3D',
|
name: 'Object3D',
|
||||||
inject: ['three', 'scene', 'renderer'],
|
inject: ['three', 'scene', 'renderer'],
|
||||||
@ -14,55 +19,57 @@ export default defineComponent({
|
|||||||
autoRemove: { type: Boolean, default: true },
|
autoRemove: { type: Boolean, default: true },
|
||||||
userData: { type: Object, default: () => ({}) },
|
userData: { type: Object, default: () => ({}) },
|
||||||
},
|
},
|
||||||
// can't use setup because it will not be used in sub components
|
setup(): Object3DInterface {
|
||||||
// setup() {},
|
return {}
|
||||||
|
},
|
||||||
unmounted() {
|
unmounted() {
|
||||||
if (this.autoRemove) this.removeFromParent()
|
if (this.autoRemove) this.removeFromParent()
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
initObject3D(o3d: Object3D) {
|
initObject3D(o3d: Object3D) {
|
||||||
this.o3d = o3d
|
this.o3d = o3d
|
||||||
this.o3d.userData = this.userData
|
|
||||||
this.$emit('created', this.o3d)
|
|
||||||
|
|
||||||
bindProp(this, 'position', this.o3d)
|
o3d.userData = this.userData
|
||||||
bindProp(this, 'rotation', this.o3d)
|
this.$emit('created', o3d)
|
||||||
bindProp(this, 'scale', this.o3d)
|
|
||||||
|
bindProp(this, 'position', o3d)
|
||||||
|
bindProp(this, 'rotation', o3d)
|
||||||
|
bindProp(this, 'scale', o3d)
|
||||||
|
|
||||||
// TODO : fix lookat.x
|
// TODO : fix lookat.x
|
||||||
if (this.lookAt) this.o3d.lookAt(this.lookAt.x, this.lookAt.y, this.lookAt.z)
|
if (this.lookAt) 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 })
|
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)
|
if (this.addToParent()) this.$emit('ready', this)
|
||||||
else console.error('Missing parent (Scene, Group...)')
|
else console.error('Missing parent (Scene, Group...)')
|
||||||
},
|
},
|
||||||
getParent() {
|
getParent(): undefined | ComponentPublicInstance {
|
||||||
let parent = this.$parent
|
let parent = this.$parent
|
||||||
while (parent) {
|
while (parent) {
|
||||||
if (parent.add) return parent
|
if ((parent as any).add) return parent
|
||||||
parent = parent.$parent
|
parent = parent.$parent
|
||||||
}
|
}
|
||||||
return false
|
return undefined
|
||||||
},
|
},
|
||||||
addToParent(o) {
|
addToParent(o?: Object3D) {
|
||||||
const o3d = o || this.o3d
|
const o3d = o || this.o3d
|
||||||
if (this._parent) {
|
if (this.parent) {
|
||||||
this._parent.add(o3d)
|
(this.parent as any).add(o3d)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
},
|
},
|
||||||
removeFromParent(o) {
|
removeFromParent(o?: Object3D) {
|
||||||
const o3d = o || this.o3d
|
const o3d = o || this.o3d
|
||||||
if (this._parent) {
|
if (this.parent) {
|
||||||
this._parent.remove(o3d)
|
(this.parent as any).remove(o3d)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
},
|
},
|
||||||
add(o: Object3D) { this.o3d.add(o) },
|
add(o: Object3D) { this.o3d?.add(o) },
|
||||||
remove(o: Object3D) { 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() : []
|
||||||
|
@ -18,21 +18,20 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
setup(props) {
|
setup(props) {
|
||||||
const camera = new OrthographicCamera(props.left, props.right, props.top, props.bottom, props.near, props.far)
|
const camera = new OrthographicCamera(props.left, props.right, props.top, props.bottom, props.near, props.far)
|
||||||
return {
|
|
||||||
camera,
|
bindProp(this, 'position', camera)
|
||||||
}
|
|
||||||
},
|
|
||||||
created() {
|
|
||||||
bindProp(this, 'position', this.camera)
|
|
||||||
|
|
||||||
const watchProps = ['left', 'right', 'top', 'bottom', 'near', 'far', 'zoom']
|
const watchProps = ['left', 'right', 'top', 'bottom', 'near', 'far', 'zoom']
|
||||||
watchProps.forEach(p => {
|
watchProps.forEach(p => {
|
||||||
watch(() => this[p], () => {
|
watch(() => props[p], (value) => {
|
||||||
this.camera[p] = this[p]
|
camera[p] = value
|
||||||
this.camera.updateProjectionMatrix()
|
camera.updateProjectionMatrix()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
return { camera }
|
||||||
|
},
|
||||||
|
created() {
|
||||||
this.three.camera = this.camera
|
this.three.camera = this.camera
|
||||||
},
|
},
|
||||||
__hmrId: 'OrthographicCamera',
|
__hmrId: 'OrthographicCamera',
|
||||||
|
@ -16,25 +16,25 @@ export default defineComponent({
|
|||||||
lookAt: { type: Object, default: null },
|
lookAt: { type: Object, default: null },
|
||||||
},
|
},
|
||||||
setup(props) {
|
setup(props) {
|
||||||
return {
|
const camera = new PerspectiveCamera(props.fov, props.aspect, props.near, props.far)
|
||||||
camera: new PerspectiveCamera(props.fov, props.aspect, props.near, props.far),
|
|
||||||
}
|
bindProp(props, 'position', camera)
|
||||||
},
|
|
||||||
created() {
|
|
||||||
bindProp(this, 'position', this.camera)
|
|
||||||
|
|
||||||
// TODO : fix lookAt x
|
// TODO : fix lookAt x
|
||||||
if (this.lookAt) this.camera.lookAt(this.lookAt.x, this.lookAt.y, this.lookAt.z)
|
if (props.lookAt) camera.lookAt(props.lookAt.x, props.lookAt.y, props.lookAt.z)
|
||||||
watch(() => this.lookAt, (v) => { this.camera.lookAt(v.x, v.y, v.z) }, { deep: true })
|
watch(() => props.lookAt, (v) => { camera.lookAt(v.x, v.y, v.z) }, { deep: true })
|
||||||
|
|
||||||
const watchProps = ['aspect', 'far', 'fov', 'near']
|
const watchProps = ['aspect', 'far', 'fov', 'near']
|
||||||
watchProps.forEach(p => {
|
watchProps.forEach(p => {
|
||||||
watch(() => this[p], () => {
|
watch(() => props[p], (value) => {
|
||||||
this.camera[p] = this[p]
|
camera[p] = value
|
||||||
this.camera.updateProjectionMatrix()
|
camera.updateProjectionMatrix()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
return { camera }
|
||||||
|
},
|
||||||
|
created() {
|
||||||
this.three.camera = this.camera
|
this.three.camera = this.camera
|
||||||
},
|
},
|
||||||
__hmrId: 'PerspectiveCamera',
|
__hmrId: 'PerspectiveCamera',
|
||||||
|
@ -1,7 +1,12 @@
|
|||||||
import { WebGLRenderer } from 'three'
|
import { defineComponent } from 'vue'
|
||||||
import { defineComponent, h } from 'vue'
|
|
||||||
import useThree, { ThreeConfigInterface } from './useThree'
|
import useThree, { ThreeConfigInterface } from './useThree'
|
||||||
|
|
||||||
|
interface RendererEventInterface {
|
||||||
|
}
|
||||||
|
|
||||||
|
// interface RendererEventListenerInterface {
|
||||||
|
// }
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'Renderer',
|
name: 'Renderer',
|
||||||
props: {
|
props: {
|
||||||
@ -15,20 +20,38 @@ export default defineComponent({
|
|||||||
width: String,
|
width: String,
|
||||||
height: String,
|
height: String,
|
||||||
xr: Boolean,
|
xr: Boolean,
|
||||||
|
onReady: Function,
|
||||||
|
onFrame: Function,
|
||||||
},
|
},
|
||||||
setup() {
|
setup(props) {
|
||||||
const renderer: null | WebGLRenderer = null
|
const canvas = document.createElement('canvas')
|
||||||
const _render: {(): void} = () => {}
|
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 onMountedCallbacks: {(): void}[] = []
|
||||||
const beforeRenderCallbacks: {(): void}[] = []
|
const beforeRenderCallbacks: {(): void}[] = []
|
||||||
const afterRenderCallbacks: {(): void}[] = []
|
const afterRenderCallbacks: {(): void}[] = []
|
||||||
|
|
||||||
return {
|
return {
|
||||||
three: useThree(),
|
canvas,
|
||||||
renderer,
|
three,
|
||||||
|
renderer: three.renderer,
|
||||||
|
renderFn,
|
||||||
raf: true,
|
raf: true,
|
||||||
_render,
|
|
||||||
onMountedCallbacks,
|
onMountedCallbacks,
|
||||||
beforeRenderCallbacks,
|
beforeRenderCallbacks,
|
||||||
afterRenderCallbacks,
|
afterRenderCallbacks,
|
||||||
@ -41,24 +64,14 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
const params: ThreeConfigInterface = {
|
// appendChild won't work on reload
|
||||||
canvas: this.$el,
|
this.$el.parentNode.insertBefore(this.canvas, this.$el)
|
||||||
antialias: this.antialias,
|
|
||||||
alpha: this.alpha,
|
|
||||||
autoClear: this.autoClear,
|
|
||||||
orbitCtrl: this.orbitCtrl,
|
|
||||||
pointer: this.pointer,
|
|
||||||
resize: this.resize,
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.width) params.width = parseInt(this.width)
|
if (this.three.init()) {
|
||||||
if (this.height) params.height = parseInt(this.height)
|
this.onReady?.(this)
|
||||||
|
|
||||||
if (this.three.init(params)) {
|
|
||||||
this.renderer = this.three.renderer
|
|
||||||
this.renderer.shadowMap.enabled = this.shadow
|
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) {
|
if (this.xr) {
|
||||||
this.renderer.xr.enabled = true
|
this.renderer.xr.enabled = true
|
||||||
@ -71,6 +84,7 @@ export default defineComponent({
|
|||||||
this.onMountedCallbacks.forEach(c => c())
|
this.onMountedCallbacks.forEach(c => c())
|
||||||
},
|
},
|
||||||
beforeUnmount() {
|
beforeUnmount() {
|
||||||
|
this.canvas.remove()
|
||||||
this.beforeRenderCallbacks = []
|
this.beforeRenderCallbacks = []
|
||||||
this.afterRenderCallbacks = []
|
this.afterRenderCallbacks = []
|
||||||
this.raf = false
|
this.raf = false
|
||||||
@ -99,9 +113,11 @@ export default defineComponent({
|
|||||||
this.three.offAfterResize(cb)
|
this.three.offAfterResize(cb)
|
||||||
},
|
},
|
||||||
render(time: number) {
|
render(time: number) {
|
||||||
this.beforeRenderCallbacks.forEach(c => c({ time }))
|
const cbParams = { time, renderer: this }
|
||||||
this._render()
|
this.beforeRenderCallbacks.forEach(cb => cb(cbParams))
|
||||||
this.afterRenderCallbacks.forEach(c => c({ time }))
|
this.onFrame?.(cbParams)
|
||||||
|
this.renderFn()
|
||||||
|
this.afterRenderCallbacks.forEach(cb => cb(cbParams))
|
||||||
},
|
},
|
||||||
renderLoop(time: number) {
|
renderLoop(time: number) {
|
||||||
if (this.raf) requestAnimationFrame(this.renderLoop)
|
if (this.raf) requestAnimationFrame(this.renderLoop)
|
||||||
@ -109,7 +125,8 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
render() {
|
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',
|
__hmrId: 'Renderer',
|
||||||
})
|
})
|
||||||
|
@ -21,7 +21,7 @@ export default defineComponent({
|
|||||||
scene: this.scene,
|
scene: this.scene,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
created() {
|
||||||
if (!this.three.scene) {
|
if (!this.three.scene) {
|
||||||
this.three.scene = this.scene
|
this.three.scene = this.scene
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||||
|
import { createRenderer } from '@vue/runtime-core'
|
||||||
import { Camera, OrthographicCamera, PerspectiveCamera, Scene, WebGLRenderer } from 'three'
|
import { Camera, OrthographicCamera, PerspectiveCamera, Scene, WebGLRenderer } from 'three'
|
||||||
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
|
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
|
||||||
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js'
|
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js'
|
||||||
@ -27,14 +28,14 @@ export interface SizeInterface {
|
|||||||
|
|
||||||
export interface ThreeInterface {
|
export interface ThreeInterface {
|
||||||
conf: ThreeConfigInterface
|
conf: ThreeConfigInterface
|
||||||
renderer?: WebGLRenderer
|
renderer: WebGLRenderer
|
||||||
|
composer?: EffectComposer
|
||||||
camera?: Camera
|
camera?: Camera
|
||||||
cameraCtrl?: OrbitControls
|
cameraCtrl?: OrbitControls
|
||||||
scene?: Scene
|
scene?: Scene
|
||||||
pointer?: PointerInterface
|
pointer?: PointerInterface
|
||||||
size: SizeInterface
|
size: SizeInterface
|
||||||
composer?: EffectComposer
|
init(): boolean
|
||||||
init(config: ThreeConfigInterface): boolean
|
|
||||||
dispose(): void
|
dispose(): void
|
||||||
render(): void
|
render(): void
|
||||||
renderC(): void
|
renderC(): void
|
||||||
@ -49,7 +50,7 @@ export interface ThreeInterface {
|
|||||||
/**
|
/**
|
||||||
* Three.js helper
|
* Three.js helper
|
||||||
*/
|
*/
|
||||||
export default function useThree(): ThreeInterface {
|
export default function useThree(params: ThreeConfigInterface): ThreeInterface {
|
||||||
// default conf
|
// default conf
|
||||||
const conf: ThreeConfigInterface = {
|
const conf: ThreeConfigInterface = {
|
||||||
antialias: true,
|
antialias: true,
|
||||||
@ -62,6 +63,12 @@ export default function useThree(): ThreeInterface {
|
|||||||
height: 150,
|
height: 150,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (params) {
|
||||||
|
Object.entries(params).forEach(([key, value]) => {
|
||||||
|
conf[key] = value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// size
|
// size
|
||||||
const size: SizeInterface = {
|
const size: SizeInterface = {
|
||||||
width: 1, height: 1,
|
width: 1, height: 1,
|
||||||
@ -70,9 +77,6 @@ export default function useThree(): ThreeInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// handlers
|
// handlers
|
||||||
// const afterInitCallbacks: void[] = []
|
|
||||||
// let afterResizeCallbacks: void[] = []
|
|
||||||
// let beforeRenderCallbacks: void[] = []
|
|
||||||
const afterInitCallbacks: {(): void}[] = []
|
const afterInitCallbacks: {(): void}[] = []
|
||||||
let afterResizeCallbacks: {(): void}[] = []
|
let afterResizeCallbacks: {(): void}[] = []
|
||||||
let beforeRenderCallbacks: {(): void}[] = []
|
let beforeRenderCallbacks: {(): void}[] = []
|
||||||
@ -82,6 +86,7 @@ export default function useThree(): ThreeInterface {
|
|||||||
// returned object
|
// returned object
|
||||||
const obj: ThreeInterface = {
|
const obj: ThreeInterface = {
|
||||||
conf,
|
conf,
|
||||||
|
renderer: createRenderer(),
|
||||||
size,
|
size,
|
||||||
init,
|
init,
|
||||||
dispose,
|
dispose,
|
||||||
@ -96,15 +101,18 @@ export default function useThree(): ThreeInterface {
|
|||||||
return obj
|
return obj
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* init three
|
* create WebGLRenderer
|
||||||
*/
|
*/
|
||||||
function init(params: ThreeConfigInterface) {
|
function createRenderer(): WebGLRenderer {
|
||||||
if (params) {
|
const renderer = new WebGLRenderer({ canvas: conf.canvas, antialias: conf.antialias, alpha: conf.alpha })
|
||||||
Object.entries(params).forEach(([key, value]) => {
|
renderer.autoClear = conf.autoClear
|
||||||
conf[key] = value
|
return renderer
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* init three
|
||||||
|
*/
|
||||||
|
function init() {
|
||||||
if (!obj.scene) {
|
if (!obj.scene) {
|
||||||
console.error('Missing Scene')
|
console.error('Missing Scene')
|
||||||
return false
|
return false
|
||||||
@ -115,14 +123,11 @@ export default function useThree(): ThreeInterface {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
obj.renderer = new WebGLRenderer({ canvas: conf.canvas, antialias: conf.antialias, alpha: conf.alpha })
|
|
||||||
obj.renderer.autoClear = conf.autoClear
|
|
||||||
|
|
||||||
if (conf.resize) {
|
if (conf.resize) {
|
||||||
onResize()
|
onResize()
|
||||||
window.addEventListener('resize', onResize)
|
window.addEventListener('resize', onResize)
|
||||||
} else {
|
} else if (conf.width && conf.height) {
|
||||||
setSize(conf.width!, conf.height!)
|
setSize(conf.width, conf.height)
|
||||||
}
|
}
|
||||||
|
|
||||||
initPointer()
|
initPointer()
|
||||||
@ -142,6 +147,9 @@ export default function useThree(): ThreeInterface {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* init pointer
|
||||||
|
*/
|
||||||
function initPointer() {
|
function initPointer() {
|
||||||
let pointerConf: PointerConfigInterface = {
|
let pointerConf: PointerConfigInterface = {
|
||||||
camera: obj.camera!,
|
camera: obj.camera!,
|
||||||
|
Loading…
Reference in New Issue
Block a user