[Three.js 代码组织] 资源加载器

2023-08-02  本文已影响0人  alue

如果每个文件都写一遍加载流程, 代码会随着文件数量线性增加. 这里用一个工具类来完成这个工作.

首先将资源列表, 整理成一个规范的三元组列表文件,分别由资源名称、类型、路径构成:

export default [  
    {        
        name: 'environmentMapTexture',  
        type: 'cubeTexture',  
        path:  
            [  
                'textures/environmentMap/px.jpg',  
                'textures/environmentMap/nx.jpg',  
                'textures/environmentMap/py.jpg',  
                'textures/environmentMap/ny.jpg',  
                'textures/environmentMap/pz.jpg',  
                'textures/environmentMap/nz.jpg'  
            ]    },  
    {  
        name: 'grassColorTexture',  
        type: 'texture',  
        path: 'textures/dirt/color.jpg'  
    },  
    {  
        name: 'grassNormalTexture',  
        type: 'texture',  
        path: 'textures/dirt/normal.jpg'  
    },  
    {  
        name: 'foxModel',  
        type: 'gltfModel',  
        path: 'models/Fox/glTF/Fox.gltf'  
    }  
]

然后编写资源加载类如下:

import * as THREE from 'three'  
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'  
import { OBJLoader } from 'three/addons/loaders/OBJLoader.js';  
import EventEmitter from './EventEmitter.js'  
  
export default class Resources extends EventEmitter  
{  
    constructor(sources)  
    {  
        super()  
  
        this.sources = sources  
  
        this.items = {}  
        this.toLoad = this.sources.length  
        this.loaded = 0  
  
        this.setLoaders()  
        this.startLoading()  
    }  
  
    setLoaders()  
    {  
        this.loaders = {}  
        this.loaders.gltfLoader = new GLTFLoader()  
        this.loaders.textureLoader = new THREE.TextureLoader()  
        this.loaders.cubeTextureLoader = new THREE.CubeTextureLoader()  
        this.loaders.objLoader = new OBJLoader()  
    }  
  
    startLoading()  
    {  
        // Load each source  
        for(const source of this.sources)  
        {  
            if(source.type === 'gltfModel')  
            {  
                this.loaders.gltfLoader.load(  
                    source.path,  
                    (file) =>  
                    {  
                        this.sourceLoaded(source, file)  
                    }  
                )  
            }  
            else if(source.type === 'texture')  
            {  
                this.loaders.textureLoader.load(  
                    source.path,  
                    (file) =>  
                    {  
                        this.sourceLoaded(source, file)  
                    }                )  
            }            else if(source.type === 'cubeTexture')  
            {                this.loaders.cubeTextureLoader.load(  
                    source.path,  
                    (file) =>  
                    {  
                        this.sourceLoaded(source, file)  
                    }  
                )            }  
            else if(source.type==='obj')  
            {  
                this.loaders.objLoader.load(  
                    source.path,  
                    (file) =>  
                    {  
                        this.sourceLoaded(source, file)  
                    }  
                )  
            }  
        }  
    }  
  
    sourceLoaded(source, file)  
    {  
        this.items[source.name] = file  
  
        this.loaded++  
  
        if(this.loaded === this.toLoad)  
        {  
            this.trigger('ready')  
        }  
    }  
}

将需要用到的所有loader都集中在 setLoaders 方法内。通过 startLoading 来加载资源,资源加载完毕后,会向外抛出 ready 事件。

调用资源对象时,可以监听 ready 事件,来完成资源对象类对象的新建。

import Floor from './Floor.js'  
import Fox from './Fox.js'
// Wait for resources  
this.resources.on('ready', () =>  
{  
    // Setup  
    this.floor = new Floor()  
    this.fox = new Fox()  
})

资源对象类,可以直接调用 resources 中的材质对象

// Floor.js
import * as THREE from 'three'  
import Experience from '../Experience.js'  
  
export default class Floor  
{  
    constructor()  
    {  
        this.experience = new Experience()  
        this.scene = this.experience.scene  
        this.resources = this.experience.resources  
  
        this.setGeometry()  
        this.setTextures()  
        this.setMaterial()  
        this.setMesh()  
    }  
  
    setGeometry()  
    {  
        this.geometry = new THREE.CircleGeometry(5, 64)  
    }  
  
    setTextures()  
    {  
        this.textures = {}  
  
        this.textures.color = this.resources.items.grassColorTexture  
        this.textures.color.encoding = THREE.sRGBEncoding  
        this.textures.color.repeat.set(1.5, 1.5)  
        this.textures.color.wrapS = THREE.RepeatWrapping  
        this.textures.color.wrapT = THREE.RepeatWrapping  
  
        this.textures.normal = this.resources.items.grassNormalTexture  
        this.textures.normal.repeat.set(1.5, 1.5)  
        this.textures.normal.wrapS = THREE.RepeatWrapping  
        this.textures.normal.wrapT = THREE.RepeatWrapping  
    }  
  
    setMaterial()  
    {  
        this.material = new THREE.MeshStandardMaterial({  
            map: this.textures.color,  
            normalMap: this.textures.normal  
        })  
    }  
  
    setMesh()  
    {  
        this.mesh = new THREE.Mesh(this.geometry, this.material)  
        this.mesh.rotation.x = - Math.PI * 0.5  
        this.mesh.receiveShadow = true  
        this.scene.add(this.mesh)  
    }  
}

直接调用模型资源

// Fox.js

import * as THREE from 'three'  
import Experience from '../Experience.js'  
  
export default class Fox  
{  
    constructor()  
    {  
        this.experience = new Experience()  
        this.scene = this.experience.scene  
        this.resources = this.experience.resources  
        this.time = this.experience.time  
 
        // Resource  
        this.resource = this.resources.items.foxModel  
  
        this.setModel()  
        this.setAnimation()  
    }  
  
    setModel()  
    {  
        this.model = this.resource.scene  
        this.model.scale.set(0.02, 0.02, 0.02)  
        this.scene.add(this.model)  
  
        this.model.traverse((child) =>  
        {            if(child instanceof THREE.Mesh)  
            {  
                child.castShadow = true  
            }  
        })  
    }  
  
    setAnimation()  
    {  
        this.animation = {}  
        // Mixer  
        this.animation.mixer = new THREE.AnimationMixer(this.model)  
        // Actions  
        this.animation.actions = {}  
        this.animation.actions.idle = this.animation.mixer.clipAction(this.resource.animations[0])  
        this.animation.actions.walking = this.animation.mixer.clipAction(this.resource.animations[1])  
        this.animation.actions.running = this.animation.mixer.clipAction(this.resource.animations[2])  
        this.animation.actions.current = this.animation.actions.idle  
        this.animation.actions.current.play()  
  
        // Play the action  
        this.animation.play = (name) =>  
        {            const newAction = this.animation.actions[name]  
            const oldAction = this.animation.actions.current  
  
            newAction.reset()  
            newAction.play()  
            newAction.crossFadeFrom(oldAction, 1)  
  
            this.animation.actions.current = newAction  
        }  
    }  
  
    update()  
    {  
        this.animation.mixer.update(this.time.delta * 0.001)  
    }  
}

上一篇下一篇

猜你喜欢

热点阅读