mylingo3d
Version:
Lingo3D is a React/Vue 3d game development framework that ships with a complete visual editor
204 lines (176 loc) • 5.99 kB
text/typescript
import {
Color,
MeshStandardMaterial,
RepeatWrapping,
SpriteMaterial,
Texture,
Vector2,
VideoTexture
} from "three"
import loadTexture from "../../utils/loaders/loadTexture"
import ITexturedBasic from "../../../interface/ITexturedBasic"
import { Reactive } from "@lincode/reactivity"
import queueDebounce from "../../../utils/queueDebounce"
import { deg2Rad } from "@lincode/math"
const mapNames = ["map", "alphaMap"]
const queueTextureRepeat = queueDebounce()
export default abstract class TexturedBasicMixin implements ITexturedBasic {
protected abstract material: MeshStandardMaterial | SpriteMaterial
protected materialCloned?: boolean
protected tryCloneMaterial() {
if (this.materialCloned) return
this.materialCloned = true
//@ts-ignore
this.material = this.nativeObject3d.material = this.material.clone()
//@ts-ignore
this.then(() => this.material.dispose())
}
public get color() {
return "#" + this.material.color.getHexString()
}
public set color(val) {
this.tryCloneMaterial()
this.material.color = new Color(val)
}
public get fog() {
return this.material.fog
}
public set fog(val) {
this.tryCloneMaterial()
this.material.fog = val
}
private _opacity?: number
public get opacity() {
return (this._opacity ??= 1)
}
public set opacity(val) {
this.tryCloneMaterial()
this._opacity = val
this.material.opacity = val
this.material.transparent = val <= 1
//@ts-ignore
this.nativeObject3d.visible = !!val
}
protected applyTexture(mapNames: Array<string>) {
const repeat = this._textureRepeat
const flipY = this._textureFlipY
const rotation = this._textureRotation
queueTextureRepeat(this, () => {
this.tryCloneMaterial()
for (const name of mapNames) {
//@ts-ignore
const map: Texture = this.material[name]
if (!map) return
repeat !== undefined && (map.repeat = repeat)
flipY !== undefined && (map.flipY = flipY)
rotation !== undefined && (map.rotation = rotation * deg2Rad)
map.needsUpdate = true
}
})
}
private videoTextureState?: Reactive<string | HTMLVideoElement | undefined>
private textureState?: Reactive<string | undefined>
private initTexture() {
if (this.textureState) return
this.tryCloneMaterial()
const videoTextureState = (this.videoTextureState = new Reactive<
string | HTMLVideoElement | undefined
>(undefined))
const textureState = (this.textureState = new Reactive<
string | undefined
>(undefined))
//@ts-ignore
this.createEffect(() => {
const url = textureState.get()
const videoURL = videoTextureState.get()
if (videoURL) {
let video: HTMLVideoElement
if (typeof videoURL === "string") {
video = document.createElement("video")
video.crossOrigin = "anonymous"
video.src = videoURL
video.loop = true
video.autoplay = true
video.muted = true
video.playsInline = true
video.play()
} else video = videoURL
const videoTexture = new VideoTexture(
video,
undefined,
RepeatWrapping,
RepeatWrapping
)
const { material } = this
const { map } = material
material.map = videoTexture
material.needsUpdate = true
this.applyTexture(mapNames)
return () => {
video.pause()
videoTexture.dispose()
material.map = map
material.needsUpdate = true
}
}
if (!url) return
const { material } = this
const { map } = material
material.map = loadTexture(url)
this.applyTexture(mapNames)
return () => {
material.map = map
this.material.needsUpdate = true
}
}, [videoTextureState.get, textureState.get])
}
public get videoTexture() {
return this.videoTextureState?.get()
}
public set videoTexture(url) {
this.initTexture()
this.videoTextureState!.set(url)
}
public get texture() {
return this.textureState?.get()
}
public set texture(url) {
this.initTexture()
this.textureState!.set(url)
}
private _alphaMap?: string
public get alphaMap() {
return this._alphaMap
}
public set alphaMap(val) {
this.tryCloneMaterial()
this._alphaMap = val
this.material.alphaMap = val ? loadTexture(val) : null
this.applyTexture(mapNames)
}
protected _textureRepeat?: Vector2
public get textureRepeat() {
return this._textureRepeat
}
public set textureRepeat(val: Vector2 | number | undefined) {
typeof val === "number" && (val = new Vector2(val, val))
this._textureRepeat = val
this.applyTexture(mapNames)
}
protected _textureFlipY?: boolean
public get textureFlipY() {
return this._textureFlipY
}
public set textureFlipY(val) {
this._textureFlipY = val
this.applyTexture(mapNames)
}
protected _textureRotation?: number
public get textureRotation() {
return this._textureRotation
}
public set textureRotation(val) {
this._textureRotation = val
this.applyTexture(mapNames)
}
}