Skip to content

Commit ea1074c

Browse files
committed
added some webGL utils for astro
1 parent 93a75cb commit ea1074c

File tree

4 files changed

+143
-0
lines changed

4 files changed

+143
-0
lines changed

src/scripts/webgl/utils/Mouse3D.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import * as THREE from 'three'
2+
import { gl } from '../core/WebGL'
3+
4+
class Mouse3D {
5+
private ray = new THREE.Ray()
6+
private mouse = new THREE.Vector2(0, 0)
7+
8+
constructor() {
9+
window.addEventListener('mousemove', this.handleMouseMove)
10+
window.addEventListener('mousemove', this.handleTouchMove)
11+
}
12+
13+
get position() {
14+
gl.camera.updateMatrixWorld()
15+
this.ray.origin.setFromMatrixPosition(gl.camera.matrixWorld)
16+
this.ray.direction.set(this.mouse.x, this.mouse.y, 0.5).unproject(gl.camera).sub(this.ray.origin).normalize()
17+
const distance = this.ray.origin.length() / Math.cos(Math.PI - this.ray.direction.angleTo(this.ray.origin))
18+
this.ray.origin.add(this.ray.direction.multiplyScalar(distance * 1.0))
19+
return this.ray.origin
20+
}
21+
22+
private handleMouseMove = (e: TouchEvent) => {
23+
this.mouse.x = ((e.pageX - window.scrollX) / window.innerWidth) * 2 - 1
24+
this.mouse.y = -((e.pageY - window.scrollY) / window.innerHeight) * 2 + 1
25+
}
26+
27+
private handleTouchMove = (e: TouchEvent) => {
28+
const { pageX, pageY } = e.touches[0]
29+
this.mouse.x = ((pageX - window.scrollX) / window.innerWidth) * 2 - 1
30+
this.mouse.y = -((pageY - window.scrollY) / window.innerHeight) * 2 + 1
31+
}
32+
33+
dispose = () => {
34+
document.removeEventListener('mousemove', this.handleMouseMove)
35+
document.removeEventListener('mousemove', this.handleTouchMove)
36+
}
37+
}
38+
39+
export const mouse3d = new Mouse3D()
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { OrbitControls as OC } from 'three/examples/jsm/controls/OrbitControls'
2+
import { gl } from '../core/WebGL'
3+
4+
class OrbitControls {
5+
private orbitControls: OC
6+
7+
constructor() {
8+
this.orbitControls = new OC(gl.camera, gl.renderer.domElement)
9+
this.orbitControls.enableDamping = true
10+
this.orbitControls.dampingFactor = 0.1
11+
}
12+
13+
get primitive() {
14+
return this.orbitControls
15+
}
16+
17+
disableDamping() {
18+
this.orbitControls.enableDamping = false
19+
}
20+
21+
update() {
22+
this.orbitControls.update()
23+
}
24+
}
25+
26+
export const controls = new OrbitControls()
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import * as THREE from 'three'
2+
import { GLTF, GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'
3+
import { RBGELoader } from 'three/examples/jsm/loaders/RBGELoader'
4+
import { resolvePath } from '../../utils'
5+
6+
export type Assets = {
7+
[key in string]: {
8+
data?: THREE.Texture | THREE.VideoTexture | GLTF
9+
path: string
10+
encoding?: boolean
11+
flipY?: boolean
12+
}
13+
}
14+
15+
export async function loadAssets(assets: Assets) {
16+
const textureLoader = new THREE.TextureLoader()
17+
const gltfLoader = new GLTFLoader()
18+
const rgbeLoader = new RBGELoader()
19+
20+
const getExtension = (path: string) => {
21+
const s = path.split('.')
22+
return s[s.length - 1]
23+
}
24+
25+
await Promise.all(
26+
Object.values(assets).map(async (v) => {
27+
const path = resolvePath(v.path)
28+
const extension = getExtension(path)
29+
30+
if (['jpg', 'png', 'webp'].includes(extension)) {
31+
const texture = await textureLoader.loadAsync(path)
32+
texture.userData.aspect = texture.image.width / texture.image.height
33+
v.encoding && (texture.colorSpace = THREE.SRGBColorSpace)
34+
v.flipY !== undefined && (texture.flipY = v.flipY)
35+
v.data = texture
36+
} else if (['glb'].includes(extension)) {
37+
const gltf = await gltfLoader.loadAsync(path)
38+
v.data = gltf
39+
} else if (['web', 'mp4'].includes(extension)) {
40+
const video = document.createElement('video')
41+
video.src = path
42+
video.muted = true
43+
video.loop = true
44+
video.autoplay = true
45+
video.preload = 'metadata'
46+
video.playsInline = true
47+
// await video.play()
48+
const texture = new THREE.VideoTexture(video)
49+
texture.userData.aspect = video.videoWidth / video.videoHeight
50+
v.encoding && (texture.colorSpace = THREE.SRGBColorSpace)
51+
v.data = texture
52+
} else if (['hdr'].includes(extension)) {
53+
const texture = await regbeLoader.loadAsync(path)
54+
texture.mapping = THREE.EquirectangularReflectionMapping
55+
v.data = texture
56+
}
57+
}),
58+
)
59+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
export function calcCoveredTextureScale(texture: THREE.Texture, aspect: number, target?: THREE.Vector2) {
2+
const imageAspect = texture.image.width / texture.image.height
3+
4+
let result: [number, number] = [1, 1]
5+
if (aspect < imageAspect) result = [aspect / imageAspect, 1]
6+
else result = [1, imageAspect / aspect]
7+
8+
target?.set(result[0], result[1])
9+
10+
return result
11+
}
12+
13+
export function coveredTexture(texture: THREE.Texture, screenAspect: number) {
14+
texture.matrixAutoUpdate = false
15+
const scale = calcCoveredTextureScale(texture, screenAspect)
16+
texture.matrix.setUvTransform(0, 0, scale[0], scale[1], 0, 0.5, 0.5)
17+
18+
return texture
19+
}

0 commit comments

Comments
 (0)