diff --git a/src/meshes/Image.js b/src/meshes/Image.js deleted file mode 100644 index 34dd502..0000000 --- a/src/meshes/Image.js +++ /dev/null @@ -1,69 +0,0 @@ -import { defineComponent, watch } from 'vue'; -import { DoubleSide, MeshBasicMaterial, PlaneGeometry, TextureLoader } from 'three'; -import Mesh from './Mesh.js'; - -export default defineComponent({ - emits: ['loaded'], - extends: Mesh, - props: { - src: String, - width: Number, - height: Number, - keepSize: Boolean, - }, - created() { - this.createGeometry(); - this.createMaterial(); - this.initMesh(); - - watch(() => this.src, this.refreshTexture); - - ['width', 'height'].forEach(p => { - watch(() => this[p], this.resize); - }); - - if (this.keepSize) this.three.onAfterResize(this.resize); - }, - methods: { - createGeometry() { - this.geometry = new PlaneGeometry(1, 1, 1, 1); - }, - createMaterial() { - this.material = new MeshBasicMaterial({ side: DoubleSide, map: this.loadTexture() }); - }, - loadTexture() { - return new TextureLoader().load(this.src, this.onLoaded); - }, - refreshTexture() { - if (this.texture) this.texture.dispose(); - this.material.map = this.loadTexture(); - this.material.needsUpdate = true; - }, - onLoaded(texture) { - this.texture = texture; - this.resize(); - this.$emit('loaded'); - }, - resize() { - if (!this.texture) return; - const screen = this.three.size; - const iW = this.texture.image.width; - const iH = this.texture.image.height; - const iRatio = iW / iH; - let w, h; - if (this.width && this.height) { - w = this.width * screen.wWidth / screen.width; - h = this.height * screen.wHeight / screen.height; - } else if (this.width) { - w = this.width * screen.wWidth / screen.width; - h = w / iRatio; - } else if (this.height) { - h = this.height * screen.wHeight / screen.height; - w = h * iRatio; - } - this.mesh.scale.x = w; - this.mesh.scale.y = h; - }, - }, - __hmrId: 'Image', -}); diff --git a/src/meshes/Image.ts b/src/meshes/Image.ts new file mode 100644 index 0000000..3db5434 --- /dev/null +++ b/src/meshes/Image.ts @@ -0,0 +1,80 @@ +import { defineComponent, watch } from 'vue' +import { DoubleSide, MeshBasicMaterial, PlaneGeometry, Texture, TextureLoader } from 'three' +import Mesh, { MeshSetupInterface } from './Mesh' +import { object3DSetup } from '../core/Object3D' + +interface ImageInterface extends MeshSetupInterface { + material?: MeshBasicMaterial + texture?: Texture +} + +export default defineComponent({ + emits: ['loaded'], + extends: Mesh, + props: { + src: { type: String, required: true }, + width: Number, + height: Number, + keepSize: Boolean, + }, + setup(): ImageInterface { + return object3DSetup() + }, + created() { + this.geometry = new PlaneGeometry(1, 1, 1, 1) + this.material = new MeshBasicMaterial({ side: DoubleSide, map: this.loadTexture() }) + + watch(() => this.src, this.refreshTexture); + + ['width', 'height'].forEach(p => { + // @ts-ignore + watch(() => this[p], this.resize) + }) + + this.resize() + if (this.keepSize) this.renderer.onResize(this.resize) + }, + methods: { + loadTexture() { + return new TextureLoader().load(this.src, this.onLoaded) + }, + refreshTexture() { + this.texture?.dispose() + if (this.material) { + this.material.map = this.loadTexture() + this.material.needsUpdate = true + } + }, + onLoaded(texture: Texture) { + this.texture = texture + this.resize() + this.$emit('loaded', texture) + }, + resize() { + if (!this.texture) return + const screen = this.renderer.size + const iW = this.texture.image.width + const iH = this.texture.image.height + const iRatio = iW / iH + let w = 1, h = 1 + if (this.width && this.height) { + w = this.width * screen.wWidth / screen.width + h = this.height * screen.wHeight / screen.height + } else if (this.width) { + w = this.width * screen.wWidth / screen.width + h = w / iRatio + } else if (this.height) { + h = this.height * screen.wHeight / screen.height + w = h * iRatio + } else { + if (iRatio > 1) w = h * iRatio + else h = w / iRatio + } + if (this.mesh) { + this.mesh.scale.x = w + this.mesh.scale.y = h + } + }, + }, + __hmrId: 'Image', +}) diff --git a/src/meshes/Mesh.ts b/src/meshes/Mesh.ts index 204b343..577ae63 100644 --- a/src/meshes/Mesh.ts +++ b/src/meshes/Mesh.ts @@ -1,8 +1,7 @@ -import { ComponentPropsOptions, defineComponent, inject, watch } from 'vue' +import { ComponentPropsOptions, defineComponent, watch } from 'vue' import { BufferGeometry, Material, Mesh as TMesh } from 'three' -import Object3D from '../core/Object3D' +import Object3D, { object3DSetup, Object3DSetupInterface } from '../core/Object3D' import { bindProp } from '../tools' -import { ThreeInterface } from '../core/useThree' export const pointerProps = { onPointerEnter: Function, @@ -14,23 +13,18 @@ export const pointerProps = { onClick: Function, } -interface MeshSetupInterface { - three?: ThreeInterface +export interface MeshSetupInterface extends Object3DSetupInterface { mesh?: TMesh geometry?: BufferGeometry material?: Material loading?: boolean } -export interface MeshInterface { +export interface MeshInterface extends MeshSetupInterface { + setGeometry(g: BufferGeometry): void setMaterial(m: Material): void } -export function defaultSetup(): MeshSetupInterface { - const three = inject('three') as ThreeInterface - return { three } -} - const Mesh = defineComponent({ name: 'Mesh', extends: Object3D, @@ -39,10 +33,8 @@ const Mesh = defineComponent({ receiveShadow: Boolean, ...pointerProps, }, - setup() { - return defaultSetup() - }, - created() { + setup(): MeshSetupInterface { + return object3DSetup() }, provide() { return { @@ -114,8 +106,8 @@ export function meshComponent(name, props, createGeometry) { name, extends: Mesh, props, - setup() { - return defaultSetup() + setup(): MeshSetupInterface { + return object3DSetup() }, created() { this.createGeometry() diff --git a/src/meshes/Sprite.js b/src/meshes/Sprite.js deleted file mode 100644 index 7a2f3f6..0000000 --- a/src/meshes/Sprite.js +++ /dev/null @@ -1,54 +0,0 @@ -import { defineComponent } from 'vue'; -import { Sprite, SpriteMaterial, TextureLoader } from 'three'; -import Object3D from '../core/Object3D'; - -export default defineComponent({ - extends: Object3D, - emits: ['loaded'], - props: { - src: String, - }, - data() { - return { - loading: true, - }; - }, - created() { - this.texture = new TextureLoader().load(this.src, this.onLoaded); - this.material = new SpriteMaterial({ map: this.texture }); - this.sprite = new Sprite(this.material); - this.geometry = this.sprite.geometry; - this.initObject3D(this.sprite); - }, - unmounted() { - this.texture.dispose(); - this.material.dispose(); - }, - methods: { - onLoaded() { - this.loading = false; - this.updateUV(); - this.$emit('loaded'); - }, - updateUV() { - this.iWidth = this.texture.image.width; - this.iHeight = this.texture.image.height; - this.iRatio = this.iWidth / this.iHeight; - - let x = 0.5, y = 0.5; - if (this.iRatio > 1) { - y = 0.5 / this.iRatio; - } else { - x = 0.5 / this.iRatio; - } - - const positions = this.geometry.attributes.position.array; - positions[0] = -x; positions[1] = -y; - positions[5] = x; positions[6] = -y; - positions[10] = x; positions[11] = y; - positions[15] = -x; positions[16] = y; - this.geometry.attributes.position.needsUpdate = true; - }, - }, - __hmrId: 'Sprite', -}); diff --git a/src/meshes/Sprite.ts b/src/meshes/Sprite.ts new file mode 100644 index 0000000..05bb088 --- /dev/null +++ b/src/meshes/Sprite.ts @@ -0,0 +1,58 @@ +import { defineComponent } from 'vue' +import { Sprite, SpriteMaterial, Texture, TextureLoader } from 'three' +import Object3D, { object3DSetup, Object3DSetupInterface } from '../core/Object3D' + +interface SpriteSetupInterface extends Object3DSetupInterface { + texture?: Texture + material?: SpriteMaterial + sprite?: Sprite +} + +export default defineComponent({ + extends: Object3D, + emits: ['loaded'], + props: { + src: { type: String, required: true }, + }, + setup(): SpriteSetupInterface { + return object3DSetup() + }, + created() { + this.texture = new TextureLoader().load(this.src, this.onLoaded) + this.material = new SpriteMaterial({ map: this.texture }) + this.sprite = new Sprite(this.material) + this.initObject3D(this.sprite) + }, + unmounted() { + this.texture?.dispose() + this.material?.dispose() + }, + methods: { + onLoaded() { + this.updateUV() + this.$emit('loaded') + }, + updateUV() { + if (!this.texture || !this.sprite) return + + const iWidth = this.texture.image.width + const iHeight = this.texture.image.height + const iRatio = iWidth / iHeight + + let x = 0.5, y = 0.5 + if (iRatio > 1) { + x = 0.5 * iRatio + } else { + y = 0.5 / iRatio + } + + const positions = this.sprite.geometry.attributes.position.array as Array + positions[0] = -x; positions[1] = -y + positions[5] = x; positions[6] = -y + positions[10] = x; positions[11] = y + positions[15] = -x; positions[16] = y + this.sprite.geometry.attributes.position.needsUpdate = true + }, + }, + __hmrId: 'Sprite', +})