Three.js+TypeScript+Webpack learning record (3)

Use environment reference

Node.js v16.19.1

text

stand-alone function file

We can't keep writing code in index.ts, separate files:

// init.ts
import * as THREE from 'three'

export const initScene = () => {
    
    
    const scene = new THREE.Scene()
    scene.background = new THREE.Color('white')
    const light = new THREE.AmbientLight('white', 1.3)
    scene.add(light)
    return scene
}

export const initCamera = () => {
    
    
    const camera = new THREE.PerspectiveCamera(55, window.innerWidth / window.innerHeight, 0.1, 1000)
    camera.position.set(0, 3, 5)
    camera.lookAt(new THREE.Vector3(0, 0, 0))
    return camera
}

export const initWebGLRenderer = () => {
    
    
    const renderer = new THREE.WebGLRenderer({
    
     antialias: true })
    renderer.setSize(window.innerWidth, window.innerHeight)
    document.body.appendChild(renderer.domElement)
    return renderer
}

// load.ts
import * as THREE from 'three'
import {
    
     GLTF, GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'

export const loadGLTF = (url: string) => new Promise<THREE.Group>((resolve, reject) => {
    
    
    const loader = new GLTFLoader()
    loader.load(url, (gltf: GLTF) => {
    
    
        console.log(gltf)
        resolve(gltf.scene)
    })
})

// index.ts
import * as THREE from 'three'
import {
    
     OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
import {
    
     initCamera, initScene, initWebGLRenderer } from './init'
import {
    
     loadGLTF } from './load'

class Game {
    
    
    scene: THREE.Scene
    camera: THREE.PerspectiveCamera
    renderer: THREE.WebGLRenderer
    orbitControls: OrbitControls

    constructor() {
    
    
        this.scene = initScene()
        this.camera = initCamera()
        this.scene.add(this.camera)
        this.renderer = initWebGLRenderer()
        this.orbitControls = this.addOrbitControls(this.camera, this.renderer)
        this.addModel()
        this.addResizeEventListener()
    }

    addOrbitControls(camera: THREE.Camera, renderer: THREE.WebGLRenderer) {
    
    
        const controls = new OrbitControls(camera, renderer.domElement)
        controls.autoRotate = true
        controls.enableDamping = true
        controls.update()
        return controls
    }

    async addModel() {
    
    
        const model = await loadGLTF('gltf/SheenChair.glb')
        this.scene.add(model)
    }

    addResizeEventListener() {
    
    
        window.addEventListener('resize', () => {
    
    
            this.camera.aspect = window.innerWidth / window.innerHeight
            this.camera.updateProjectionMatrix()
            this.renderer.setSize(window.innerWidth, window.innerHeight)
        })
    }

    startMainLoop() {
    
    
        // 等待一帧用于初始化
        Promise.resolve().then(() => {
    
    
            this.step()
        })
    }

    step() {
    
    
        requestAnimationFrame(this.step.bind(this))
        this.orbitControls && this.orbitControls.update()
        this.renderer.render(this.scene, this.camera)
    }
}

const game = new Game()
game.startMainLoop()

X-ray inspection

Mouse click on an object is the most common requirement. Add a click event to the dom, and then calculate the coordinate ratio relative to the canvas, and calculate it into the three coordinate system (-1 ~ 1).

import * as THREE from 'three'
import {
    
     OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
import {
    
     initCamera, initScene, initWebGLRenderer } from './init'
import {
    
     loadGLTF } from './load'

class Game {
    
    
    scene: THREE.Scene
    camera: THREE.PerspectiveCamera
    renderer: THREE.WebGLRenderer
    orbitControls: OrbitControls

    raycaster = new THREE.Raycaster()
    mouse = new THREE.Vector2()

    constructor() {
    
    
        this.scene = initScene()
        this.camera = initCamera()
        this.scene.add(this.camera)
        this.renderer = initWebGLRenderer()
        this.orbitControls = this.addOrbitControls(this.camera, this.renderer)
        this.addModel()
        this.addResizeEventListener()
        this.addClickEvent()
    }

    addClickEvent() {
    
    
        this.renderer.domElement.addEventListener('click', (ev) => {
    
    
            this.mouse.x = (ev.clientX / window.innerWidth) * 2 - 1
            this.mouse.y = -(ev.clientY / window.innerHeight) * 2 + 1
            this.raycaster.setFromCamera(this.mouse, this.camera)
            const intersects = this.raycaster.intersectObject(this.scene, true)
            console.log(intersects)
        })
    }

    addOrbitControls(camera: THREE.Camera, renderer: THREE.WebGLRenderer) {
    
    
        const controls = new OrbitControls(camera, renderer.domElement)
        controls.autoRotate = true
        controls.enableDamping = true
        controls.update()
        return controls
    }

    async addModel() {
    
    
        const model = await loadGLTF('gltf/SheenChair.glb')
        this.scene.add(model)
    }

    addResizeEventListener() {
    
    
        window.addEventListener('resize', () => {
    
    
            this.camera.aspect = window.innerWidth / window.innerHeight
            this.camera.updateProjectionMatrix()
            this.renderer.setSize(window.innerWidth, window.innerHeight)
        })
    }

    startMainLoop() {
    
    
        // 等待一帧用于初始化
        Promise.resolve().then(() => {
    
    
            this.step()
        })
    }

    step() {
    
    
        requestAnimationFrame(this.step.bind(this))
        this.orbitControls && this.orbitControls.update()
        this.renderer.render(this.scene, this.camera)
    }
}

const game = new Game()
game.startMainLoop()

animation track

Click a model with the mouse to animate the model.

Create a new animation.ts responsible for handling animation

// animation.ts
import * as THREE from 'three'

export class AnimationManager {
    
    
    mixers: THREE.AnimationMixer[] = []
    clock = new THREE.Clock()

    addOnePosAnima(obj: THREE.Object3D) {
    
    
        const positionTimes = [0, 1, 2]
        const positionArr = [
            0, 0, 0,
            0, 1, 0,
            0, 0, 0
        ]
        const track = new THREE.VectorKeyframeTrack(
            `${
      
      obj.name}.position`,
            positionTimes,
            positionArr,
            THREE.InterpolateSmooth
        )
        // 动画名称,持续时间,track
        const clip = new THREE.AnimationClip('clip1', 2, [track])
        const mixer = new THREE.AnimationMixer(obj)
        const action = mixer.clipAction(clip)
        action.play()
        this.mixers.push(mixer)
    }

    step() {
    
    
        const dt = this.clock.getDelta()
        this.mixers.forEach(m => m.update(dt))
    }
}

In index.ts, an animation is added to the model wherever it is clicked.

import * as THREE from 'three'
import {
    
     OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
import {
    
     initCamera, initScene, initWebGLRenderer } from './init'
import {
    
     loadGLTF } from './load'
import {
    
     AnimationManager } from './animation'

class Game {
    
    
    scene: THREE.Scene
    camera: THREE.PerspectiveCamera
    renderer: THREE.WebGLRenderer
    orbitControls: OrbitControls

    raycaster = new THREE.Raycaster()
    mouse = new THREE.Vector2()

    animationManager = new AnimationManager()

    constructor() {
    
    
        this.scene = initScene()
        this.camera = initCamera()
        this.scene.add(this.camera)
        this.renderer = initWebGLRenderer()
        this.orbitControls = this.addOrbitControls(this.camera, this.renderer)
        this.addModel()
        this.addResizeEventListener()
        this.addClickEvent()
    }

    addClickEvent() {
    
    
        this.renderer.domElement.addEventListener('click', (ev) => {
    
    
            this.mouse.x = (ev.clientX / window.innerWidth) * 2 - 1
            this.mouse.y = -(ev.clientY / window.innerHeight) * 2 + 1
            this.raycaster.setFromCamera(this.mouse, this.camera)
            const intersects = this.raycaster.intersectObject(this.scene, true)
            console.log(intersects)
            if (intersects[0]) {
    
    
                this.animationManager.addOnePosAnima(intersects[0].object)
            }
        })
    }

    addOrbitControls(camera: THREE.Camera, renderer: THREE.WebGLRenderer) {
    
    
        const controls = new OrbitControls(camera, renderer.domElement)
        controls.autoRotate = true
        controls.enableDamping = true
        controls.update()
        return controls
    }

    async addModel() {
    
    
        const model = await loadGLTF('gltf/SheenChair.glb')
        this.scene.add(model)
    }

    addResizeEventListener() {
    
    
        window.addEventListener('resize', () => {
    
    
            this.camera.aspect = window.innerWidth / window.innerHeight
            this.camera.updateProjectionMatrix()
            this.renderer.setSize(window.innerWidth, window.innerHeight)
        })
    }

    startMainLoop() {
    
    
        // 等待一帧用于初始化
        Promise.resolve().then(() => {
    
    
            this.step()
        })
    }

    step() {
    
    
        requestAnimationFrame(this.step.bind(this))
        this.orbitControls && this.orbitControls.update()
        this.animationManager.step()
        this.renderer.render(this.scene, this.camera)
    }
}

const game = new Game()
game.startMainLoop()

It should be noted that the model is composed of multiple sub-Mesh. If you want to move the whole, query the parent or save it in a variable in advance or record the name to search.

More articles and shares

Three study project link: https://github.com/KuoKuo666/threejs-study

Personal website: www.kuokuo666.com

2023!Day Day Up!

Guess you like

Origin blog.csdn.net/kuokuo666/article/details/130313907